Просмотр исходного кода

pokemon: support gen ii trades

All gen ii pokemon, items, moves, and stats were added. In addition to
the original gen i tunables, the user can specify shininess, gender,
held item, pokerus status, and Unown forms.

Code changes:
Separate out the mass of functions and const structs to standalone files
for interacting with those.

Make the trade and party struct completely opaque and provide accessor
functions to get and set arbitrary members of the struct. *

Create a main menu of sorts to select the desired generation to emulate.
This also moves the EXT pinout selection here so this needs to be set up
before a trade is initiated.

Remove repeated scenes and enforce better reuse, e.g. Level & OT ID use
the same scene code with different scene names; same with Nickname, OT
Name, and Unown Form.

Fix issue with MALVEKE board detection.

Implement exit confirmation from each generations setup. It was really
easy to accidentally back out to the main menu after a trade (e.g. to
edit a pokmeon) and then lose all of the data.

Better synchronize states in trade, if the Flipper wants to go back out
of the trade, act more like a real Gameboy and block until the trade
partner confirms that both sides agree to backing out of the trade menu.

Add gen ii pokemon to data table. This now mixes data between gen i and
ii, but in ways that don't actually conflict. Gen i uses an arbitrary
index to ID the pokemon, gen ii uses the national pokedex number. Gen i
trades types, gen ii does not. Gen ii adds spc_atk and spc_def, gen i
only uses spc; so now every gen i pokemon has spc, spc_atk, and spc_def
and gen ii pokemon only use spc_atk and spc_def. Gender ratio used only
in gen ii. Growth rate is the same for all 251 pokemon between gen i and
ii. Default moves are the only desync'ed entry. The first 151 pokemon in
the table use the default moves from gen i games, the next 100 use
default moves from gen ii.

Add gen ii moves, these are bitmasked to show what moves are valid in
which generations.

Add gen ii items, only used in gen ii.

(*) One of the attempted optimizations was to not use the messy accessor
functions; maintaining a single struct of all possible data needed. When
trade time comes, copy all of that in to the right format for the
selected generation. The problem is that moving to and from the single
common struct to individual trade structs and back added way more code
than what is done now.

Visuals:
Move pokemon icons out of assets/ and set up loading sprites from SD
card. These are held in sprites/, translated to FXBM format (i.e. the
sprite format used by the flipper game engine), and catted together to a
single file.

Rearrange trade and select views to ensure a better view of pokemon
sprites. Sprites will be eventually moved to 56x56 (right now all are
50x50) and this is taken in to account. This also includes proper
layering of drawn assets/sprites/etc. with transparencies to make the
whole interface look more natural.

Add graphics testing macro at compile time

Unused assets, repeated assets, and assets that can be redrawn with
primitives in firmware are removed.

Signed-off-by: Kris Bahnsen <Kris@KBEmbedded.com>
Kris Bahnsen 2 лет назад
Родитель
Сommit
e0581ec8aa
100 измененных файлов с 6564 добавлено и 2828 удалено
  1. 1 0
      application.fam
  2. BIN
      assets/Background.png
  3. BIN
      assets/Button_18x18.png
  4. BIN
      assets/Connect_me_62x31.png
  5. BIN
      assets/Connected_62x31.png
  6. BIN
      assets/Space_100x18.png
  7. BIN
      assets/Space_95x18.png
  8. BIN
      assets/dolphin.png
  9. BIN
      assets/fossilaerodactyl.png
  10. BIN
      assets/fossilkabutops.png
  11. BIN
      assets/hand_cable.png
  12. BIN
      assets/hand_thumbsup.png
  13. BIN
      assets/red.png
  14. BIN
      files/all_sprites.fxbm
  15. 231 0
      item_nl.c
  16. 8 0
      item_nl.h
  17. 25 0
      missingno_i.h
  18. 258 0
      move_nl.c
  19. 8 0
      move_nl.h
  20. 61 0
      named_list.c
  21. 46 0
      named_list.h
  22. 2 2129
      pokemon_app.c
  23. 6 70
      pokemon_app.h
  24. 912 0
      pokemon_data.c
  25. 79 62
      pokemon_data.h
  26. 143 0
      pokemon_data_i.h
  27. 3361 0
      pokemon_table.c
  28. 33 0
      pokemon_table.h
  29. 88 0
      scenes/pokemon_exit_confirm.c
  30. 15 0
      scenes/pokemon_exit_confirm.h
  31. 232 0
      scenes/pokemon_gen.c
  32. 8 0
      scenes/pokemon_gen.h
  33. 104 0
      scenes/pokemon_gender.c
  34. 22 0
      scenes/pokemon_gender.h
  35. 94 0
      scenes/pokemon_item.c
  36. 10 0
      scenes/pokemon_item.h
  37. 0 60
      scenes/pokemon_level.c
  38. 0 9
      scenes/pokemon_level.h
  39. 94 98
      scenes/pokemon_menu.c
  40. 11 0
      scenes/pokemon_menu.h
  41. 51 44
      scenes/pokemon_move.c
  42. 124 0
      scenes/pokemon_name_input.c
  43. 8 0
      scenes/pokemon_name_input.h
  44. 0 85
      scenes/pokemon_nickname.c
  45. 0 9
      scenes/pokemon_nickname.h
  46. 102 0
      scenes/pokemon_number_input.c
  47. 8 0
      scenes/pokemon_number_input.h
  48. 0 74
      scenes/pokemon_ot_id.c
  49. 0 9
      scenes/pokemon_ot_id.h
  50. 0 70
      scenes/pokemon_ot_name.c
  51. 0 9
      scenes/pokemon_ot_name.h
  52. 1 7
      scenes/pokemon_pins.c
  53. 0 1
      scenes/pokemon_pins.h
  54. 172 0
      scenes/pokemon_pokerus.c
  55. 11 0
      scenes/pokemon_pokerus.h
  56. 0 13
      scenes/pokemon_select.c
  57. 0 1
      scenes/pokemon_select.h
  58. 81 0
      scenes/pokemon_shiny.c
  59. 9 0
      scenes/pokemon_shiny.h
  60. 14 26
      scenes/pokemon_stats.c
  61. 0 1
      scenes/pokemon_stats.h
  62. 45 50
      scenes/pokemon_type.c
  63. 0 1
      scenes/pokemon_type.h
  64. 73 0
      scenes/unown_form.c
  65. 13 0
      scenes/unown_form.h
  66. BIN
      sprites/000.png
  67. 0 0
      sprites/001.png
  68. 0 0
      sprites/002.png
  69. 0 0
      sprites/003.png
  70. 0 0
      sprites/004.png
  71. 0 0
      sprites/005.png
  72. 0 0
      sprites/006.png
  73. 0 0
      sprites/007.png
  74. 0 0
      sprites/008.png
  75. 0 0
      sprites/009.png
  76. 0 0
      sprites/010.png
  77. 0 0
      sprites/011.png
  78. 0 0
      sprites/012.png
  79. 0 0
      sprites/013.png
  80. 0 0
      sprites/014.png
  81. 0 0
      sprites/015.png
  82. 0 0
      sprites/016.png
  83. 0 0
      sprites/017.png
  84. 0 0
      sprites/018.png
  85. 0 0
      sprites/019.png
  86. 0 0
      sprites/020.png
  87. 0 0
      sprites/021.png
  88. 0 0
      sprites/022.png
  89. 0 0
      sprites/023.png
  90. 0 0
      sprites/024.png
  91. 0 0
      sprites/025.png
  92. 0 0
      sprites/026.png
  93. 0 0
      sprites/027.png
  94. 0 0
      sprites/028.png
  95. 0 0
      sprites/029.png
  96. 0 0
      sprites/030.png
  97. 0 0
      sprites/031.png
  98. 0 0
      sprites/032.png
  99. 0 0
      sprites/033.png
  100. 0 0
      sprites/034.png

+ 1 - 0
application.fam

@@ -9,6 +9,7 @@ App(
     fap_category="GPIO",
     fap_category="GPIO",
     fap_icon="pokemon_10px.png",
     fap_icon="pokemon_10px.png",
     fap_icon_assets="assets",
     fap_icon_assets="assets",
+    fap_file_assets="files",
     fap_author="Esteban Fuentealba, Kris Bahnsen, Darryn Cull",
     fap_author="Esteban Fuentealba, Kris Bahnsen, Darryn Cull",
     fap_weburl="https://github.com/EstebanFuentealba",
     fap_weburl="https://github.com/EstebanFuentealba",
     fap_description="Pokemon exchange from Flipper Zero to Game Boy for Generation I (Pokemon Red, Blue, Yellow)",
     fap_description="Pokemon exchange from Flipper Zero to Game Boy for Generation I (Pokemon Red, Blue, Yellow)",

BIN
assets/Background.png


BIN
assets/Button_18x18.png


BIN
assets/Connect_me_62x31.png


BIN
assets/Connected_62x31.png


BIN
assets/Space_100x18.png


BIN
assets/Space_95x18.png


BIN
assets/dolphin.png


BIN
assets/fossilaerodactyl.png


BIN
assets/fossilkabutops.png


BIN
assets/hand_cable.png


BIN
assets/hand_thumbsup.png


BIN
assets/red.png


BIN
files/all_sprites.fxbm


+ 231 - 0
item_nl.c

@@ -0,0 +1,231 @@
+#include <named_list.h>
+#include <pokemon_data.h>
+
+const NamedList item_list[] = {
+    {"No Item", 0x00, GEN_II},
+    {"Amulet Coin", 0x5B, GEN_II},
+    {"Antidote", 0x09, GEN_II},
+    {"Awakening", 0x0C, GEN_II},
+    {"Basement Key", 0x85, GEN_II},
+    {"Berry", 0xAD, GEN_II},
+    {"Berry Juice", 0x8B, GEN_II},
+    {"Berserk Gene", 0x98, GEN_II},
+    {"Bicycle", 0x07, GEN_II},
+    {"Big Mushroom", 0x57, GEN_II},
+    {"Big Pearl", 0x6F, GEN_II},
+    {"Bitter Berry", 0x53, GEN_II},
+    {"Blackbelt", 0x62, GEN_II},
+    {"Black Glasses", 0x66, GEN_II},
+    {"Blk Apricorn", 0x63, GEN_II},
+    {"Blu Apricorn", 0x59, GEN_II},
+    {"Blue Card (C Only)", 0x74, GEN_II},
+    {"Bluesky Mail", 0xBB, GEN_II},
+    {"Brick Piece", 0xB4, GEN_II},
+    {"Bright Powder", 0x03, GEN_II},
+    {"Burn Heal", 0x0A, GEN_II},
+    {"Burnt Berry", 0x4F, GEN_II},
+    {"Calcium", 0x1F, GEN_II},
+    {"Carbos", 0x1D, GEN_II},
+    {"Card Key", 0x7F, GEN_II},
+    {"Charcoal", 0x8A, GEN_II},
+    {"Cleanse Tag", 0x5E, GEN_II},
+    {"Clear Bell (C Only)", 0x46, GEN_II},
+    {"Coin Case", 0x36, GEN_II},
+    {"Dire Hit", 0x2C, GEN_II},
+    {"Dragon Fang", 0x90, GEN_II},
+    {"Dragon Scale", 0x97, GEN_II},
+    {"Egg Ticket (C Only)", 0x81, GEN_II},
+    {"Elixir", 0x41, GEN_II},
+    {"Energy Powder", 0x79, GEN_II},
+    {"Energy Root", 0x7A, GEN_II},
+    {"Eon Mail", 0xB9, GEN_II},
+    {"Escape Rope", 0x13, GEN_II},
+    {"Ether", 0x3F, GEN_II},
+    {"Everstone", 0x70, GEN_II},
+    {"Exp. Share", 0x39, GEN_II},
+    {"Fast Ball", 0xA1, GEN_II},
+    {"Fire Stone", 0x16, GEN_II},
+    {"Flower Mail", 0x9E, GEN_II},
+    {"Focus Band", 0x77, GEN_II},
+    {"Fresh Water", 0x2E, GEN_II},
+    {"Friend Ball", 0xA4, GEN_II},
+    {"Full Heal", 0x26, GEN_II},
+    {"Full Restore", 0x0E, GEN_II},
+    {"Gold Berry", 0xAE, GEN_II},
+    {"Gold Leaf", 0x4B, GEN_II},
+    {"Good Rod", 0x3B, GEN_II},
+    {"Gorgeous Box", 0xA8, GEN_II},
+    {"Great Ball", 0x04, GEN_II},
+    {"Grn Apricorn", 0x5D, GEN_II},
+    {"GS Ball (C Only)", 0x73, GEN_II},
+    {"Guard Spec.", 0x29, GEN_II},
+    {"Hard Stone", 0x7D, GEN_II},
+    {"Heal Powder", 0x7B, GEN_II},
+    {"Heavy Ball", 0x9D, GEN_II},
+    {"HM01", 0xF3, GEN_II},
+    {"HM02", 0xF4, GEN_II},
+    {"HM03", 0xF5, GEN_II},
+    {"HM04", 0xF6, GEN_II},
+    {"HM05", 0xF7, GEN_II},
+    {"HM06", 0xF8, GEN_II},
+    {"HM07", 0xF9, GEN_II},
+    {"HP Up", 0x1A, GEN_II},
+    {"Hyper Potion", 0x10, GEN_II},
+    {"Ice Berry", 0x50, GEN_II},
+    {"Ice Heal", 0x0B, GEN_II},
+    {"Iron", 0x1C, GEN_II},
+    {"Itemfinder", 0x37, GEN_II},
+    {"King's Rock", 0x52, GEN_II},
+    {"Leaf Stone", 0x22, GEN_II},
+    {"Leftovers", 0x92, GEN_II},
+    {"Lemonade", 0x30, GEN_II},
+    {"Level Ball", 0x9F, GEN_II},
+    {"Light Ball", 0xA3, GEN_II},
+    {"Litebluemail", 0xB6, GEN_II},
+    {"Lost Item", 0x82, GEN_II},
+    {"Love Ball", 0xA6, GEN_II},
+    {"Lovely Mail", 0xB8, GEN_II},
+    {"Lucky Egg", 0x7E, GEN_II},
+    {"Lucky Punch", 0x1E, GEN_II},
+    {"Lure Ball", 0xA0, GEN_II},
+    {"Machine Part", 0x80, GEN_II},
+    {"Magnet", 0x6C, GEN_II},
+    {"Master Ball", 0x01, GEN_II},
+    {"Max Elixir", 0x15, GEN_II},
+    {"Max Ether", 0x40, GEN_II},
+    {"Max Potion", 0x0F, GEN_II},
+    {"Max Repel", 0x2B, GEN_II},
+    {"Max Revive", 0x28, GEN_II},
+    {"Metal Coat", 0x8F, GEN_II},
+    {"Metal Powder", 0x23, GEN_II},
+    {"Mint Berry", 0x54, GEN_II},
+    {"MiracleBerry", 0x6D, GEN_II},
+    {"Miracle Seed", 0x75, GEN_II},
+    {"Mirage Mail", 0xBD, GEN_II},
+    {"Moomoo Milk", 0x48, GEN_II},
+    {"Moon Ball", 0xA5, GEN_II},
+    {"Moon Stone", 0x08, GEN_II},
+    {"Morph Mail", 0xBA, GEN_II},
+    {"Music Mail", 0xBC, GEN_II},
+    {"Mystery Berry", 0x96, GEN_II},
+    {"Mystery Egg", 0x45, GEN_II},
+    {"Mystic Water", 0x5F, GEN_II},
+    {"Never-Melt Ice", 0x6B, GEN_II},
+    {"Normal Box", 0xA7, GEN_II},
+    {"Nugget", 0x24, GEN_II},
+    {"Old Rod", 0x3A, GEN_II},
+    {"Paralyze Heal", 0x0D, GEN_II},
+    {"Park Ball", 0xB1, GEN_II},
+    {"Pass", 0x86, GEN_II},
+    {"Pear	l", 0x6E, GEN_II},
+    {"Pink Bow", 0x68, GEN_II},
+    {"Pnk Apricorn", 0x65, GEN_II},
+    {"Poison Barb", 0x51, GEN_II},
+    {"Poke Ball", 0x05, GEN_II},
+    {"Poke Doll", 0x25, GEN_II},
+    {"Polkadot Bow", 0xAA, GEN_II},
+    {"Portraitmail", 0xB7, GEN_II},
+    {"Potion", 0x12, GEN_II},
+    {"PP Up", 0x3E, GEN_II},
+    {"Protein", 0x1B, GEN_II},
+    {"PRZCureBerry", 0x4E, GEN_II},
+    {"PSNCureBerry", 0x4A, GEN_II},
+    {"Quick Claw", 0x49, GEN_II},
+    {"Rage Candy Bar", 0x72, GEN_II},
+    {"Rainbow Wing", 0xB2, GEN_II},
+    {"Rare Candy", 0x20, GEN_II},
+    {"Red Apricorn", 0x55, GEN_II},
+    {"Red Scale", 0x42, GEN_II},
+    {"Repel", 0x14, GEN_II},
+    {"Revival Herb", 0x7C, GEN_II},
+    {"Revive", 0x27, GEN_II},
+    {"Sacred Ash", 0x9C, GEN_II},
+    {"Scope Lens", 0x8C, GEN_II},
+    {"Secret Potion", 0x43, GEN_II},
+    {"Sharp Beak", 0x4D, GEN_II},
+    {"Silver Leaf", 0x3C, GEN_II},
+    {"Silver Powder", 0x58, GEN_II},
+    {"Silver Wing", 0x47, GEN_II},
+    {"Slowpoke Tail", 0x67, GEN_II},
+    {"Smoke Ball", 0x6A, GEN_II},
+    {"Soda Pop", 0x2F, GEN_II},
+    {"Soft Sand", 0x4C, GEN_II},
+    {"Spell Tag", 0x71, GEN_II},
+    {"Squirt Bottle", 0xAF, GEN_II},
+    {"S.S. Ticket", 0x44, GEN_II},
+    {"Stardust", 0x83, GEN_II},
+    {"Star Piece", 0x84, GEN_II},
+    {"Stick", 0x69, GEN_II},
+    {"Sun Stone", 0xA9, GEN_II},
+    {"Super Potion", 0x11, GEN_II},
+    {"Super Repel", 0x2A, GEN_II},
+    {"Super Rod", 0x3D, GEN_II},
+    {"Surf Mail", 0xB5, GEN_II},
+    {"Teru-sama", 0x5A, GEN_II},
+    {"Teru-sama", 0x78, GEN_II},
+    {"Thick Club", 0x76, GEN_II},
+    {"Thunder Stone", 0x17, GEN_II},
+    {"Tiny Mushroom", 0x56, GEN_II},
+    {"TM01", 0xBF, GEN_II},
+    {"TM02", 0xC0, GEN_II},
+    {"TM03", 0xC1, GEN_II},
+    {"TM04", 0xC2, GEN_II},
+    {"TM05", 0xC4, GEN_II},
+    {"TM06", 0xC5, GEN_II},
+    {"TM07", 0xC6, GEN_II},
+    {"TM08", 0xC7, GEN_II},
+    {"TM09", 0xC8, GEN_II},
+    {"TM10", 0xC9, GEN_II},
+    {"TM11", 0xCA, GEN_II},
+    {"TM12", 0xCB, GEN_II},
+    {"TM13", 0xCC, GEN_II},
+    {"TM14", 0xCD, GEN_II},
+    {"TM15", 0xCE, GEN_II},
+    {"TM16", 0xCF, GEN_II},
+    {"TM17", 0xD0, GEN_II},
+    {"TM18", 0xD1, GEN_II},
+    {"TM19", 0xD2, GEN_II},
+    {"TM20", 0xD3, GEN_II},
+    {"TM21", 0xD4, GEN_II},
+    {"TM22", 0xD5, GEN_II},
+    {"TM23", 0xD6, GEN_II},
+    {"TM24", 0xD7, GEN_II},
+    {"TM25", 0xD8, GEN_II},
+    {"TM26", 0xD9, GEN_II},
+    {"TM27", 0xDA, GEN_II},
+    {"TM28", 0xDB, GEN_II},
+    {"TM29", 0xDD, GEN_II},
+    {"TM30", 0xDE, GEN_II},
+    {"TM31", 0xDF, GEN_II},
+    {"TM32", 0xE0, GEN_II},
+    {"TM33", 0xE1, GEN_II},
+    {"TM34", 0xE2, GEN_II},
+    {"TM35", 0xE3, GEN_II},
+    {"TM36", 0xE4, GEN_II},
+    {"TM37", 0xE5, GEN_II},
+    {"TM38", 0xE6, GEN_II},
+    {"TM39", 0xE7, GEN_II},
+    {"TM40", 0xE8, GEN_II},
+    {"TM41", 0xE9, GEN_II},
+    {"TM42", 0xEA, GEN_II},
+    {"TM43", 0xEB, GEN_II},
+    {"TM44", 0xEC, GEN_II},
+    {"TM45", 0xED, GEN_II},
+    {"TM46", 0xEE, GEN_II},
+    {"TM47", 0xEF, GEN_II},
+    {"TM48", 0xF0, GEN_II},
+    {"TM49", 0xF1, GEN_II},
+    {"TM50", 0xF2, GEN_II},
+    {"Twisted Spoon", 0x60, GEN_II},
+    {"Ultra Ball", 0x02, GEN_II},
+    {"Up-Grade", 0xAC, GEN_II},
+    {"Water Stone", 0x18, GEN_II},
+    {"Wht Apricorn", 0x61, GEN_II},
+    {"X Accuracy", 0x21, GEN_II},
+    {"X Attack", 0x31, GEN_II},
+    {"X Defend", 0x33, GEN_II},
+    {"X Special", 0x35, GEN_II},
+    {"X Speed", 0x34, GEN_II},
+    {"Ylw Apricorn", 0x5C, GEN_II},
+    {},
+};

+ 8 - 0
item_nl.h

@@ -0,0 +1,8 @@
+#ifndef __ITEM_NL_H__
+#define __ITEM_NL_H__
+
+#pragma once
+
+extern const NamedList item_list[];
+
+#endif // __ITEM_NL_H__

+ 25 - 0
missingno_i.h

@@ -0,0 +1,25 @@
+unsigned char __000_fxbm[] = {
+    0x66, 0x01, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0xc0, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe,
+    0xff, 0x01, 0x00, 0x00, 0x00, 0x80, 0xff, 0xff, 0x07, 0x00, 0x00, 0x00, 0xc0, 0x3f, 0xf0, 0x0f,
+    0x00, 0x00, 0x00, 0xf0, 0x07, 0x80, 0x3f, 0x00, 0x00, 0x00, 0xf8, 0x01, 0x00, 0x7e, 0x00, 0x00,
+    0x00, 0xfc, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, 0x3e,
+    0xe0, 0x1f, 0xf0, 0x01, 0x00, 0x00, 0x1f, 0xf8, 0x7f, 0xe0, 0x03, 0x00, 0x00, 0x0f, 0x7c, 0xf8,
+    0xc0, 0x03, 0x00, 0x80, 0x07, 0x1e, 0xe0, 0x81, 0x07, 0x00, 0x80, 0x07, 0x0f, 0xc0, 0x83, 0x07,
+    0x00, 0xc0, 0x83, 0x07, 0x83, 0x07, 0x0f, 0x00, 0xc0, 0x83, 0x87, 0x87, 0x07, 0x0f, 0x00, 0xc0,
+    0xc3, 0x87, 0x87, 0x0f, 0x0f, 0x00, 0xe0, 0xc1, 0x87, 0x87, 0x0f, 0x1e, 0x00, 0xe0, 0xe1, 0x87,
+    0x83, 0x1f, 0x1e, 0x00, 0xe0, 0xe1, 0xcf, 0xc1, 0x1f, 0x1e, 0x00, 0xe0, 0xe1, 0xff, 0xe0, 0x1f,
+    0x1e, 0x00, 0xe0, 0xe1, 0x7f, 0xf0, 0x1f, 0x1e, 0x00, 0xe0, 0xc1, 0x7f, 0xf8, 0x0f, 0x1e, 0x00,
+    0xc0, 0xc3, 0x7f, 0xf8, 0x0f, 0x0f, 0x00, 0xc0, 0x83, 0xff, 0xff, 0x07, 0x0f, 0x00, 0xc0, 0x83,
+    0x7f, 0xf8, 0x07, 0x0f, 0x00, 0x80, 0x07, 0x3f, 0xf0, 0x83, 0x07, 0x00, 0x80, 0x07, 0x3e, 0xf0,
+    0x81, 0x07, 0x00, 0x00, 0x0f, 0x7c, 0xf8, 0xc0, 0x03, 0x00, 0x00, 0x1f, 0xf8, 0x7f, 0xe0, 0x03,
+    0x00, 0x00, 0x3e, 0xe0, 0x1f, 0xf0, 0x01, 0x00, 0x00, 0x7c, 0x80, 0x07, 0xf8, 0x00, 0x00, 0x00,
+    0xfc, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0xf8, 0x01, 0x00, 0x7e, 0x00, 0x00, 0x00, 0xf0, 0x07,
+    0x80, 0x3f, 0x00, 0x00, 0x00, 0xc0, 0x3f, 0xf0, 0x0f, 0x00, 0x00, 0x00, 0x80, 0xff, 0xff, 0x07,
+    0x00, 0x00, 0x00, 0x00, 0xfe, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x7f, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0xc0, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+unsigned int __000_fxbm_len = 362;

+ 258 - 0
move_nl.c

@@ -0,0 +1,258 @@
+#include <named_list.h>
+#include <pokemon_data.h>
+
+const NamedList move_list[] = {
+    {"No Move", 0x00, (GEN_I | GEN_II)},
+    {"Absorb", 0x47, (GEN_I | GEN_II)},
+    {"Acid", 0x33, (GEN_I | GEN_II)},
+    {"Acid Armor", 0x97, (GEN_I | GEN_II)},
+    {"Aeroblast", 0xB1, GEN_II},
+    {"Agility", 0x61, (GEN_I | GEN_II)},
+    {"Amnesia", 0x85, (GEN_I | GEN_II)},
+    {"Ancient Power", 0xF6, GEN_II},
+    {"Attract", 0xD5, GEN_II},
+    {"Aurora Beam", 0x3E, (GEN_I | GEN_II)},
+    {"Barrage", 0x8C, (GEN_I | GEN_II)},
+    {"Barrier", 0x70, (GEN_I | GEN_II)},
+    {"Baton Pass", 0xE2, GEN_II},
+    {"Beat Up", 0xFB, GEN_II},
+    {"Belly Drum", 0xBB, GEN_II},
+    {"Bide", 0x75, (GEN_I | GEN_II)},
+    {"Bind", 0x14, (GEN_I | GEN_II)},
+    {"Bite", 0x2C, (GEN_I | GEN_II)},
+    {"Blizzard", 0x3B, (GEN_I | GEN_II)},
+    {"Body Slam", 0x22, (GEN_I | GEN_II)},
+    {"Bone Club", 0x7D, (GEN_I | GEN_II)},
+    {"Bone Rush", 0xC6, GEN_II},
+    {"Boomerang", 0x9B, (GEN_I | GEN_II)},
+    {"Bubble", 0x91, (GEN_I | GEN_II)},
+    {"Bubblebeam", 0x3D, (GEN_I | GEN_II)},
+    {"Charm", 0xCC, GEN_II},
+    {"Clamp", 0x80, (GEN_I | GEN_II)},
+    {"Comet Punch", 0x04, (GEN_I | GEN_II)},
+    {"Confuse Ray", 0x6D, (GEN_I | GEN_II)},
+    {"Confusion", 0x5D, (GEN_I | GEN_II)},
+    {"Constrict", 0x84, (GEN_I | GEN_II)},
+    {"Conversion", 0xA0, (GEN_I | GEN_II)},
+    {"Conversion 2", 0xB0, GEN_II},
+    {"Cotton Spore", 0xB2, GEN_II},
+    {"Counter", 0x44, (GEN_I | GEN_II)},
+    {"Crabhammer", 0x98, (GEN_I | GEN_II)},
+    {"Cross Chop", 0xEE, GEN_II},
+    {"Crunch", 0xF2, GEN_II},
+    {"Curse", 0xAE, GEN_II},
+    {"Cut", 0x0F, (GEN_I | GEN_II)},
+    {"Defense Curl", 0x6F, (GEN_I | GEN_II)},
+    {"Destiny Bond", 0xC2, GEN_II},
+    {"Detect", 0xC5, GEN_II},
+    {"Dig", 0x5B, (GEN_I | GEN_II)},
+    {"Disable", 0x32, (GEN_I | GEN_II)},
+    {"Dizzy Punch", 0x92, (GEN_I | GEN_II)},
+    {"Double-Edge", 0x26, (GEN_I | GEN_II)},
+    {"Double Kick", 0x18, (GEN_I | GEN_II)},
+    {"Doubleslap", 0x03, (GEN_I | GEN_II)},
+    {"Double Team", 0x68, (GEN_I | GEN_II)},
+    {"Dragon Breath", 0xE1, GEN_II},
+    {"Dragon Rage", 0x52, (GEN_I | GEN_II)},
+    {"Dream Eater", 0x8A, (GEN_I | GEN_II)},
+    {"Drill Peck", 0x41, (GEN_I | GEN_II)},
+    {"Dynamic Punch", 0xDF, GEN_II},
+    {"Earthquake", 0x59, (GEN_I | GEN_II)},
+    {"Egg Bomb", 0x79, (GEN_I | GEN_II)},
+    {"Ember", 0x34, (GEN_I | GEN_II)},
+    {"Encore", 0xE3, GEN_II},
+    {"Endure", 0xCB, GEN_II},
+    {"Explosion", 0x99, (GEN_I | GEN_II)},
+    {"Extreme Speed", 0xF5, GEN_II},
+    {"False Swipe", 0xCE, GEN_II},
+    {"Feint Attack", 0xB9, GEN_II},
+    {"Fire Blast", 0x7E, (GEN_I | GEN_II)},
+    {"Fire Punch", 0x07, (GEN_I | GEN_II)},
+    {"Fire Spin", 0x53, (GEN_I | GEN_II)},
+    {"Fissure", 0x5A, (GEN_I | GEN_II)},
+    {"Flail", 0xAF, GEN_II},
+    {"Flamethrower", 0x35, (GEN_I | GEN_II)},
+    {"Flame Wheel", 0xAC, GEN_II},
+    {"Flash", 0x94, (GEN_I | GEN_II)},
+    {"Fly", 0x13, (GEN_I | GEN_II)},
+    {"Focus Energy", 0x74, (GEN_I | GEN_II)},
+    {"Foresight", 0xC1, GEN_II},
+    {"Frustration", 0xDA, GEN_II},
+    {"Fury Attack", 0x1F, (GEN_I | GEN_II)},
+    {"Fury Cutter", 0xD2, GEN_II},
+    {"Fury Swipes", 0x9A, (GEN_I | GEN_II)},
+    {"Future Sight", 0xF8, GEN_II},
+    {"Giga Drain", 0xCA, GEN_II},
+    {"Glare", 0x89, (GEN_I | GEN_II)},
+    {"Growl", 0x2D, (GEN_I | GEN_II)},
+    {"Growth", 0x4A, (GEN_I | GEN_II)},
+    {"Guillotine", 0x0C, (GEN_I | GEN_II)},
+    {"Gust", 0x10, (GEN_I | GEN_II)},
+    {"Harden", 0x6A, (GEN_I | GEN_II)},
+    {"Haze", 0x72, (GEN_I | GEN_II)},
+    {"Headbutt", 0x1D, (GEN_I | GEN_II)},
+    {"Heal Bell", 0xD7, GEN_II},
+    {"Hidden Power", 0xED, GEN_II},
+    {"Hi Jump Kick", 0x88, (GEN_I | GEN_II)},
+    {"Horn Attack", 0x1E, (GEN_I | GEN_II)},
+    {"Horn Drill", 0x20, (GEN_I | GEN_II)},
+    {"Hydro Pump", 0x38, (GEN_I | GEN_II)},
+    {"Hyper Beam", 0x3F, (GEN_I | GEN_II)},
+    {"Hyper Fang", 0x9E, (GEN_I | GEN_II)},
+    {"Hypnosis", 0x5F, (GEN_I | GEN_II)},
+    {"Ice Beam", 0x3A, (GEN_I | GEN_II)},
+    {"Ice Punch", 0x08, (GEN_I | GEN_II)},
+    {"Icy Wind", 0xC4, GEN_II},
+    {"Iron Tail", 0xE7, GEN_II},
+    {"Jump Kick", 0x1A, (GEN_I | GEN_II)},
+    {"Karate Chop", 0x02, (GEN_I | GEN_II)},
+    {"Kinesis", 0x86, (GEN_I | GEN_II)},
+    {"Leech Life", 0x8D, (GEN_I | GEN_II)},
+    {"Leech Seed", 0x49, (GEN_I | GEN_II)},
+    {"Leer", 0x2B, (GEN_I | GEN_II)},
+    {"Lick", 0x7A, (GEN_I | GEN_II)},
+    {"Light Screen", 0x71, (GEN_I | GEN_II)},
+    {"Lock-On", 0xC7, GEN_II},
+    {"Lovely Kiss", 0x8E, (GEN_I | GEN_II)},
+    {"Low Kick", 0x43, (GEN_I | GEN_II)},
+    {"Mach Punch", 0xB7, GEN_II},
+    {"Magnitude", 0xDE, GEN_II},
+    {"Mean Look", 0xD4, GEN_II},
+    {"Meditate", 0x60, (GEN_I | GEN_II)},
+    {"Mega Drain", 0x48, (GEN_I | GEN_II)},
+    {"Megahorn", 0xE0, GEN_II},
+    {"Mega Kick", 0x19, (GEN_I | GEN_II)},
+    {"Mega Punch", 0x05, (GEN_I | GEN_II)},
+    {"Metal Claw", 0xE8, GEN_II},
+    {"Metronome", 0x76, (GEN_I | GEN_II)},
+    {"Milk Drink", 0xD0, GEN_II},
+    {"Mimic", 0x66, (GEN_I | GEN_II)},
+    {"Mind Reader", 0xAA, GEN_II},
+    {"Minimize", 0x6B, (GEN_I | GEN_II)},
+    {"Mirror Coat", 0xF3, GEN_II},
+    {"Mirror Move", 0x77, (GEN_I | GEN_II)},
+    {"Mist", 0x36, (GEN_I | GEN_II)},
+    {"Moonlight", 0xEC, GEN_II},
+    {"Morning Sun", 0xEA, GEN_II},
+    {"Mud-Slap", 0xBD, GEN_II},
+    {"Nightmare", 0xAB, GEN_II},
+    {"Night Shade", 0x65, (GEN_I | GEN_II)},
+    {"Octazooka", 0xBE, GEN_II},
+    {"Outrage", 0xC8, GEN_II},
+    {"Pain Split", 0xDC, GEN_II},
+    {"Pay Day", 0x06, (GEN_I | GEN_II)},
+    {"Peck", 0x40, (GEN_I | GEN_II)},
+    {"Perish Song", 0xC3, GEN_II},
+    {"Petal Dance", 0x50, (GEN_I | GEN_II)},
+    {"Pin Missile", 0x2A, (GEN_I | GEN_II)},
+    {"Poison Gas", 0x8B, (GEN_I | GEN_II)},
+    {"Poisonpowder", 0x4D, (GEN_I | GEN_II)},
+    {"Poison Sting", 0x28, (GEN_I | GEN_II)},
+    {"Pound", 0x01, (GEN_I | GEN_II)},
+    {"Powder Snow", 0xB5, GEN_II},
+    {"Present", 0xD9, GEN_II},
+    {"Protect", 0xB6, GEN_II},
+    {"Psybeam", 0x3C, (GEN_I | GEN_II)},
+    {"Psychic", 0x5E, (GEN_I | GEN_II)},
+    {"Psych Up", 0xF4, GEN_II},
+    {"Psywave", 0x95, (GEN_I | GEN_II)},
+    {"Pursuit", 0xE4, GEN_II},
+    {"Quick Attack", 0x62, (GEN_I | GEN_II)},
+    {"Rage", 0x63, (GEN_I | GEN_II)},
+    {"Rain Dance", 0xF0, GEN_II},
+    {"Rapid Spin", 0xE5, GEN_II},
+    {"Razor Leaf", 0x4B, (GEN_I | GEN_II)},
+    {"Razor Wind", 0x0D, (GEN_I | GEN_II)},
+    {"Recover", 0x69, (GEN_I | GEN_II)},
+    {"Reflect", 0x73, (GEN_I | GEN_II)},
+    {"Rest", 0x9C, (GEN_I | GEN_II)},
+    {"Return", 0xD8, GEN_II},
+    {"Reversal", 0xB3, GEN_II},
+    {"Roar", 0x2E, (GEN_I | GEN_II)},
+    {"Rock Slide", 0x9D, (GEN_I | GEN_II)},
+    {"Rock Smash", 0xF9, GEN_II},
+    {"Rock Throw", 0x58, (GEN_I | GEN_II)},
+    {"Rolling Kick", 0x1B, (GEN_I | GEN_II)},
+    {"Rollout", 0xCD, GEN_II},
+    {"Sacred Fire", 0xDD, GEN_II},
+    {"Safeguard", 0xDB, GEN_II},
+    {"Sand Attack", 0x1C, (GEN_I | GEN_II)},
+    {"Sandstorm", 0xC9, GEN_II},
+    {"Scary Face", 0xB8, GEN_II},
+    {"Scratch", 0x0A, (GEN_I | GEN_II)},
+    {"Screech", 0x67, (GEN_I | GEN_II)},
+    {"Seismic Toss", 0x45, (GEN_I | GEN_II)},
+    {"Selfdestruct", 0x78, (GEN_I | GEN_II)},
+    {"Shadow Ball", 0xF7, GEN_II},
+    {"Sharpen", 0x9F, (GEN_I | GEN_II)},
+    {"Sing", 0x2F, (GEN_I | GEN_II)},
+    {"Sketch", 0xA6, GEN_II},
+    {"Skull Bash", 0x82, (GEN_I | GEN_II)},
+    {"Sky Attack", 0x8F, (GEN_I | GEN_II)},
+    {"Slam", 0x15, (GEN_I | GEN_II)},
+    {"Slash", 0xA3, (GEN_I | GEN_II)},
+    {"Sleep Powder", 0x4F, (GEN_I | GEN_II)},
+    {"Sleep Talk", 0xD6, GEN_II},
+    {"Sludge", 0x7C, (GEN_I | GEN_II)},
+    {"Sludge Bomb", 0xBC, GEN_II},
+    {"Smog", 0x7B, (GEN_I | GEN_II)},
+    {"Smokescreen", 0x6C, (GEN_I | GEN_II)},
+    {"Snore", 0xAD, GEN_II},
+    {"Softboiled", 0x87, (GEN_I | GEN_II)},
+    {"Solar Beam", 0x4C, (GEN_I | GEN_II)},
+    {"Sonicboom", 0x31, (GEN_I | GEN_II)},
+    {"Spark", 0xD1, GEN_II},
+    {"Spider Web", 0xA9, GEN_II},
+    {"Spike Cannon", 0x83, (GEN_I | GEN_II)},
+    {"Spikes", 0xBF, GEN_II},
+    {"Spite", 0xB4, GEN_II},
+    {"Splash", 0x96, (GEN_I | GEN_II)},
+    {"Spore", 0x93, (GEN_I | GEN_II)},
+    {"Steel Wing", 0xD3, GEN_II},
+    {"Stomp", 0x17, (GEN_I | GEN_II)},
+    {"Strength", 0x46, (GEN_I | GEN_II)},
+    {"String Shot", 0x51, (GEN_I | GEN_II)},
+    {"Struggle", 0xA5, (GEN_I | GEN_II)},
+    {"Stun Spore", 0x4E, (GEN_I | GEN_II)},
+    {"Submission", 0x42, (GEN_I | GEN_II)},
+    {"Substitute", 0xA4, (GEN_I | GEN_II)},
+    {"Sunny Day", 0xF1, GEN_II},
+    {"Super Fang", 0xA2, (GEN_I | GEN_II)},
+    {"Supersonic", 0x30, (GEN_I | GEN_II)},
+    {"Surf", 0x39, (GEN_I | GEN_II)},
+    {"Swagger", 0xCF, GEN_II},
+    {"Sweet Kiss", 0xBA, GEN_II},
+    {"Sweet Scent", 0xE6, GEN_II},
+    {"Swift", 0x81, (GEN_I | GEN_II)},
+    {"Swords Dance", 0x0E, (GEN_I | GEN_II)},
+    {"Synthesis", 0xEB, GEN_II},
+    {"Tackle", 0x21, (GEN_I | GEN_II)},
+    {"Tail Whip", 0x27, (GEN_I | GEN_II)},
+    {"Take Down", 0x24, (GEN_I | GEN_II)},
+    {"Teleport", 0x64, (GEN_I | GEN_II)},
+    {"Thief", 0xA8, GEN_II},
+    {"Thrash", 0x25, (GEN_I | GEN_II)},
+    {"Thunder", 0x57, (GEN_I | GEN_II)},
+    {"Thunderbolt", 0x55, (GEN_I | GEN_II)},
+    {"Thunderpunch", 0x09, (GEN_I | GEN_II)},
+    {"Thundershock", 0x54, (GEN_I | GEN_II)},
+    {"Thunder Wave", 0x56, (GEN_I | GEN_II)},
+    {"Toxic", 0x5C, (GEN_I | GEN_II)},
+    {"Transform", 0x90, (GEN_I | GEN_II)},
+    {"Tri Attack", 0xA1, (GEN_I | GEN_II)},
+    {"Triple Kick", 0xA7, GEN_II},
+    {"Twineedle", 0x29, (GEN_I | GEN_II)},
+    {"Twister", 0xEF, GEN_II},
+    {"Vicegrip", 0x0B, (GEN_I | GEN_II)},
+    {"Vine Whip", 0x16, (GEN_I | GEN_II)},
+    {"Vital Throw", 0xE9, GEN_II},
+    {"Waterfall", 0x7F, (GEN_I | GEN_II)},
+    {"Water Gun", 0x37, (GEN_I | GEN_II)},
+    {"Whirlpool", 0xFA, GEN_II},
+    {"Whirlwind", 0x12, (GEN_I | GEN_II)},
+    {"Wing Attack", 0x11, (GEN_I | GEN_II)},
+    {"Withdraw", 0x6E, (GEN_I | GEN_II)},
+    {"Wrap", 0x23, (GEN_I | GEN_II)},
+    {"Zap Cannon", 0xC0, GEN_II},
+    {},
+};

+ 8 - 0
move_nl.h

@@ -0,0 +1,8 @@
+#ifndef __MOVE_NL_H__
+#define __MOVE_NL_H__
+
+#pragma once
+
+extern const NamedList move_list[];
+
+#endif // __MOVE_NL_H__

+ 61 - 0
named_list.c

@@ -0,0 +1,61 @@
+#include <stddef.h>
+#include <stdint.h>
+
+#include <named_list.h>
+
+/* Get number of elements in a list
+ * This is not very efficient as-is since it has to walk the whole list.
+ */
+size_t namedlist_cnt(const NamedList* list) {
+    size_t i;
+
+    for(i = 0;; i++) {
+        if(list[i].name == NULL) return i;
+    }
+}
+
+/* Returns the generation mask of the requested item in the list */
+uint32_t namedlist_gen_get_pos(const NamedList* list, uint32_t pos) {
+    return list[pos].gen;
+}
+
+/* Returns the generation mask of the item in the list that matches the
+ * provided index. This will ultimately return the 0th element in the case
+ * of the provided index not matching any of the list elements.
+ */
+uint32_t namedlist_gen_get_index(const NamedList* list, uint32_t index) {
+    return list[namedlist_pos_get(list, index)].gen;
+}
+
+/* Returns the list position based on the provided index. If index is not
+ * matched, the 0th position is returned. In most lists this is a "NONE"
+ * indicator. e.g. No Move.
+ */
+uint32_t namedlist_pos_get(const NamedList* list, uint32_t index) {
+    int i;
+
+    for(i = 0;; i++) {
+        if(list[i].name == NULL) break;
+        if(index == list[i].index) return i;
+    }
+
+    /* This will return the first entry in case index is not matched.
+     * Could be surprising at runtime.
+     */
+    return 0;
+}
+
+/* Get the item's index value from the position specified */
+uint32_t namedlist_index_get(const NamedList* list, uint32_t pos) {
+    return list[pos].index;
+}
+
+/* Get a pointer to the item's name from an item's index */
+const char* namedlist_name_get_index(const NamedList* list, uint32_t index) {
+    return list[namedlist_pos_get(list, index)].name;
+}
+
+/* Get a pointer to the item's name from a position */
+const char* namedlist_name_get_pos(const NamedList* list, uint32_t pos) {
+    return list[pos].name;
+}

+ 46 - 0
named_list.h

@@ -0,0 +1,46 @@
+#ifndef __NAMED_LIST_H__
+#define __NAMED_LIST_H__
+
+#pragma once
+
+#include <stddef.h>
+#include <stdint.h>
+
+struct __attribute__((__packed__)) named_list {
+    const char* name;
+    const uint8_t index;
+    const uint8_t gen; // Bitfield of compatible generations
+};
+
+typedef struct named_list NamedList;
+
+/* Get number of elements in a list
+ * This is not very efficient as-is since it has to walk the whole list.
+ */
+size_t namedlist_cnt(const NamedList* list);
+
+/* Returns the generation mask of the requested item in the list */
+uint32_t namedlist_gen_get_pos(const NamedList* list, uint32_t pos);
+
+/* Returns the generation mask of the item in the list that matches the
+ * provided index. This will ultimately return the 0th element in the case
+ * of the provided index not matching any of the list elements.
+ */
+uint32_t namedlist_gen_get_index(const NamedList* list, uint32_t index);
+
+/* Returns the list position based on the provided index. If index is not
+ * matched, the 0th position is returned. In most lists this is a "NONE"
+ * indicator. e.g. No Move.
+ */
+uint32_t namedlist_pos_get(const NamedList* list, uint32_t index);
+
+/* Get the item's index value from the position specified */
+uint32_t namedlist_index_get(const NamedList* list, uint32_t pos);
+
+/* Get a pointer to the item's name from an item's index */
+const char* namedlist_name_get_index(const NamedList* list, uint32_t index);
+
+/* Get a pointer to the item's name from a position */
+const char* namedlist_name_get_pos(const NamedList* list, uint32_t pos);
+
+#endif //__NAMED_LIST_H__

Разница между файлами не показана из-за своего большого размера
+ 2 - 2129
pokemon_app.c


+ 6 - 70
pokemon_app.h

@@ -7,6 +7,7 @@
 #include <gui/view.h>
 #include <gui/view.h>
 #include <gui/view_dispatcher.h>
 #include <gui/view_dispatcher.h>
 #include <gui/icon.h>
 #include <gui/icon.h>
+#include <gui/modules/dialog_ex.h>
 #include <gui/modules/submenu.h>
 #include <gui/modules/submenu.h>
 #include <gui/modules/text_input.h>
 #include <gui/modules/text_input.h>
 #include <gui/modules/variable_item_list.h>
 #include <gui/modules/variable_item_list.h>
@@ -16,78 +17,27 @@
 
 
 #define TAG "Pokemon"
 #define TAG "Pokemon"
 
 
-/* #defines for the data table entries */
-#define GROWTH_FAST 4
-#define GROWTH_MEDIUM_FAST 0
-#define GROWTH_MEDIUM_SLOW 3
-#define GROWTH_SLOW 5
-
-struct pokemon_data_table {
-    const char* name;
-    const Icon* icon;
-    const uint8_t index;
-    const uint8_t base_hp;
-    const uint8_t base_atk;
-    const uint8_t base_def;
-    const uint8_t base_spd;
-    const uint8_t base_special;
-    const uint8_t type[2];
-    const uint8_t move[4];
-    const uint8_t growth;
-};
-
-typedef struct pokemon_data_table PokemonTable;
-
-struct named_list {
-    const char* name;
-    const uint8_t index;
-};
-
-typedef struct named_list NamedList;
-
 struct pokemon_fap {
 struct pokemon_fap {
+    /* Various anonymous pointers for flipper UI/navigation */
     ViewDispatcher* view_dispatcher;
     ViewDispatcher* view_dispatcher;
-
-    /* View ports for each of the application's steps */
-    View* select_view;
-    void* trade;
-
-    /* Scene manager */
     SceneManager* scene_manager;
     SceneManager* scene_manager;
-
-    /* gui modules used in the application lifetime */
+    void* select;
+    void* trade;
     Submenu* submenu;
     Submenu* submenu;
     TextInput* text_input;
     TextInput* text_input;
     VariableItemList* variable_item_list;
     VariableItemList* variable_item_list;
-
-    /* Table of pokemon data for Gen I */
-    const PokemonTable* pokemon_table;
-
-    /* List of moves, alphabetically ordered */
-    const NamedList* move_list;
-
-    /* List of types, alphabetically ordered */
-    const NamedList* type_list;
+    DialogEx* dialog_ex;
 
 
     /* Struct for holding trade data */
     /* Struct for holding trade data */
     /* NOTE: There may be some runtime memory savings by adding more intelligence
     /* NOTE: There may be some runtime memory savings by adding more intelligence
      * to views/trade and slimming down this struct to only contain the single
      * to views/trade and slimming down this struct to only contain the single
      * pokemon data rather than the full 6 member party data.
      * pokemon data rather than the full 6 member party data.
      */
      */
-    TradeBlock* trade_block;
+    PokemonData* pdata;
 
 
     /* Pin definition to actual Game Link Cable interface */
     /* Pin definition to actual Game Link Cable interface */
     struct gblink_pins pins;
     struct gblink_pins pins;
     int malveke_detected;
     int malveke_detected;
-
-    /* The currently selected pokemon */
-    int curr_pokemon;
-
-    /* TODO: Other variables will end up here, like selected level, EV/IV,
-     * moveset, etc. Likely will want to be another sub struct similar to
-     * the actual pokemon data structure.
-     */
-    int curr_stats;
 };
 };
 
 
 typedef struct pokemon_fap PokemonFap;
 typedef struct pokemon_fap PokemonFap;
@@ -100,18 +50,4 @@ typedef enum {
     AppViewExitConfirm,
     AppViewExitConfirm,
 } AppView;
 } AppView;
 
 
-int pokemon_table_get_num_from_index(const PokemonTable* table, uint8_t index);
-
-int pokemon_named_list_get_num_elements(const NamedList* list);
-
-int pokemon_named_list_get_list_pos_from_index(const NamedList* list, uint8_t index);
-
-const char* pokemon_named_list_get_name_from_index(const NamedList* list, uint8_t index);
-
-void pokemon_trade_block_set_default_name(char* dest, PokemonFap* pokemon_fap, size_t n);
-
-void pokemon_trade_block_recalculate(PokemonFap* pokemon_fap);
-
-void pokemon_trade_block_recalculate_stats_from_level(PokemonFap* pokemon_fap);
-
 #endif /* POKEMON_APP_H */
 #endif /* POKEMON_APP_H */

+ 912 - 0
pokemon_data.c

@@ -0,0 +1,912 @@
+#include <pokemon_icons.h>
+
+#include <storage/storage.h>
+
+#include "pokemon_data.h"
+#include "pokemon_data_i.h"
+#include "pokemon_app.h"
+#include "pokemon_char_encode.h"
+
+#include "pokemon_table.h"
+
+#include <named_list.h>
+#include <item_nl.h>
+#include <stat_nl.h>
+#include <type_nl.h>
+#include <move_nl.h>
+
+#include <missingno_i.h>
+
+#define RECALC_NONE 0x00
+#define RECALC_EXP 0x01
+#define RECALC_EVS 0x02
+#define RECALC_IVS 0x04
+#define RECALC_STATS 0x08
+#define RECALC_NICKNAME 0x10
+#define RECALC_MOVES 0x20
+#define RECALC_TYPES 0x40
+#define RECALC_ALL 0xFF
+
+#define FXBM_SPRITE_SIZE 362 // Each 50x50 sprite
+
+/* Text lookups to make debug output cleaner and easier to parse as a human */
+static char* stat_text_get(DataStat stat) {
+    switch(stat) {
+    case STAT_ATK:
+        return "ATK";
+    case STAT_DEF:
+        return "DEF";
+    case STAT_SPD:
+        return "SPD";
+    case STAT_SPC:
+        return "SPC";
+    case STAT_SPC_ATK:
+        return "SPC_ATK";
+    case STAT_SPC_DEF:
+        return "SPC_DEF";
+    case STAT_HP:
+        return "HP";
+    case STAT_TYPE:
+        return "Type";
+    case STAT_MOVE:
+        return "Move";
+    case STAT_ATK_EV:
+        return "ATK_EV";
+    case STAT_DEF_EV:
+        return "DEF_EV";
+    case STAT_SPD_EV:
+        return "SPD_EV";
+    case STAT_SPC_ATK_EV:
+    case STAT_SPC_DEF_EV:
+    case STAT_SPC_EV:
+        return "SPC_EV";
+    case STAT_HP_EV:
+        return "HP_EV";
+    case STAT_IV:
+        return "IV";
+    case STAT_ATK_IV:
+        return "ATK_IV";
+    case STAT_DEF_IV:
+        return "DEF_IV";
+    case STAT_SPD_IV:
+        return "SPD_IV";
+    case STAT_SPC_ATK_IV:
+    case STAT_SPC_DEF_IV:
+    case STAT_SPC_IV:
+        return "SPC_IV";
+    case STAT_HP_IV:
+        return "HP_IV";
+    case STAT_LEVEL:
+        return "Lvl.";
+    case STAT_INDEX:
+        return "Idx.";
+    case STAT_NUM:
+        return "Num.";
+    case STAT_CONDITION:
+        return "Cond.";
+    case STAT_NICKNAME:
+        return "Nick.";
+    case STAT_OT_NAME:
+        return "OT Name";
+    case STAT_OT_ID:
+        return "OT ID";
+    case STAT_TRAINER_NAME:
+        return "Trainer Name";
+    case STAT_SEL:
+        return "EV/IV Sel."; // which EV/IV calc to use
+    case STAT_EXP:
+        return "Exp.";
+    case STAT_HELD_ITEM:
+        return "Held Item";
+    case STAT_POKERUS:
+        return "Pokerus";
+    default:
+        return "UNKNOWN STAT";
+    }
+}
+
+/* Allocates a chunk of memory for the trade data block and sets up some
+ * default values.
+ */
+PokemonData* pokemon_data_alloc(uint8_t gen) {
+    PokemonData* pdata;
+
+    pdata = malloc(sizeof(PokemonData));
+    pdata->gen = gen;
+
+    /* Set up lists */
+    pdata->move_list = move_list;
+    pdata->type_list = type_list;
+    pdata->stat_list = stat_list;
+    pdata->item_list = item_list;
+    pdata->pokemon_table = table_pointer_get();
+
+    pdata->storage = furi_record_open(RECORD_STORAGE);
+    pdata->asset_path = furi_string_alloc_set(APP_ASSETS_PATH());
+    storage_common_resolve_path_and_ensure_app_directory(pdata->storage, pdata->asset_path);
+
+    switch(gen) {
+    case GEN_I:
+        /* Allocate trade block and set its size for the trade view to use */
+        pdata->trade_block_sz = sizeof(TradeBlockGenI);
+        pdata->trade_block = malloc(pdata->trade_block_sz);
+
+        /* The party_members element needs to be 0xff for unused */
+        memset(
+            ((TradeBlockGenI*)pdata->trade_block)->party_members,
+            0xFF,
+            sizeof(((TradeBlockGenI*)pdata->trade_block)->party_members));
+
+        pdata->party = ((TradeBlockGenI*)pdata->trade_block)->party;
+
+        /* Set party count to 1 */
+        ((TradeBlockGenI*)pdata->trade_block)->party_cnt = 1;
+
+        /* Set the max pokedex number, 0 indexed */
+        pdata->dex_max = 150;
+        break;
+    case GEN_II:
+        /* Allocate trade block and set its size for the trade view to use */
+        pdata->trade_block_sz = sizeof(TradeBlockGenII);
+        pdata->trade_block = malloc(pdata->trade_block_sz);
+
+        /* The party_members element needs to be 0xff for unused */
+        memset(
+            ((TradeBlockGenII*)pdata->trade_block)->party_members,
+            0xFF,
+            sizeof(((TradeBlockGenII*)pdata->trade_block)->party_members));
+
+        pdata->party = ((TradeBlockGenII*)pdata->trade_block)->party;
+
+        /* Set party count to 1 */
+        ((TradeBlockGenII*)pdata->trade_block)->party_cnt = 1;
+
+        /* Set the max pokedex number, 0 indexed */
+        pdata->dex_max = 250;
+        break;
+    default:
+        furi_crash("Invalid Gen");
+        break;
+    }
+
+    /* Trainer/OT name, not to exceed 7 characters! */
+    pokemon_name_set(pdata, STAT_TRAINER_NAME, "Flipper");
+    pokemon_name_set(pdata, STAT_OT_NAME, "Flipper");
+
+    /* OT trainer ID# */
+    pokemon_stat_set(pdata, STAT_OT_ID, NONE, 42069);
+
+    /* Notes:
+     * Move pp isn't explicitly set up, should be fine
+     * Catch/held isn't explicitly set up, should be okay for only Gen I support now
+     * Status condition isn't explicity let up, would you ever want to?
+     */
+
+    /* Set up initial pokemon and level */
+    /* This causes all other stats to be recalculated */
+    pokemon_stat_set(pdata, STAT_NUM, NONE, 0); // First Pokemon
+    pokemon_stat_set(pdata, STAT_LEVEL, NONE, 2); // Minimum level of 2
+
+    return pdata;
+}
+
+void pokemon_data_free(PokemonData* pdata) {
+    furi_record_close(RECORD_STORAGE);
+    free(pdata->trade_block);
+    if(pdata->bitmap && pdata->bitmap_num != 0) free(pdata->bitmap);
+    furi_string_free(pdata->asset_path);
+    free(pdata);
+}
+
+/* Recalculate values and stats based on their dependencies.
+ * The order of the if statements are in order of dependence from
+ * depending on no other value, to dpeneding on multiple other values.
+ *
+ * level:	depends on:	none
+ * iv: 		depends on: 	none (only what the EV/IV general setting is, which recalculates EV/IV at time of set)
+ * ev:		depends on: 	level (sometimes)
+ * exp:		depends on:	level, index
+ * moves:	depends on:	index
+ * types:	depends on:	index
+ * nickname:	depends on:	index
+ * atk/def/etc:	depends on:	level, iv, ev, index
+ */
+void pokemon_recalculate(PokemonData* pdata, uint8_t recalc) {
+    furi_assert(pdata);
+    int i;
+
+    if(recalc == RECALC_NONE) return;
+
+    /* Ordered in order of priority for calculating other stats */
+    if(recalc & RECALC_NICKNAME) pokemon_default_nickname_set(NULL, pdata, 0);
+
+    if(recalc & RECALC_MOVES) {
+        for(i = MOVE_0; i <= MOVE_3; i++) {
+            pokemon_stat_set(
+                pdata,
+                STAT_MOVE,
+                i,
+                table_stat_base_get(
+                    pdata->pokemon_table,
+                    pokemon_stat_get(pdata, STAT_NUM, NONE),
+                    STAT_BASE_MOVE,
+                    i));
+        }
+    }
+
+    if(recalc & RECALC_TYPES) {
+        for(i = TYPE_0; i <= TYPE_1; i++) {
+            pokemon_stat_set(
+                pdata,
+                STAT_TYPE,
+                i,
+                table_stat_base_get(
+                    pdata->pokemon_table,
+                    pokemon_stat_get(pdata, STAT_NUM, NONE),
+                    STAT_BASE_TYPE,
+                    i));
+        }
+    }
+
+    if(recalc & RECALC_EXP) pokemon_exp_calc(pdata);
+
+    if(recalc & RECALC_EVS) pokemon_stat_ev_calc(pdata, pdata->stat_sel);
+
+    /* This just rerolls the IVs, nothing really to calculate */
+    if(recalc & RECALC_IVS) pokemon_stat_iv_calc(pdata, pdata->stat_sel);
+
+    /* Note: This will still end up calculating spc_def on gen i pokemon.
+     * However, the way the accessors are set up the calculated value will
+     * never be written anywhere. This is just wasted CPU time.
+     */
+    if(recalc & RECALC_STATS) {
+        for(i = STAT; i < STAT_END; i++) {
+            pokemon_stat_calc(pdata, i);
+        }
+    }
+}
+
+/* This needs to convert to encoded characters */
+void pokemon_name_set(PokemonData* pdata, DataStat stat, char* name) {
+    furi_assert(pdata);
+    size_t len;
+    uint8_t gen = pdata->gen;
+    uint8_t* ptr = NULL;
+
+    switch(stat) {
+    case STAT_NICKNAME:
+        if(gen == GEN_I) ptr = ((TradeBlockGenI*)pdata->trade_block)->nickname[0].str;
+        if(gen == GEN_II) ptr = ((TradeBlockGenII*)pdata->trade_block)->nickname[0].str;
+        len = 10;
+        break;
+    case STAT_OT_NAME:
+        if(gen == GEN_I) ptr = ((TradeBlockGenI*)pdata->trade_block)->ot_name[0].str;
+        if(gen == GEN_II) ptr = ((TradeBlockGenII*)pdata->trade_block)->ot_name[0].str;
+        len = 7;
+        break;
+    case STAT_TRAINER_NAME:
+        if(gen == GEN_I) ptr = ((TradeBlockGenI*)pdata->trade_block)->trainer_name.str;
+        if(gen == GEN_II) ptr = ((TradeBlockGenII*)pdata->trade_block)->trainer_name.str;
+        len = 7;
+        break;
+    default:
+        furi_crash("name");
+        break;
+    }
+
+    /* Clear the buffer with TERM character */
+    memset(ptr, TERM_, LEN_NAME_BUF);
+
+    /* Set the encoded name in the buffer */
+    pokemon_str_to_encoded_array(ptr, name, len);
+    FURI_LOG_D(TAG, "[data] %s name set to %s", stat_text_get(stat), name);
+}
+
+void pokemon_name_get(PokemonData* pdata, DataStat stat, char* dest, size_t len) {
+    furi_assert(pdata);
+    uint8_t* ptr = NULL;
+    uint8_t gen = pdata->gen;
+
+    switch(stat) {
+    case STAT_NICKNAME:
+        if(gen == GEN_I) ptr = ((TradeBlockGenI*)pdata->trade_block)->nickname[0].str;
+        if(gen == GEN_II) ptr = ((TradeBlockGenII*)pdata->trade_block)->nickname[0].str;
+        break;
+    case STAT_OT_NAME:
+        if(gen == GEN_I) ptr = ((TradeBlockGenI*)pdata->trade_block)->ot_name[0].str;
+        if(gen == GEN_II) ptr = ((TradeBlockGenII*)pdata->trade_block)->ot_name[0].str;
+        break;
+    default:
+        furi_crash("name_get invalid");
+        break;
+    }
+
+    pokemon_encoded_array_to_str(dest, ptr, len);
+}
+
+/* If dest is not NULL, a copy of the default name is written to it as well */
+void pokemon_default_nickname_set(char* dest, PokemonData* pdata, size_t n) {
+    furi_assert(pdata);
+    unsigned int i;
+    char buf[LEN_NAME_BUF];
+
+    /* First, get the default name */
+    strncpy(
+        buf,
+        table_stat_name_get(pdata->pokemon_table, pokemon_stat_get(pdata, STAT_NUM, NONE)),
+        sizeof(buf));
+
+    /* Next, walk through and toupper() each character */
+    for(i = 0; i < sizeof(buf); i++) {
+        buf[i] = toupper(buf[i]);
+    }
+
+    pokemon_name_set(pdata, STAT_NICKNAME, buf);
+    FURI_LOG_D(TAG, "[data] Set default nickname");
+
+    if(dest != NULL) {
+        strncpy(dest, buf, n);
+    }
+}
+
+/* Each sprite 50x50 is 362 bytes long */
+uint8_t* pokemon_icon_get(PokemonData* pdata, int num) {
+    furi_assert(pdata);
+    File* file;
+    FuriString* path;
+    uint32_t size;
+    bool is_error = true;
+
+    if(pdata->bitmap_num != num) {
+        if(pdata->bitmap) {
+            free(pdata->bitmap);
+            pdata->bitmap = NULL;
+        }
+
+        file = storage_file_alloc(pdata->storage);
+        path = furi_string_alloc_set(pdata->asset_path);
+        furi_string_cat_printf(path, "all_sprites.fxbm");
+
+        if(storage_file_open(file, furi_string_get_cstr(path), FSAM_READ, FSOM_OPEN_EXISTING)) {
+            storage_file_seek(file, (num - 1) * FXBM_SPRITE_SIZE, true);
+            if(storage_file_read(file, &size, sizeof(size)) == sizeof(size)) {
+                pdata->bitmap = malloc(size);
+                if(storage_file_read(file, pdata->bitmap, size) ==
+                   FXBM_SPRITE_SIZE - sizeof(size)) {
+                    FURI_LOG_D(TAG, "Opened file \'%s\'", furi_string_get_cstr(path));
+                    is_error = false;
+                } else {
+                    free(pdata->bitmap);
+                }
+            }
+        }
+
+        if(is_error) {
+            FURI_LOG_E(
+                TAG, "Failed to open \'%s\' or access sprite data", furi_string_get_cstr(path));
+            pdata->bitmap = (struct fxbm_sprite*)((uint8_t*)(__000_fxbm) + sizeof(size));
+            num = 0;
+        }
+
+        storage_file_free(file);
+        furi_string_free(path);
+
+        pdata->bitmap_num = num;
+    }
+
+    return (uint8_t*)pdata->bitmap;
+}
+
+uint16_t pokemon_stat_get(PokemonData* pdata, DataStat stat, DataStatSub which) {
+    furi_assert(pdata);
+    void* party = pdata->party;
+    int gen = pdata->gen;
+    uint16_t val = 0;
+    uint8_t hp_iv = 0;
+
+    switch(stat) {
+    case STAT_ATK:
+        if(gen == GEN_I) val = ((PokemonPartyGenI*)party)->atk;
+        if(gen == GEN_II) val = ((PokemonPartyGenII*)party)->atk;
+        break;
+    case STAT_DEF:
+        if(gen == GEN_I) val = ((PokemonPartyGenI*)party)->def;
+        if(gen == GEN_II) val = ((PokemonPartyGenII*)party)->def;
+        break;
+    case STAT_SPD:
+        if(gen == GEN_I) val = ((PokemonPartyGenI*)party)->spd;
+        if(gen == GEN_II) val = ((PokemonPartyGenII*)party)->spd;
+        break;
+    case STAT_SPC:
+        if(gen == GEN_I) val = ((PokemonPartyGenI*)party)->spc;
+        break;
+    case STAT_SPC_ATK:
+        if(gen == GEN_II) val = ((PokemonPartyGenII*)party)->spc_atk;
+        break;
+    case STAT_SPC_DEF:
+        if(gen == GEN_II) val = ((PokemonPartyGenII*)party)->spc_def;
+        break;
+    case STAT_HP:
+        if(gen == GEN_I) val = ((PokemonPartyGenI*)party)->hp;
+        if(gen == GEN_II) val = ((PokemonPartyGenII*)party)->hp;
+        break;
+    case STAT_ATK_EV:
+        if(gen == GEN_I) val = ((PokemonPartyGenI*)party)->atk_ev;
+        if(gen == GEN_II) val = ((PokemonPartyGenII*)party)->atk_ev;
+        break;
+    case STAT_DEF_EV:
+        if(gen == GEN_I) val = ((PokemonPartyGenI*)party)->def_ev;
+        if(gen == GEN_II) val = ((PokemonPartyGenII*)party)->def_ev;
+        break;
+    case STAT_SPD_EV:
+        if(gen == GEN_I) val = ((PokemonPartyGenI*)party)->spd_ev;
+        if(gen == GEN_II) val = ((PokemonPartyGenII*)party)->spd_ev;
+        break;
+    case STAT_SPC_EV:
+    case STAT_SPC_ATK_EV:
+    case STAT_SPC_DEF_EV:
+        if(gen == GEN_I) val = ((PokemonPartyGenI*)party)->spc_ev;
+        if(gen == GEN_II) val = ((PokemonPartyGenII*)party)->spc_ev;
+        break;
+    case STAT_HP_EV:
+        if(gen == GEN_I) val = ((PokemonPartyGenI*)party)->hp_ev;
+        if(gen == GEN_II) val = ((PokemonPartyGenII*)party)->hp_ev;
+        break;
+    case STAT_IV:
+        if(gen == GEN_I) val = ((PokemonPartyGenI*)party)->iv;
+        if(gen == GEN_II) val = ((PokemonPartyGenII*)party)->iv;
+        break;
+    /* The IVs in GB byte order, are always:
+     * atk, def, spd, spc
+     * Like every other 16 bit quantity that the Flipper acts on, we need to
+     * bytw swap them normally. However, the below accessors for individual
+     * IV nibbles directly pull from the data structures which will always
+     * be in GB endianness.
+     */
+    case STAT_SPD_IV:
+        if(gen == GEN_I) return (((PokemonPartyGenI*)party)->iv >> 12) & 0x0F;
+        if(gen == GEN_II) return (((PokemonPartyGenII*)party)->iv >> 12) & 0x0F;
+        break;
+    /* In order to line up all of the dynamic stat accessors used as part of the
+     * stat calculation loop, we need to overload the SPC IV accessor to allow
+     * accessing SPC, SPC_ATK, and SPC_DEF. Note that only SPC exists, the ATK
+     * and DEF are the overloaded values. This is so when, for example, gen i
+     * calculates its SPC value, or gen ii calculates is SPC_DEF value, it will
+     * always grab the same IV nibble.
+     */
+    case STAT_SPC_IV:
+    case STAT_SPC_ATK_IV:
+    case STAT_SPC_DEF_IV:
+        if(gen == GEN_I) return (((PokemonPartyGenI*)party)->iv >> 8) & 0x0F;
+        if(gen == GEN_II) return (((PokemonPartyGenII*)party)->iv >> 8) & 0x0F;
+        break;
+    case STAT_ATK_IV:
+        if(gen == GEN_I) return (((PokemonPartyGenI*)party)->iv >> 4) & 0x0F;
+        if(gen == GEN_II) return (((PokemonPartyGenII*)party)->iv >> 4) & 0x0F;
+        break;
+    case STAT_DEF_IV:
+        if(gen == GEN_I) return ((PokemonPartyGenI*)party)->iv & 0x0F;
+        if(gen == GEN_II) return ((PokemonPartyGenII*)party)->iv & 0x0F;
+        break;
+    case STAT_HP_IV:
+        /* NOTE:
+	 * HP IV is calculated as the LSB of each other IV, assembled in the
+	 * same bit order down to a single nibble.
+	 */
+        if(gen == GEN_I) val = (((PokemonPartyGenI*)party)->iv);
+        if(gen == GEN_II) val = (((PokemonPartyGenII*)party)->iv);
+        /* NOTE:
+	 * As noted above, we store the IV in the trade struct in the byte order
+	 * of the gameboy which is swapped from the Flipper's byte order.
+	 */
+        hp_iv |= ((val & 0x0010) >> 1); // ATK IV, MSbit of the hp_iv nibble
+        hp_iv |= ((val & 0x0001) << 2); // DEF IV, right of ATK IV in hp_iv nibble
+        hp_iv |= ((val & 0x1000) >> 11); // SPD IV, right of DEF IV in hp_iv nibble
+        hp_iv |= ((val & 0x0100) >> 8); // SPC IV, right of SPD IV in hp_iv nibble
+        return hp_iv;
+        break;
+    case STAT_LEVEL:
+        if(gen == GEN_I) return ((PokemonPartyGenI*)party)->level;
+        if(gen == GEN_II) return ((PokemonPartyGenII*)party)->level;
+        break;
+    case STAT_INDEX:
+        if(gen == GEN_I) return ((PokemonPartyGenI*)party)->index;
+        if(gen == GEN_II) return ((PokemonPartyGenII*)party)->index - 1;
+        break;
+    /* In Gen I, index is not relative at all to dex num.
+     * In Gen II, index is the same as the dex num.
+     */
+    case STAT_NUM:
+        if(gen == GEN_I) {
+            val = ((PokemonPartyGenI*)party)->index;
+            return table_pokemon_pos_get(pdata->pokemon_table, val);
+        }
+        if(gen == GEN_II) return ((PokemonPartyGenII*)party)->index - 1;
+        break;
+    case STAT_MOVE:
+        if(gen == GEN_I) return ((PokemonPartyGenI*)party)->move[which];
+        if(gen == GEN_II) return ((PokemonPartyGenII*)party)->move[which];
+        break;
+    case STAT_TYPE:
+        if(gen == GEN_I) return ((PokemonPartyGenI*)party)->type[which];
+        break;
+    case STAT_OT_ID:
+        if(gen == GEN_I) val = ((PokemonPartyGenI*)party)->ot_id;
+        if(gen == GEN_II) val = ((PokemonPartyGenII*)party)->ot_id;
+        break;
+    case STAT_POKERUS:
+        if(gen == GEN_II) return ((PokemonPartyGenII*)party)->pokerus;
+        break;
+    case STAT_SEL:
+        if(gen == GEN_I) return pdata->stat_sel;
+        if(gen == GEN_II) return pdata->stat_sel;
+        break;
+    case STAT_CONDITION:
+        if(gen == GEN_I) return ((PokemonPartyGenI*)party)->status_condition = val;
+        if(gen == GEN_II) return ((PokemonPartyGenII*)party)->status_condition = val;
+        break;
+    case STAT_HELD_ITEM:
+        if(gen == GEN_II) return ((PokemonPartyGenII*)party)->held_item;
+        break;
+    default:
+        furi_crash("STAT_GET: invalid stat");
+        break;
+    }
+
+    return __builtin_bswap16(val);
+}
+
+void pokemon_stat_set(PokemonData* pdata, DataStat stat, DataStatSub which, uint16_t val) {
+    furi_assert(pdata);
+    void* party = pdata->party;
+    int gen = pdata->gen;
+    uint8_t recalc = 0;
+    uint16_t val_swap = __builtin_bswap16(val);
+
+    switch(stat) {
+    case STAT_ATK:
+        if(gen == GEN_I) ((PokemonPartyGenI*)party)->atk = val_swap;
+        if(gen == GEN_II) ((PokemonPartyGenII*)party)->atk = val_swap;
+        break;
+    case STAT_DEF:
+        if(gen == GEN_I) ((PokemonPartyGenI*)party)->def = val_swap;
+        if(gen == GEN_II) ((PokemonPartyGenII*)party)->def = val_swap;
+        break;
+    case STAT_SPD:
+        if(gen == GEN_I) ((PokemonPartyGenI*)party)->spd = val_swap;
+        if(gen == GEN_II) ((PokemonPartyGenII*)party)->spd = val_swap;
+        break;
+    case STAT_SPC:
+        if(gen == GEN_I) ((PokemonPartyGenI*)party)->spc = val_swap;
+        break;
+    case STAT_SPC_ATK:
+        if(gen == GEN_II) ((PokemonPartyGenII*)party)->spc_atk = val_swap;
+        break;
+    case STAT_SPC_DEF:
+        if(gen == GEN_II) ((PokemonPartyGenII*)party)->spc_def = val_swap;
+        break;
+    case STAT_HP:
+        if(gen == GEN_I) {
+            ((PokemonPartyGenI*)party)->hp = val_swap;
+            ((PokemonPartyGenI*)party)->max_hp = val_swap;
+        }
+        if(gen == GEN_II) {
+            ((PokemonPartyGenII*)party)->hp = val_swap;
+            ((PokemonPartyGenII*)party)->max_hp = val_swap;
+        }
+        break;
+    case STAT_ATK_EV:
+        if(gen == GEN_I) ((PokemonPartyGenI*)party)->atk_ev = val_swap;
+        if(gen == GEN_II) ((PokemonPartyGenII*)party)->atk_ev = val_swap;
+        break;
+    case STAT_DEF_EV:
+        if(gen == GEN_I) ((PokemonPartyGenI*)party)->def_ev = val_swap;
+        if(gen == GEN_II) ((PokemonPartyGenII*)party)->def_ev = val_swap;
+        break;
+    case STAT_SPD_EV:
+        if(gen == GEN_I) ((PokemonPartyGenI*)party)->spd_ev = val_swap;
+        if(gen == GEN_II) ((PokemonPartyGenII*)party)->spd_ev = val_swap;
+        break;
+    /* The SPC ATK/DEF EVs are not real values, we just pretend they are */
+    case STAT_SPC_EV:
+    case STAT_SPC_ATK_EV:
+    case STAT_SPC_DEF_EV:
+        if(gen == GEN_I) ((PokemonPartyGenI*)party)->spc_ev = val_swap;
+        if(gen == GEN_II) ((PokemonPartyGenII*)party)->spc_ev = val_swap;
+        break;
+    case STAT_HP_EV:
+        if(gen == GEN_I) ((PokemonPartyGenI*)party)->hp_ev = val_swap;
+        if(gen == GEN_II) ((PokemonPartyGenII*)party)->hp_ev = val_swap;
+        break;
+    case STAT_IV:
+        if(gen == GEN_I) ((PokemonPartyGenI*)party)->iv = val_swap;
+        if(gen == GEN_II) ((PokemonPartyGenII*)party)->iv = val_swap;
+        break;
+    /* The IVs in GB byte order, are always:
+     * atk, def, spd, spc
+     * Like every other 16 bit quantity that the Flipper acts on, we need to
+     * bytw swap them normally. However, the below accessors for individual
+     * IV nibbles directly manipulate the data structures which will always
+     * be in GB endianness.
+     */
+    case STAT_SPD_IV:
+        if(gen == GEN_I) {
+            ((PokemonPartyGenI*)party)->iv &= ~(0x0F << 12);
+            ((PokemonPartyGenI*)party)->iv |= ((val & 0x0F) << 12);
+        }
+        if(gen == GEN_II) {
+            ((PokemonPartyGenII*)party)->iv &= ~(0x0F << 12);
+            ((PokemonPartyGenII*)party)->iv |= ((val & 0x0F) << 12);
+        }
+        break;
+    /* In order to line up all of the dynamic stat accessors used as part of the
+     * stat calculation loop, we need to overload the SPC IV accessor to allow
+     * accessing SPC, SPC_ATK, and SPC_DEF. Note that only SPC exists, the ATK
+     * and DEF are the overloaded values. This is so when, for example, gen i
+     * calculates its SPC value, or gen ii calculates is SPC_DEF value, it will
+     * always grab the same IV nibble.
+     */
+    case STAT_SPC_IV:
+    case STAT_SPC_ATK_IV:
+    case STAT_SPC_DEF_IV:
+        if(gen == GEN_I) {
+            ((PokemonPartyGenI*)party)->iv &= ~(0x0F << 8);
+            ((PokemonPartyGenI*)party)->iv |= ((val & 0x0F) << 8);
+        }
+        if(gen == GEN_II) {
+            ((PokemonPartyGenII*)party)->iv &= ~(0x0F << 8);
+            ((PokemonPartyGenII*)party)->iv |= ((val & 0x0F) << 8);
+        }
+        break;
+    case STAT_ATK_IV:
+        if(gen == GEN_I) {
+            ((PokemonPartyGenI*)party)->iv &= ~(0x0F << 4);
+            ((PokemonPartyGenI*)party)->iv |= ((val & 0x0F) << 4);
+        }
+        if(gen == GEN_II) {
+            ((PokemonPartyGenII*)party)->iv &= ~(0x0F << 4);
+            ((PokemonPartyGenII*)party)->iv |= ((val & 0x0F) << 4);
+        }
+        break;
+    case STAT_DEF_IV:
+        if(gen == GEN_I) {
+            ((PokemonPartyGenI*)party)->iv &= ~(0x0F);
+            ((PokemonPartyGenI*)party)->iv |= (val & 0x0F);
+        }
+        if(gen == GEN_II) {
+            ((PokemonPartyGenII*)party)->iv &= ~(0x0F);
+            ((PokemonPartyGenII*)party)->iv |= (val & 0x0F);
+        }
+        break;
+    case STAT_MOVE:
+        if(gen == GEN_I) ((PokemonPartyGenI*)party)->move[which] = val;
+        if(gen == GEN_II) ((PokemonPartyGenII*)party)->move[which] = val;
+        break;
+    case STAT_TYPE:
+        /* Gen II doesn't have type assignment */
+        if(gen == GEN_I) ((PokemonPartyGenI*)party)->type[which] = val;
+        break;
+    case STAT_LEVEL:
+        if(gen == GEN_I) {
+            ((PokemonPartyGenI*)party)->level = val;
+            ((PokemonPartyGenI*)party)->level_again = val;
+        }
+        if(gen == GEN_II) ((PokemonPartyGenII*)party)->level = val;
+        recalc = (RECALC_STATS | RECALC_EXP | RECALC_EVS);
+        break;
+    /* In Gen I, index is not relative at all to dex num.
+     * In Gen II, index is the same as the dex num.
+     */
+    case STAT_INDEX:
+        if(gen == GEN_I) {
+            ((PokemonPartyGenI*)party)->index = val;
+            ((TradeBlockGenI*)pdata->trade_block)->party_members[0] = val;
+        }
+        if(gen == GEN_II) {
+            ((PokemonPartyGenII*)party)->index = val + 1;
+            ((TradeBlockGenII*)pdata->trade_block)->party_members[0] = val + 1;
+        }
+        recalc = RECALC_ALL; // Always recalculate everything if we selected a different pokemon
+        break;
+    case STAT_NUM:
+        if(gen == GEN_I)
+            pokemon_stat_set(
+                pdata,
+                STAT_INDEX,
+                NONE,
+                table_stat_base_get(pdata->pokemon_table, val, STAT_BASE_INDEX, NONE));
+        if(gen == GEN_II) pokemon_stat_set(pdata, STAT_INDEX, NONE, val);
+        break;
+    case STAT_OT_ID:
+        if(gen == GEN_I) ((PokemonPartyGenI*)party)->ot_id = val_swap;
+        if(gen == GEN_II) ((PokemonPartyGenII*)party)->ot_id = val_swap;
+        break;
+    case STAT_POKERUS:
+        if(gen == GEN_II) ((PokemonPartyGenII*)party)->pokerus = val;
+        break;
+    case STAT_SEL:
+        pdata->stat_sel = val;
+        recalc = (RECALC_EVS | RECALC_IVS | RECALC_STATS);
+        break;
+    case STAT_EXP:
+        if(gen == GEN_I) ((PokemonPartyGenI*)party)->exp[which] = val;
+        if(gen == GEN_II) ((PokemonPartyGenII*)party)->exp[which] = val;
+        break;
+    case STAT_CONDITION:
+        if(gen == GEN_I) ((PokemonPartyGenI*)party)->status_condition = val;
+        if(gen == GEN_II) ((PokemonPartyGenII*)party)->status_condition = val;
+        break;
+    case STAT_HELD_ITEM:
+        if(gen == GEN_II) ((PokemonPartyGenII*)party)->held_item = val;
+        break;
+    default:
+        furi_crash("STAT_SET: invalid stat");
+        break;
+    }
+    FURI_LOG_D(TAG, "[data] stat %s:%d set to 0x%X", stat_text_get(stat), which, val);
+    pokemon_recalculate(pdata, recalc);
+}
+
+static void pokemon_stat_ev_calc(PokemonData* pdata, EvIv val) {
+    furi_assert(pdata);
+    int level;
+    uint16_t ev;
+    DataStat i;
+
+    level = pokemon_stat_get(pdata, STAT_LEVEL, NONE);
+
+    /* Generate STATEXP */
+    switch(val) {
+    case RANDIV_LEVELEV:
+    case MAXIV_LEVELEV:
+        ev = (0xffff / 100) * level;
+        break;
+    case RANDIV_MAXEV:
+    case MAXIV_MAXEV:
+        ev = 0xffff;
+        break;
+    default:
+        ev = 0;
+        break;
+    }
+
+    for(i = STAT_EV; i < STAT_EV_END; i++) {
+        pokemon_stat_set(pdata, i, NONE, ev);
+    }
+}
+
+static void pokemon_stat_iv_calc(PokemonData* pdata, EvIv val) {
+    furi_assert(pdata);
+    uint16_t iv;
+
+    /* Set up IVs */
+    switch(val) {
+    case RANDIV_ZEROEV:
+    case RANDIV_LEVELEV:
+    case RANDIV_MAXEV:
+        iv = (uint16_t)rand();
+        break;
+    default:
+        iv = 0xFFFF;
+        break;
+    }
+
+    pokemon_stat_set(pdata, STAT_IV, NONE, iv);
+}
+
+#define UINT32_TO_EXP(input, output_array)                     \
+    do {                                                       \
+        (output_array)[2] = (uint8_t)((input) & 0xFF);         \
+        (output_array)[1] = (uint8_t)(((input) >> 8) & 0xFF);  \
+        (output_array)[0] = (uint8_t)(((input) >> 16) & 0xFF); \
+    } while(0)
+
+void pokemon_exp_set(PokemonData* pdata, uint32_t exp) {
+    furi_assert(pdata);
+    uint8_t exp_tmp[3];
+    int i;
+
+    UINT32_TO_EXP(exp, exp_tmp);
+
+    for(i = EXP_0; i <= EXP_2; i++) {
+        pokemon_stat_set(pdata, STAT_EXP, i, exp_tmp[i]);
+    }
+
+    FURI_LOG_D(TAG, "[data] Set pkmn exp %d", (int)exp);
+}
+
+void pokemon_exp_calc(PokemonData* pdata) {
+    furi_assert(pdata);
+    int level;
+    uint32_t exp;
+    uint8_t growth = table_stat_base_get(
+        pdata->pokemon_table, pokemon_stat_get(pdata, STAT_NUM, NONE), STAT_BASE_GROWTH, NONE);
+
+    level = (int)pokemon_stat_get(pdata, STAT_LEVEL, NONE);
+    /* Calculate exp */
+    switch(growth) {
+    case GROWTH_FAST:
+        // https://bulbapedia.bulbagarden.net/wiki/Experience#Fast
+        exp = (4 * level * level * level) / 5;
+        break;
+    case GROWTH_MEDIUM_FAST:
+        // https://bulbapedia.bulbagarden.net/wiki/Experience#Medium_Fast
+        exp = (level * level * level);
+        break;
+    case GROWTH_MEDIUM_SLOW:
+        // https://bulbapedia.bulbagarden.net/wiki/Experience#Medium_Slow
+        exp = (((level * level * level) * 6 / 5) - (15 * level * level) + (100 * level) - 140);
+        break;
+    case GROWTH_SLOW:
+        // https://bulbapedia.bulbagarden.net/wiki/Experience#Slow
+        exp = (5 * level * level * level) / 4;
+        break;
+    default:
+        furi_crash("incorrect growth val");
+        break;
+    }
+
+    pokemon_exp_set(pdata, exp);
+}
+
+/* Calculates stat from current level */
+void pokemon_stat_calc(PokemonData* pdata, DataStat stat) {
+    furi_assert(pdata);
+    uint8_t iv;
+    uint16_t ev;
+    uint8_t base;
+    uint8_t level;
+    uint16_t calc;
+
+    level = pokemon_stat_get(pdata, STAT_LEVEL, NONE);
+    base = table_stat_base_get(
+        pdata->pokemon_table, pokemon_stat_get(pdata, STAT_NUM, NONE), stat, NONE);
+    ev = pokemon_stat_get(pdata, stat + STAT_EV_OFFS, NONE);
+    iv = pokemon_stat_get(pdata, stat + STAT_IV_OFFS, NONE);
+
+    /* Gen I and II calculation */
+    // https://bulbapedia.bulbagarden.net/wiki/Stat#Generations_I_and_II
+    calc = floor((((2 * (base + iv)) + floor(sqrt(ev) / 4)) * level) / 100);
+
+    if(stat == STAT_HP)
+        calc += (level + 10);
+    else
+        calc += 5;
+
+    pokemon_stat_set(pdata, stat, NONE, calc);
+}
+
+/* Copy the traded-in Pokemon's main data to our struct */
+void pokemon_stat_memcpy(PokemonData* dst, PokemonData* src, uint8_t which) {
+    if(dst->gen == GEN_I) {
+        ((TradeBlockGenI*)dst->trade_block)->party_members[0] =
+            ((TradeBlockGenI*)src->trade_block)->party_members[which];
+        memcpy(
+            &(((TradeBlockGenI*)dst->trade_block)->party[0]),
+            &(((TradeBlockGenI*)src->trade_block)->party[which]),
+            sizeof(PokemonPartyGenI));
+        memcpy(
+            &(((TradeBlockGenI*)dst->trade_block)->nickname[0]),
+            &(((TradeBlockGenI*)src->trade_block)->nickname[which]),
+            sizeof(struct name));
+        memcpy(
+            &(((TradeBlockGenI*)dst->trade_block)->ot_name[0]),
+            &(((TradeBlockGenI*)src->trade_block)->ot_name[which]),
+            sizeof(struct name));
+    } else if(dst->gen == GEN_II) {
+        ((TradeBlockGenI*)dst->trade_block)->party_members[0] =
+            ((TradeBlockGenI*)src->trade_block)->party_members[which];
+        memcpy(
+            &(((TradeBlockGenII*)dst->trade_block)->party[0]),
+            &(((TradeBlockGenII*)src->trade_block)->party[which]),
+            sizeof(PokemonPartyGenI));
+        memcpy(
+            &(((TradeBlockGenII*)dst->trade_block)->nickname[0]),
+            &(((TradeBlockGenII*)src->trade_block)->nickname[which]),
+            sizeof(struct name));
+        memcpy(
+            &(((TradeBlockGenII*)dst->trade_block)->ot_name[0]),
+            &(((TradeBlockGenII*)src->trade_block)->ot_name[which]),
+            sizeof(struct name));
+    }
+}

+ 79 - 62
pokemon_data.h

@@ -3,72 +3,89 @@
 
 
 #pragma once
 #pragma once
 
 
-/* The struct is laid out exactly as the data trasfer that gets sent for trade
- * information. It has to be packed in order to not have padding in the Flipper.
- * Packing is always potentially filled with pitfalls, however this has worked
- * in testing without issue and this code isn't meant to be portable.
- */
-
-/* NOTE: These are all opposite endianness on the flipper than they are in the
- * GB/Z80. e.g. a uint16_t value of 0x2c01 translates to 0x012c.
- * Need to use __builtin_bswap16(val) to switch between Flipper and Pokemon.
- */
-/* This is 44 bytes in memory */
-struct __attribute__((__packed__)) pokemon_structure {
-    uint8_t index;
-    uint16_t hp; // Calculated from level
-    /* Level is normally calculated from exp, however, level is more human
-     * readable/digestable compared to exp. Therefore, we set legel and then
-     * from that calculate, (Max)HP, ATK, DEF, SPD, SPC.
-     */
-    uint8_t level;
-    uint8_t status_condition; // Do you really want to trade a Poisoned pokemon?
-    uint8_t type[2]; // Pokemon with a single type just repeat the type twice
-    uint8_t catch_held; // Unsure if this has any effect in Gen 1
-    uint8_t move[4];
-    uint16_t ot_id;
-    uint8_t exp[3]; // Calculated from level
-    uint16_t hp_ev;
-    uint16_t atk_ev;
-    uint16_t def_ev;
-    uint16_t spd_ev;
-    uint16_t special_ev;
-    uint16_t iv;
-    uint8_t move_pp[4];
-    uint8_t level_again; // Copy of level
-    uint16_t max_hp; // Calculated from level
-    uint16_t atk; // Calculated from level
-    uint16_t def; // Calculated from level
-    uint16_t spd; // Calculated from level
-    uint16_t special; // Calculated from level
-};
+#include <furi.h>
+#include <gui/icon.h>
+#include <storage/storage.h>
+#include <toolbox/stream/stream.h>
+#include <toolbox/stream/file_stream.h>
+
+#include <math.h>
+#include <stdint.h>
+
+#include <named_list.h>
+#include <stat_nl.h>
+#include <pokemon_table.h>
+#include "stats.h"
+
+/* Generation defines */
+#define GEN_I 0x01
+#define GEN_II 0x02
+
+/* Some length macros */
+#define LEN_NAME_BUF 11
+#define LEN_NICKNAME 11 // Max 10 chars
+#define LEN_OT_NAME 8 // Max 7 chars
+#define LEN_NUM_BUF 6
+#define LEN_LEVEL 4 // Max 3 digits
+#define LEN_OT_ID 6 // Max 5 digits
+
+typedef struct pokemon_party_data_gen_i PokemonPartyGenI;
+typedef struct trade_block_gen_i TradeBlockGenI;
+typedef struct pokemon_party_data_gen_ii PokemonPartyGenII;
+typedef struct trade_block_gen_ii TradeBlockGenII;
 
 
-struct __attribute__((__packed__)) name {
-    /* Reused a few times, but in Gen I, all name strings are 11 bytes in memory.
-     * At most, 10 symbols and a TERM_ byte.
-     * Note that some strings must be shorter than 11.
-     */
-    unsigned char str[11];
+/* Based on the flipperzero-game-engine sprite structure */
+struct fxbm_sprite {
+    uint32_t width;
+    uint32_t height;
+    uint8_t data[];
 };
 };
 
 
-/* This is 415 bytes in memory/transmitted */
-struct __attribute__((__packed__)) trade_data_block {
-    /* TODO: Change this to use struct name above */
-    unsigned char trainer_name[11];
-    uint8_t party_cnt;
-    /* Only the first pokemon is ever used even though there are 7 bytes here.
-     * If the remaining 6 bytes are _not_ 0xff, then the trade window renders
-     * garbage for the Flipper's party.
-     */
-    uint8_t party_members[7];
-    /* Only the first pokemon is set up, even though there are 6 total party members */
-    struct pokemon_structure party[6];
-    /* Only the first pokemon has an OT name and nickname even though there are 6 members */
-    /* OT name should not exceed 7 chars! */
-    struct name ot_name[6];
-    struct name nickname[6];
+struct pokemon_data {
+    const NamedList* move_list;
+    const NamedList* stat_list;
+    const NamedList* type_list;
+    const NamedList* item_list;
+    const PokemonTable* pokemon_table;
+    /* Pointer to the live trade_block */
+    void* trade_block;
+    /* The length of the current trade_block. */
+    size_t trade_block_sz;
+    /* Shortcut pointer to the actual party data in the trade block */
+    void* party;
+
+    /* Current EV/IV stat selection */
+    EvIv stat_sel;
+
+    /* Current generation */
+    uint8_t gen;
+
+    /* 0 indexed max pokedex number */
+    uint8_t dex_max;
+
+    /* These are private to pokemon_data */
+    Storage* storage;
+    struct fxbm_sprite* bitmap;
+    uint8_t bitmap_num;
+    FuriString* asset_path;
 };
 };
+typedef struct pokemon_data PokemonData;
+
+PokemonData* pokemon_data_alloc(uint8_t gen);
+void pokemon_data_free(PokemonData* pdata);
 
 
-typedef struct trade_data_block TradeBlock;
+uint8_t* pokemon_icon_get(PokemonData* pdata, int num);
 
 
+void pokemon_stat_memcpy(PokemonData* dst, PokemonData* src, uint8_t which);
+uint16_t pokemon_stat_get(PokemonData* pdata, DataStat stat, DataStatSub num);
+void pokemon_stat_set(PokemonData* pdata, DataStat stat, DataStatSub which, uint16_t val);
+uint16_t pokemon_stat_ev_get(PokemonData* pdata, DataStat stat);
+void pokemon_stat_ev_set(PokemonData* pdata, DataStat stat, uint16_t val);
+void pokemon_stat_iv_set(PokemonData* pdata, int val);
+void pokemon_exp_set(PokemonData* pdata, uint32_t exp);
+void pokemon_exp_calc(PokemonData* pdata);
+void pokemon_stat_calc(PokemonData* pdata, DataStat stat);
+void pokemon_default_nickname_set(char* dest, PokemonData* pdata, size_t n);
+void pokemon_name_set(PokemonData* pdata, DataStat stat, char* name);
+void pokemon_name_get(PokemonData* pdata, DataStat stat, char* dest, size_t len);
 #endif /* POKEMON_DATA_H */
 #endif /* POKEMON_DATA_H */

+ 143 - 0
pokemon_data_i.h

@@ -0,0 +1,143 @@
+#ifndef __POKEMON_DATA_I_H__
+#define __POKEMON_DATA_I_H__
+
+#include "pokemon_data.h"
+
+//#include "pokemon_app.h"
+//#include "pokemon_char_encode.h"
+
+static void pokemon_stat_ev_calc(PokemonData* pdata, EvIv val);
+static void pokemon_stat_iv_calc(PokemonData* pdata, EvIv val);
+
+/* The struct is laid out exactly as the data trasfer that gets sent for trade
+ * information. It has to be packed in order to not have padding in the Flipper.
+ * Packing is always potentially filled with pitfalls, however this has worked
+ * in testing without issue and this code isn't meant to be portable.
+ */
+
+/* NOTE: These are all opposite endianness on the flipper than they are in the
+ * GB/Z80. e.g. a uint16_t value of 0x2c01 translates to 0x012c.
+ * Need to use __builtin_bswap16(val) to switch between Flipper and Pokemon.
+ */
+/* This is 44 bytes in memory */
+struct __attribute__((__packed__)) pokemon_party_data_gen_i {
+    uint8_t index;
+    uint16_t hp; // Calculated from level
+    /* Level is normally calculated from exp, however, level is more human
+     * readable/digestable compared to exp. Therefore, we set legel and then
+     * from that calculate, (Max)HP, ATK, DEF, SPD, SPC.
+     */
+    uint8_t level;
+    uint8_t status_condition; // Do you really want to trade a Poisoned pokemon?
+    uint8_t type[2]; // Pokemon with a single type just repeat the type twice
+    uint8_t catch_held; // Unsure if this has any effect in Gen 1
+    uint8_t move[4];
+    uint16_t ot_id;
+    uint8_t exp[3]; // Calculated from level
+    uint16_t hp_ev;
+    uint16_t atk_ev;
+    uint16_t def_ev;
+    uint16_t spd_ev;
+    uint16_t spc_ev;
+    uint16_t iv;
+    uint8_t move_pp[4];
+    uint8_t level_again; // Copy of level
+    uint16_t max_hp; // Calculated from level
+    uint16_t atk; // Calculated from level
+    uint16_t def; // Calculated from level
+    uint16_t spd; // Calculated from level
+    uint16_t spc; // Calculated from level
+};
+
+struct __attribute__((__packed__)) name {
+    /* Reused a few times, but in Gen I, all name strings are 11 bytes in memory.
+     * At most, 10 symbols and a TERM_ byte.
+     * Note that some strings must be shorter than 11.
+     */
+    uint8_t str[LEN_NAME_BUF];
+};
+typedef struct name Name;
+
+/* This is 415 bytes in memory/transmitted */
+struct __attribute__((__packed__)) trade_block_gen_i {
+    Name trainer_name;
+    uint8_t party_cnt;
+    /* Only the first pokemon is ever used even though there are 7 bytes here.
+     * If the remaining 6 bytes are _not_ 0xff, then the trade window renders
+     * garbage for the Flipper's party.
+     */
+    uint8_t party_members[7];
+    /* Only the first pokemon is set up, even though there are 6 total party members */
+    PokemonPartyGenI party[6];
+    /* Only the first pokemon has an OT name and nickname even though there are 6 members */
+    /* OT name should not exceed 7 chars! */
+    Name ot_name[6];
+    Name nickname[6];
+};
+
+/* NOTE: These are all opposite endianness on the flipper than they are in the
+ * GB/Z80. e.g. a uint16_t value of 0x2c01 translates to 0x012c.
+ * Need to use __builtin_bswap16(val) to switch between Flipper and Pokemon.
+ */
+/* This is 48 bytes in memory */
+struct __attribute__((__packed__)) pokemon_party_data_gen_ii {
+    uint8_t index;
+    uint8_t held_item;
+    uint8_t move[4];
+    uint16_t ot_id;
+    uint8_t exp[3];
+    uint16_t hp_ev;
+    uint16_t atk_ev;
+    uint16_t def_ev;
+    uint16_t spd_ev;
+    uint16_t spc_ev;
+    uint16_t iv;
+    uint8_t move_pp[4];
+    uint8_t friendship;
+    uint8_t pokerus;
+    uint16_t caught_data;
+    /* Level is normally calculated from exp, however, level is more human
+     * readable/digestable compared to exp. Therefore, we set level and then
+     * from that calculate, (Max)HP, ATK, DEF, SPD, SPC.
+     */
+    uint8_t level;
+    uint8_t status_condition;
+    uint8_t unused;
+    uint16_t hp;
+    uint16_t max_hp;
+    uint16_t atk;
+    uint16_t def;
+    uint16_t spd;
+    uint16_t spc_atk;
+    uint16_t spc_def;
+};
+
+/* NOTE:
+ * For eggs in gen ii, the handling is a bit clever. The party structure is set
+ * up as normal for the pokemon that will hatch. The only difference is the
+ * friendship vairable is used to denote number of egg cycles remaining.
+ * Then, in the party_members array, that pokemon's index is set to 0xFD which
+ * is the index for an egg. Once traded, its now an egg.
+ * Creating an egg is not implemented at this time because I don't really see
+ * a reason to. But, knowing some of these details makes it really easy to
+ * implement later on.
+ */
+
+struct __attribute__((__packed__)) trade_block_gen_ii {
+    Name trainer_name;
+    uint8_t party_cnt;
+    /* Only the first pokemon is ever used even though there are 7 bytes here.
+     * If the remaining 6 bytes are _not_ 0xff, then the trade window renders
+     * garbage for the Flipper's party.
+     */
+    uint8_t party_members[7];
+    uint16_t trainer_id;
+    /* Only the first pokemon is set up, even though there are 6 total party members */
+    PokemonPartyGenII party[6];
+    /* Only the first pokemon has an OT name and nickname even though there are 6 members */
+    /* OT name should not exceed 7 chars! */
+    Name ot_name[6];
+    Name nickname[6];
+};
+
+#endif // __POKEMON_DATA_I_H__

+ 3361 - 0
pokemon_table.c

@@ -0,0 +1,3361 @@
+#include <stdint.h>
+
+#include <pokemon_table.h>
+#include "stats.h"
+
+#include <gui/icon.h>
+#include <pokemon_icons.h>
+
+#include <furi.h>
+
+/* NOTE: It seems like gen ii index is national pokedex order? */
+/* Gen i and Gen ii are _almost_ the same with all stats. The big difference
+ * is that while most gen i pokemon's spc matches the same gen ii spc_atk,
+ * some of them do differ. Therefore, we track spc for gen i, and then spc_atk
+ * and spc_def for gen ii.
+ */
+struct __attribute__((__packed__)) pokemon_data_table {
+    const char* name;
+    const uint8_t index;
+    const uint8_t base_hp;
+    const uint8_t base_atk;
+    const uint8_t base_def;
+    const uint8_t base_spd;
+    const uint8_t base_spc;
+    const uint8_t base_spc_atk;
+    const uint8_t base_spc_def;
+    const uint8_t type[2];
+    const uint8_t move[4];
+    const Growth growth;
+    const Gender gender_ratio;
+};
+
+int table_pokemon_pos_get(const PokemonTable* table, uint8_t index) {
+    int i;
+
+    for(i = 0;; i++) {
+        if(table[i].index == index) return i;
+        if(table[i].name == NULL) break;
+    }
+
+    /* This will return the first entry in case index is not matched.
+     * Could be surprising at runtime.
+     */
+    return 0;
+}
+
+const char* table_stat_name_get(const PokemonTable* table, int num) {
+    return table[num].name;
+}
+
+uint8_t
+    table_stat_base_get(const PokemonTable* table, uint8_t num, DataStat stat, DataStatSub which) {
+    furi_assert(table);
+
+    switch(stat) {
+    case STAT_BASE_INDEX:
+        return table[num].index;
+    case STAT_BASE_ATK:
+        return table[num].base_hp;
+    case STAT_BASE_DEF:
+        return table[num].base_def;
+    case STAT_BASE_SPD:
+        return table[num].base_spd;
+    case STAT_BASE_SPC:
+        return table[num].base_spc;
+    case STAT_BASE_SPC_ATK:
+        return table[num].base_spc_atk;
+    case STAT_BASE_SPC_DEF:
+        return table[num].base_spc_def;
+    case STAT_BASE_HP:
+        return table[num].base_hp;
+    case STAT_BASE_TYPE:
+        return table[num].type[which];
+    case STAT_BASE_MOVE:
+        return table[num].move[which];
+    case STAT_BASE_GROWTH:
+        return table[num].growth;
+    case STAT_BASE_GENDER_RATIO:
+        return table[num].gender_ratio;
+    default:
+        furi_crash("BASE_GET: invalid stat");
+        break;
+    }
+
+    return 0;
+}
+
+static const PokemonTable pokemon_table[] = {
+    /* Values for base_*, moves, etc., pulled directly from a copy of Pokemon Blue */
+    /* Note that, comparison between blue and gold show the same base stats for pokemon, with spc stk/def added in gold. spc atk is the same as spc between blue and gold for the first 151 pokemon. Movesets are different in gen ii for the first 151 pokeon, but, they are left with gen i movesets below. */
+    {"Bulbasaur",
+     0x99,
+     0x2D,
+     0x31,
+     0x31,
+     0x2D,
+     0x41,
+     0x41,
+     0x41,
+     {0x16, 0x03},
+     {0x21, 0x2D, 0x00, 0x00},
+     GROWTH_MEDIUM_SLOW,
+     GENDER_F12_5},
+    {"Ivysaur",
+     0x09,
+     0x3C,
+     0x3E,
+     0x3F,
+     0x3C,
+     0x50,
+     0x50,
+     0x50,
+     {0x16, 0x03},
+     {0x21, 0x2D, 0x49, 0x00},
+     GROWTH_MEDIUM_SLOW,
+     GENDER_F12_5},
+    {"Venusaur",
+     0x9A,
+     0x50,
+     0x52,
+     0x53,
+     0x50,
+     0x64,
+     0x64,
+     0x64,
+     {0x16, 0x03},
+     {0x21, 0x2D, 0x49, 0x16},
+     GROWTH_MEDIUM_SLOW,
+     GENDER_F12_5},
+    {"Charmander",
+     0xB0,
+     0x27,
+     0x34,
+     0x2B,
+     0x41,
+     0x32,
+     0x3C,
+     0x32,
+     {0x14, 0x14},
+     {0x0A, 0x2D, 0x00, 0x00},
+     GROWTH_MEDIUM_SLOW,
+     GENDER_F12_5},
+    {"Charmeleon",
+     0xB2,
+     0x3A,
+     0x40,
+     0x3A,
+     0x50,
+     0x41,
+     0x50,
+     0x41,
+     {0x14, 0x14},
+     {0x0A, 0x2D, 0x34, 0x00},
+     GROWTH_MEDIUM_SLOW,
+     GENDER_F12_5},
+    {"Charizard",
+     0xB4,
+     0x4E,
+     0x54,
+     0x4E,
+     0x64,
+     0x55,
+     0x6D,
+     0x55,
+     {0x14, 0x02},
+     {0x0A, 0x2D, 0x34, 0x2B},
+     GROWTH_MEDIUM_SLOW,
+     GENDER_F12_5},
+    {"Squirtle",
+     0xB1,
+     0x2C,
+     0x30,
+     0x41,
+     0x2B,
+     0x32,
+     0x32,
+     0x40,
+     {0x15, 0x15},
+     {0x21, 0x27, 0x00, 0x00},
+     GROWTH_MEDIUM_SLOW,
+     GENDER_F12_5},
+    {"Wartortle",
+     0xB3,
+     0x3B,
+     0x3F,
+     0x50,
+     0x3A,
+     0x41,
+     0x41,
+     0x50,
+     {0x15, 0x15},
+     {0x21, 0x27, 0x91, 0x00},
+     GROWTH_MEDIUM_SLOW,
+     GENDER_F12_5},
+    {"Blastoise",
+     0x1C,
+     0x4F,
+     0x53,
+     0x64,
+     0x4E,
+     0x55,
+     0x55,
+     0x69,
+     {0x15, 0x15},
+     {0x21, 0x27, 0x91, 0x37},
+     GROWTH_MEDIUM_SLOW,
+     GENDER_F12_5},
+    {"Caterpie",
+     0x7B,
+     0x2D,
+     0x1E,
+     0x23,
+     0x2D,
+     0x14,
+     0x14,
+     0x14,
+     {0x07, 0x07},
+     {0x21, 0x51, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Metapod",
+     0x7C,
+     0x32,
+     0x14,
+     0x37,
+     0x1E,
+     0x19,
+     0x19,
+     0x19,
+     {0x07, 0x07},
+     {0x6A, 0x00, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Butterfree",
+     0x7D,
+     0x3C,
+     0x2D,
+     0x32,
+     0x46,
+     0x50,
+     0x50,
+     0x50,
+     {0x07, 0x02},
+     {0x5D, 0x00, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Weedle",
+     0x70,
+     0x28,
+     0x23,
+     0x1E,
+     0x32,
+     0x14,
+     0x14,
+     0x14,
+     {0x07, 0x03},
+     {0x28, 0x51, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Kakuna",
+     0x71,
+     0x2D,
+     0x19,
+     0x32,
+     0x23,
+     0x19,
+     0x19,
+     0x19,
+     {0x07, 0x03},
+     {0x6A, 0x00, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Beedrill",
+     0x72,
+     0x41,
+     0x50,
+     0x28,
+     0x4B,
+     0x2D,
+     0x2D,
+     0x50,
+     {0x07, 0x03},
+     {0x1F, 0x00, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Pidgey",
+     0x24,
+     0x28,
+     0x2D,
+     0x28,
+     0x38,
+     0x23,
+     0x23,
+     0x23,
+     {0x00, 0x02},
+     {0x10, 0x00, 0x00, 0x00},
+     GROWTH_MEDIUM_SLOW,
+     GENDER_F50},
+    {"Pidgeotto",
+     0x96,
+     0x3F,
+     0x3C,
+     0x37,
+     0x47,
+     0x32,
+     0x32,
+     0x32,
+     {0x00, 0x02},
+     {0x10, 0x1C, 0x00, 0x00},
+     GROWTH_MEDIUM_SLOW,
+     GENDER_F50},
+    {"Pidgeot",
+     0x97,
+     0x53,
+     0x50,
+     0x4B,
+     0x5B,
+     0x46,
+     0x46,
+     0x46,
+     {0x00, 0x02},
+     {0x10, 0x1C, 0x62, 0x00},
+     GROWTH_MEDIUM_SLOW,
+     GENDER_F50},
+    {"Rattata",
+     0xA5,
+     0x1E,
+     0x38,
+     0x23,
+     0x48,
+     0x19,
+     0x19,
+     0x23,
+     {0x00, 0x00},
+     {0x21, 0x27, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Raticate",
+     0xA6,
+     0x37,
+     0x51,
+     0x3C,
+     0x61,
+     0x32,
+     0x32,
+     0x46,
+     {0x00, 0x00},
+     {0x21, 0x27, 0x62, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Spearow",
+     0x05,
+     0x28,
+     0x3C,
+     0x1E,
+     0x46,
+     0x1F,
+     0x1F,
+     0x1F,
+     {0x00, 0x02},
+     {0x40, 0x2D, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Fearow",
+     0x23,
+     0x41,
+     0x5A,
+     0x41,
+     0x64,
+     0x3D,
+     0x3D,
+     0x3D,
+     {0x00, 0x02},
+     {0x40, 0x2D, 0x2B, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Ekans",
+     0x6C,
+     0x23,
+     0x3C,
+     0x2C,
+     0x37,
+     0x28,
+     0x28,
+     0x36,
+     {0x03, 0x03},
+     {0x23, 0x2B, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Arbok",
+     0x2D,
+     0x3C,
+     0x55,
+     0x45,
+     0x50,
+     0x41,
+     0x41,
+     0x4F,
+     {0x03, 0x03},
+     {0x23, 0x2B, 0x28, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Pikachu",
+     0x54,
+     0x23,
+     0x37,
+     0x1E,
+     0x5A,
+     0x32,
+     0x32,
+     0x28,
+     {0x17, 0x17},
+     {0x54, 0x2D, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Raichu",
+     0x55,
+     0x3C,
+     0x5A,
+     0x37,
+     0x64,
+     0x5A,
+     0x5A,
+     0x50,
+     {0x17, 0x17},
+     {0x54, 0x2D, 0x56, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Sandshrew",
+     0x60,
+     0x32,
+     0x4B,
+     0x55,
+     0x28,
+     0x1E,
+     0x14,
+     0x1E,
+     {0x04, 0x04},
+     {0x0A, 0x00, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Sandslash",
+     0x61,
+     0x4B,
+     0x64,
+     0x6E,
+     0x41,
+     0x37,
+     0x2D,
+     0x37,
+     {0x04, 0x04},
+     {0x0A, 0x1C, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Nidoran\200",
+     0x0F,
+     0x37,
+     0x2F,
+     0x34,
+     0x29,
+     0x28,
+     0x28,
+     0x28,
+     {0x03, 0x03},
+     {0x2D, 0x21, 0x00, 0x00},
+     GROWTH_MEDIUM_SLOW,
+     GENDER_F100},
+    {"Nidorina",
+     0xA8,
+     0x46,
+     0x3E,
+     0x43,
+     0x38,
+     0x37,
+     0x37,
+     0x37,
+     {0x03, 0x03},
+     {0x2D, 0x21, 0x0A, 0x00},
+     GROWTH_MEDIUM_SLOW,
+     GENDER_F100},
+    {"Nidoqueen",
+     0x10,
+     0x5A,
+     0x52,
+     0x57,
+     0x4C,
+     0x4B,
+     0x4B,
+     0x55,
+     {0x03, 0x04},
+     {0x21, 0x0A, 0x27, 0x22},
+     GROWTH_MEDIUM_SLOW,
+     GENDER_F100},
+    {"Nidoran\201",
+     0x03,
+     0x2E,
+     0x39,
+     0x28,
+     0x32,
+     0x28,
+     0x28,
+     0x28,
+     {0x03, 0x03},
+     {0x2B, 0x21, 0x00, 0x00},
+     GROWTH_MEDIUM_SLOW,
+     GENDER_F0},
+    {"Nidorino",
+     0xA7,
+     0x3D,
+     0x48,
+     0x39,
+     0x41,
+     0x37,
+     0x37,
+     0x37,
+     {0x03, 0x03},
+     {0x2B, 0x21, 0x1E, 0x00},
+     GROWTH_MEDIUM_SLOW,
+     GENDER_F0},
+    {"Nidoking",
+     0x07,
+     0x51,
+     0x5C,
+     0x4D,
+     0x55,
+     0x4B,
+     0x55,
+     0x4B,
+     {0x03, 0x04},
+     {0x21, 0x1E, 0x28, 0x25},
+     GROWTH_MEDIUM_SLOW,
+     GENDER_F0},
+    {"Clefairy",
+     0x04,
+     0x46,
+     0x2D,
+     0x30,
+     0x23,
+     0x3C,
+     0x3C,
+     0x41,
+     {0x00, 0x00},
+     {0x01, 0x2D, 0x00, 0x00},
+     GROWTH_FAST,
+     GENDER_F75},
+    {"Clefable",
+     0x8E,
+     0x5F,
+     0x46,
+     0x49,
+     0x3C,
+     0x55,
+     0x55,
+     0x5A,
+     {0x00, 0x00},
+     {0x2F, 0x03, 0x6B, 0x76},
+     GROWTH_FAST,
+     GENDER_F75},
+    {"Vulpix",
+     0x52,
+     0x26,
+     0x29,
+     0x28,
+     0x41,
+     0x41,
+     0x32,
+     0x41,
+     {0x14, 0x14},
+     {0x34, 0x27, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F75},
+    {"Ninetales",
+     0x53,
+     0x49,
+     0x4C,
+     0x4B,
+     0x64,
+     0x64,
+     0x51,
+     0x64,
+     {0x14, 0x14},
+     {0x34, 0x27, 0x62, 0x2E},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F75},
+    {"Jigglypuff",
+     0x64,
+     0x73,
+     0x2D,
+     0x14,
+     0x14,
+     0x19,
+     0x2D,
+     0x19,
+     {0x00, 0x00},
+     {0x2F, 0x00, 0x00, 0x00},
+     GROWTH_FAST,
+     GENDER_F75},
+    {"Wigglytuff",
+     0x65,
+     0x8C,
+     0x46,
+     0x2D,
+     0x2D,
+     0x32,
+     0x4B,
+     0x32,
+     {0x00, 0x00},
+     {0x2F, 0x32, 0x6F, 0x03},
+     GROWTH_FAST,
+     GENDER_F75},
+    {"Zubat",
+     0x6B,
+     0x28,
+     0x2D,
+     0x23,
+     0x37,
+     0x28,
+     0x1E,
+     0x28,
+     {0x03, 0x02},
+     {0x8D, 0x00, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Golbat",
+     0x82,
+     0x4B,
+     0x50,
+     0x46,
+     0x5A,
+     0x4B,
+     0x41,
+     0x4B,
+     {0x03, 0x02},
+     {0x8D, 0x67, 0x2C, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Oddish",
+     0xB9,
+     0x2D,
+     0x32,
+     0x37,
+     0x1E,
+     0x4B,
+     0x4B,
+     0x41,
+     {0x16, 0x03},
+     {0x47, 0x00, 0x00, 0x00},
+     GROWTH_MEDIUM_SLOW,
+     GENDER_F50},
+    {"Gloom",
+     0xBA,
+     0x3C,
+     0x41,
+     0x46,
+     0x28,
+     0x55,
+     0x55,
+     0x4B,
+     {0x16, 0x03},
+     {0x47, 0x4D, 0x4E, 0x00},
+     GROWTH_MEDIUM_SLOW,
+     GENDER_F50},
+    {"Vileplume",
+     0xBB,
+     0x4B,
+     0x50,
+     0x55,
+     0x32,
+     0x64,
+     0x64,
+     0x5A,
+     {0x16, 0x03},
+     {0x4E, 0x4F, 0x33, 0x50},
+     GROWTH_MEDIUM_SLOW,
+     GENDER_F50},
+    {"Paras",
+     0x6D,
+     0x23,
+     0x46,
+     0x37,
+     0x19,
+     0x37,
+     0x2D,
+     0x37,
+     {0x07, 0x16},
+     {0x0A, 0x00, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Parasect",
+     0x2E,
+     0x3C,
+     0x5F,
+     0x50,
+     0x1E,
+     0x50,
+     0x3C,
+     0x50,
+     {0x07, 0x16},
+     {0x0A, 0x4E, 0x8D, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Venonat",
+     0x41,
+     0x3C,
+     0x37,
+     0x32,
+     0x2D,
+     0x28,
+     0x28,
+     0x37,
+     {0x07, 0x03},
+     {0x21, 0x32, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Venomoth",
+     0x77,
+     0x46,
+     0x41,
+     0x3C,
+     0x5A,
+     0x5A,
+     0x5A,
+     0x4B,
+     {0x07, 0x03},
+     {0x21, 0x32, 0x4D, 0x8D},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Diglett",
+     0x3B,
+     0x0A,
+     0x37,
+     0x19,
+     0x5F,
+     0x2D,
+     0x23,
+     0x2D,
+     {0x04, 0x04},
+     {0x0A, 0x00, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Dugtrio",
+     0x76,
+     0x23,
+     0x50,
+     0x32,
+     0x78,
+     0x46,
+     0x32,
+     0x46,
+     {0x04, 0x04},
+     {0x0A, 0x2D, 0x5B, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Meowth",
+     0x4D,
+     0x28,
+     0x2D,
+     0x23,
+     0x5A,
+     0x28,
+     0x28,
+     0x28,
+     {0x00, 0x00},
+     {0x0A, 0x2D, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Persian",
+     0x90,
+     0x41,
+     0x46,
+     0x3C,
+     0x73,
+     0x41,
+     0x41,
+     0x41,
+     {0x00, 0x00},
+     {0x0A, 0x2D, 0x2C, 0x67},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Psyduck",
+     0x2F,
+     0x32,
+     0x34,
+     0x30,
+     0x37,
+     0x32,
+     0x41,
+     0x32,
+     {0x15, 0x15},
+     {0x0A, 0x00, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Golduck",
+     0x80,
+     0x50,
+     0x52,
+     0x4E,
+     0x55,
+     0x50,
+     0x5F,
+     0x50,
+     {0x15, 0x15},
+     {0x0A, 0x27, 0x32, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Mankey",
+     0x39,
+     0x28,
+     0x50,
+     0x23,
+     0x46,
+     0x23,
+     0x23,
+     0x2D,
+     {0x01, 0x01},
+     {0x0A, 0x2B, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Primeape",
+     0x75,
+     0x41,
+     0x69,
+     0x3C,
+     0x5F,
+     0x3C,
+     0x3C,
+     0x46,
+     {0x01, 0x01},
+     {0x0A, 0x2B, 0x02, 0x9A},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Growlithe",
+     0x21,
+     0x37,
+     0x46,
+     0x2D,
+     0x3C,
+     0x32,
+     0x46,
+     0x32,
+     {0x14, 0x14},
+     {0x2C, 0x2E, 0x00, 0x00},
+     GROWTH_SLOW,
+     GENDER_F25},
+    {"Arcanine",
+     0x14,
+     0x5A,
+     0x6E,
+     0x50,
+     0x5F,
+     0x50,
+     0x64,
+     0x50,
+     {0x14, 0x14},
+     {0x2E, 0x34, 0x2B, 0x24},
+     GROWTH_SLOW,
+     GENDER_F25},
+    {"Poliwag",
+     0x47,
+     0x28,
+     0x32,
+     0x28,
+     0x5A,
+     0x28,
+     0x28,
+     0x28,
+     {0x15, 0x15},
+     {0x91, 0x00, 0x00, 0x00},
+     GROWTH_MEDIUM_SLOW,
+     GENDER_F50},
+    {"Poliwhirl",
+     0x6E,
+     0x41,
+     0x41,
+     0x41,
+     0x5A,
+     0x32,
+     0x32,
+     0x32,
+     {0x15, 0x15},
+     {0x91, 0x5F, 0x37, 0x00},
+     GROWTH_MEDIUM_SLOW,
+     GENDER_F50},
+    {"Poliwrath",
+     0x6F,
+     0x5A,
+     0x55,
+     0x5F,
+     0x46,
+     0x46,
+     0x46,
+     0x5A,
+     {0x15, 0x01},
+     {0x5F, 0x37, 0x03, 0x22},
+     GROWTH_MEDIUM_SLOW,
+     GENDER_F50},
+    {"Abra",
+     0x94,
+     0x19,
+     0x14,
+     0x0F,
+     0x5A,
+     0x69,
+     0x69,
+     0x37,
+     {0x18, 0x18},
+     {0x64, 0x00, 0x00, 0x00},
+     GROWTH_MEDIUM_SLOW,
+     GENDER_F25},
+    {"Kadabra",
+     0x26,
+     0x28,
+     0x23,
+     0x1E,
+     0x69,
+     0x78,
+     0x78,
+     0x46,
+     {0x18, 0x18},
+     {0x64, 0x5D, 0x32, 0x00},
+     GROWTH_MEDIUM_SLOW,
+     GENDER_F25},
+    {"Alakazam",
+     0x95,
+     0x37,
+     0x32,
+     0x2D,
+     0x78,
+     0x87,
+     0x87,
+     0x55,
+     {0x18, 0x18},
+     {0x64, 0x5D, 0x32, 0x00},
+     GROWTH_MEDIUM_SLOW,
+     GENDER_F25},
+    {"Machop",
+     0x6A,
+     0x46,
+     0x50,
+     0x32,
+     0x23,
+     0x23,
+     0x23,
+     0x23,
+     {0x01, 0x01},
+     {0x02, 0x00, 0x00, 0x00},
+     GROWTH_MEDIUM_SLOW,
+     GENDER_F25},
+    {"Machoke",
+     0x29,
+     0x50,
+     0x64,
+     0x46,
+     0x2D,
+     0x32,
+     0x32,
+     0x3C,
+     {0x01, 0x01},
+     {0x02, 0x43, 0x2B, 0x00},
+     GROWTH_MEDIUM_SLOW,
+     GENDER_F25},
+    {"Machamp",
+     0x7E,
+     0x5A,
+     0x82,
+     0x50,
+     0x37,
+     0x41,
+     0x41,
+     0x55,
+     {0x01, 0x01},
+     {0x02, 0x43, 0x2B, 0x00},
+     GROWTH_MEDIUM_SLOW,
+     GENDER_F25},
+    {"Bellsprout",
+     0xBC,
+     0x32,
+     0x4B,
+     0x23,
+     0x28,
+     0x46,
+     0x46,
+     0x1E,
+     {0x16, 0x03},
+     {0x16, 0x4A, 0x00, 0x00},
+     GROWTH_MEDIUM_SLOW,
+     GENDER_F50},
+    {"Weepinbell",
+     0xBD,
+     0x41,
+     0x5A,
+     0x32,
+     0x37,
+     0x55,
+     0x55,
+     0x2D,
+     {0x16, 0x03},
+     {0x16, 0x4A, 0x23, 0x00},
+     GROWTH_MEDIUM_SLOW,
+     GENDER_F50},
+    {"Victreebel",
+     0xBE,
+     0x50,
+     0x69,
+     0x41,
+     0x46,
+     0x64,
+     0x64,
+     0x3C,
+     {0x16, 0x03},
+     {0x4F, 0x4E, 0x33, 0x4B},
+     GROWTH_MEDIUM_SLOW,
+     GENDER_F50},
+    {"Tentacool",
+     0x18,
+     0x28,
+     0x28,
+     0x23,
+     0x46,
+     0x64,
+     0x32,
+     0x64,
+     {0x15, 0x03},
+     {0x33, 0x00, 0x00, 0x00},
+     GROWTH_SLOW,
+     GENDER_F50},
+    {"Tentacruel",
+     0x9B,
+     0x50,
+     0x46,
+     0x41,
+     0x64,
+     0x78,
+     0x50,
+     0x78,
+     {0x15, 0x03},
+     {0x33, 0x30, 0x23, 0x00},
+     GROWTH_SLOW,
+     GENDER_F50},
+    {"Geodude",
+     0xA9,
+     0x28,
+     0x50,
+     0x64,
+     0x14,
+     0x1E,
+     0x1E,
+     0x1E,
+     {0x05, 0x04},
+     {0x21, 0x00, 0x00, 0x00},
+     GROWTH_MEDIUM_SLOW,
+     GENDER_F50},
+    {"Graveler",
+     0x27,
+     0x37,
+     0x5F,
+     0x73,
+     0x23,
+     0x2D,
+     0x2D,
+     0x2D,
+     {0x05, 0x04},
+     {0x21, 0x6F, 0x00, 0x00},
+     GROWTH_MEDIUM_SLOW,
+     GENDER_F50},
+    {"Golem",
+     0x31,
+     0x50,
+     0x6E,
+     0x82,
+     0x2D,
+     0x37,
+     0x37,
+     0x41,
+     {0x05, 0x04},
+     {0x21, 0x6F, 0x00, 0x00},
+     GROWTH_MEDIUM_SLOW,
+     GENDER_F50},
+    {"Ponyta",
+     0xA3,
+     0x32,
+     0x55,
+     0x37,
+     0x5A,
+     0x41,
+     0x41,
+     0x41,
+     {0x14, 0x14},
+     {0x34, 0x00, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Rapidash",
+     0xA4,
+     0x41,
+     0x64,
+     0x46,
+     0x69,
+     0x50,
+     0x50,
+     0x50,
+     {0x14, 0x14},
+     {0x34, 0x27, 0x17, 0x2D},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Slowpoke",
+     0x25,
+     0x5A,
+     0x41,
+     0x41,
+     0x0F,
+     0x28,
+     0x28,
+     0x28,
+     {0x15, 0x18},
+     {0x5D, 0x00, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Slowbro",
+     0x08,
+     0x5F,
+     0x4B,
+     0x6E,
+     0x1E,
+     0x50,
+     0x64,
+     0x50,
+     {0x15, 0x18},
+     {0x5D, 0x32, 0x1D, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Magnemite",
+     0xAD,
+     0x19,
+     0x23,
+     0x46,
+     0x2D,
+     0x5F,
+     0x5F,
+     0x37,
+     {0x17, 0x17},
+     {0x21, 0x00, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_UNKNOWN},
+    {"Magneton",
+     0x36,
+     0x32,
+     0x3C,
+     0x5F,
+     0x46,
+     0x78,
+     0x78,
+     0x46,
+     {0x17, 0x17},
+     {0x21, 0x31, 0x54, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_UNKNOWN},
+    {"Farfetch'd",
+     0x40,
+     0x34,
+     0x41,
+     0x37,
+     0x3C,
+     0x3A,
+     0x3A,
+     0x3E,
+     {0x00, 0x02},
+     {0x40, 0x1C, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Doduo",
+     0x46,
+     0x23,
+     0x55,
+     0x2D,
+     0x4B,
+     0x23,
+     0x23,
+     0x23,
+     {0x00, 0x02},
+     {0x40, 0x00, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Dodrio",
+     0x74,
+     0x3C,
+     0x6E,
+     0x46,
+     0x64,
+     0x3C,
+     0x3C,
+     0x3C,
+     {0x00, 0x02},
+     {0x40, 0x2D, 0x1F, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Seel",
+     0x3A,
+     0x41,
+     0x2D,
+     0x37,
+     0x2D,
+     0x46,
+     0x2D,
+     0x46,
+     {0x15, 0x15},
+     {0x1D, 0x00, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Dewgong",
+     0x78,
+     0x5A,
+     0x46,
+     0x50,
+     0x46,
+     0x5F,
+     0x46,
+     0x5F,
+     {0x15, 0x19},
+     {0x1D, 0x2D, 0x3E, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Grimer",
+     0x0D,
+     0x50,
+     0x50,
+     0x32,
+     0x19,
+     0x28,
+     0x28,
+     0x32,
+     {0x03, 0x03},
+     {0x01, 0x32, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Muk",
+     0x88,
+     0x69,
+     0x69,
+     0x4B,
+     0x32,
+     0x41,
+     0x41,
+     0x64,
+     {0x03, 0x03},
+     {0x01, 0x32, 0x8B, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Shellder",
+     0x17,
+     0x1E,
+     0x41,
+     0x64,
+     0x28,
+     0x2D,
+     0x2D,
+     0x19,
+     {0x15, 0x15},
+     {0x21, 0x6E, 0x00, 0x00},
+     GROWTH_SLOW,
+     GENDER_F50},
+    {"Cloyster",
+     0x8B,
+     0x32,
+     0x5F,
+     0xB4,
+     0x46,
+     0x55,
+     0x55,
+     0x2D,
+     {0x15, 0x19},
+     {0x6E, 0x30, 0x80, 0x3E},
+     GROWTH_SLOW,
+     GENDER_F50},
+    {"Gastly",
+     0x19,
+     0x1E,
+     0x23,
+     0x1E,
+     0x50,
+     0x64,
+     0x64,
+     0x23,
+     {0x08, 0x03},
+     {0x7A, 0x6D, 0x65, 0x00},
+     GROWTH_MEDIUM_SLOW,
+     GENDER_F50},
+    {"Haunter",
+     0x93,
+     0x2D,
+     0x32,
+     0x2D,
+     0x5F,
+     0x73,
+     0x73,
+     0x37,
+     {0x08, 0x03},
+     {0x7A, 0x6D, 0x65, 0x00},
+     GROWTH_MEDIUM_SLOW,
+     GENDER_F50},
+    {"Gengar",
+     0x0E,
+     0x3C,
+     0x41,
+     0x3C,
+     0x6E,
+     0x82,
+     0x82,
+     0x4B,
+     {0x08, 0x03},
+     {0x7A, 0x6D, 0x65, 0x00},
+     GROWTH_MEDIUM_SLOW,
+     GENDER_F50},
+    {"Onix",
+     0x22,
+     0x23,
+     0x2D,
+     0xA0,
+     0x46,
+     0x1E,
+     0x1E,
+     0x2D,
+     {0x05, 0x04},
+     {0x21, 0x67, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Drowzee",
+     0x30,
+     0x3C,
+     0x30,
+     0x2D,
+     0x2A,
+     0x5A,
+     0x2B,
+     0x5A,
+     {0x18, 0x18},
+     {0x01, 0x5F, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Hypno",
+     0x81,
+     0x55,
+     0x49,
+     0x46,
+     0x43,
+     0x73,
+     0x49,
+     0x73,
+     {0x18, 0x18},
+     {0x01, 0x5F, 0x32, 0x5D},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Krabby",
+     0x4E,
+     0x1E,
+     0x69,
+     0x5A,
+     0x32,
+     0x19,
+     0x19,
+     0x19,
+     {0x15, 0x15},
+     {0x91, 0x2B, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Kingler",
+     0x8A,
+     0x37,
+     0x82,
+     0x73,
+     0x4B,
+     0x32,
+     0x32,
+     0x32,
+     {0x15, 0x15},
+     {0x91, 0x2B, 0x0B, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Voltorb",
+     0x06,
+     0x28,
+     0x1E,
+     0x32,
+     0x64,
+     0x37,
+     0x37,
+     0x37,
+     {0x17, 0x17},
+     {0x21, 0x67, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_UNKNOWN},
+    {"Electrode",
+     0x8D,
+     0x3C,
+     0x32,
+     0x46,
+     0x8C,
+     0x50,
+     0x50,
+     0x50,
+     {0x17, 0x17},
+     {0x21, 0x67, 0x31, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_UNKNOWN},
+    {"Exeggcute",
+     0x0C,
+     0x3C,
+     0x28,
+     0x50,
+     0x28,
+     0x3C,
+     0x3C,
+     0x2D,
+     {0x16, 0x18},
+     {0x8C, 0x5F, 0x00, 0x00},
+     GROWTH_SLOW,
+     GENDER_F50},
+    {"Exeggutor",
+     0x0A,
+     0x5F,
+     0x5F,
+     0x55,
+     0x37,
+     0x7D,
+     0x7D,
+     0x41,
+     {0x16, 0x18},
+     {0x8C, 0x5F, 0x00, 0x00},
+     GROWTH_SLOW,
+     GENDER_F50},
+    {"Cubone",
+     0x11,
+     0x32,
+     0x32,
+     0x5F,
+     0x23,
+     0x28,
+     0x28,
+     0x32,
+     {0x04, 0x04},
+     {0x7D, 0x2D, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Marowak",
+     0x91,
+     0x3C,
+     0x50,
+     0x6E,
+     0x2D,
+     0x32,
+     0x32,
+     0x50,
+     {0x04, 0x04},
+     {0x7D, 0x2D, 0x2B, 0x74},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Hitmonlee",
+     0x2B,
+     0x32,
+     0x78,
+     0x35,
+     0x57,
+     0x23,
+     0x23,
+     0x6E,
+     {0x01, 0x01},
+     {0x18, 0x60, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F0},
+    {"Hitmonchan",
+     0x2C,
+     0x32,
+     0x69,
+     0x4F,
+     0x4C,
+     0x23,
+     0x23,
+     0x6E,
+     {0x01, 0x01},
+     {0x04, 0x61, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F0},
+    {"Lickitung",
+     0x0B,
+     0x5A,
+     0x37,
+     0x4B,
+     0x1E,
+     0x3C,
+     0x3C,
+     0x4B,
+     {0x00, 0x00},
+     {0x23, 0x30, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Koffing",
+     0x37,
+     0x28,
+     0x41,
+     0x5F,
+     0x23,
+     0x3C,
+     0x3C,
+     0x2D,
+     {0x03, 0x03},
+     {0x21, 0x7B, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Weezing",
+     0x8F,
+     0x41,
+     0x5A,
+     0x78,
+     0x3C,
+     0x55,
+     0x55,
+     0x46,
+     {0x03, 0x03},
+     {0x21, 0x7B, 0x7C, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Rhyhorn",
+     0x12,
+     0x50,
+     0x55,
+     0x5F,
+     0x19,
+     0x1E,
+     0x1E,
+     0x1E,
+     {0x04, 0x05},
+     {0x1E, 0x00, 0x00, 0x00},
+     GROWTH_SLOW,
+     GENDER_F50},
+    {"Rhydon",
+     0x01,
+     0x69,
+     0x82,
+     0x78,
+     0x28,
+     0x2D,
+     0x2D,
+     0x2D,
+     {0x04, 0x05},
+     {0x1E, 0x17, 0x27, 0x1F},
+     GROWTH_SLOW,
+     GENDER_F50},
+    {"Chansey",
+     0x28,
+     0xFA,
+     0x05,
+     0x05,
+     0x32,
+     0x69,
+     0x23,
+     0x69,
+     {0x00, 0x00},
+     {0x01, 0x03, 0x00, 0x00},
+     GROWTH_FAST,
+     GENDER_F100},
+    {"Tangela",
+     0x1E,
+     0x41,
+     0x37,
+     0x73,
+     0x3C,
+     0x64,
+     0x64,
+     0x28,
+     {0x16, 0x16},
+     {0x84, 0x14, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Kangaskhan",
+     0x02,
+     0x69,
+     0x5F,
+     0x50,
+     0x5A,
+     0x28,
+     0x28,
+     0x50,
+     {0x00, 0x00},
+     {0x04, 0x63, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F100},
+    {"Horsea",
+     0x5C,
+     0x1E,
+     0x28,
+     0x46,
+     0x3C,
+     0x46,
+     0x46,
+     0x19,
+     {0x15, 0x15},
+     {0x91, 0x00, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Seadra",
+     0x5D,
+     0x37,
+     0x41,
+     0x5F,
+     0x55,
+     0x5F,
+     0x5F,
+     0x2D,
+     {0x15, 0x15},
+     {0x91, 0x6C, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Goldeen",
+     0x9D,
+     0x2D,
+     0x43,
+     0x3C,
+     0x3F,
+     0x32,
+     0x23,
+     0x32,
+     {0x15, 0x15},
+     {0x40, 0x27, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Seaking",
+     0x9E,
+     0x50,
+     0x5C,
+     0x41,
+     0x44,
+     0x50,
+     0x41,
+     0x50,
+     {0x15, 0x15},
+     {0x40, 0x27, 0x30, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Staryu",
+     0x1B,
+     0x1E,
+     0x2D,
+     0x37,
+     0x55,
+     0x46,
+     0x46,
+     0x37,
+     {0x15, 0x15},
+     {0x21, 0x00, 0x00, 0x00},
+     GROWTH_SLOW,
+     GENDER_UNKNOWN},
+    {"Starmie",
+     0x98,
+     0x3C,
+     0x4B,
+     0x55,
+     0x73,
+     0x64,
+     0x64,
+     0x55,
+     {0x15, 0x18},
+     {0x21, 0x37, 0x6A, 0x00},
+     GROWTH_SLOW,
+     GENDER_UNKNOWN},
+    {"Mr.Mime",
+     0x2A,
+     0x28,
+     0x2D,
+     0x41,
+     0x5A,
+     0x64,
+     0x64,
+     0x78,
+     {0x18, 0x18},
+     {0x5D, 0x70, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Scyther",
+     0x1A,
+     0x46,
+     0x6E,
+     0x50,
+     0x69,
+     0x37,
+     0x37,
+     0x50,
+     {0x07, 0x02},
+     {0x62, 0x00, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Jynx",
+     0x48,
+     0x41,
+     0x32,
+     0x23,
+     0x5F,
+     0x5F,
+     0x73,
+     0x5F,
+     {0x19, 0x18},
+     {0x01, 0x8E, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F100},
+    {"Electabuzz",
+     0x35,
+     0x41,
+     0x53,
+     0x39,
+     0x69,
+     0x55,
+     0x5F,
+     0x55,
+     {0x17, 0x17},
+     {0x62, 0x2B, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F25},
+    {"Magmar",
+     0x33,
+     0x41,
+     0x5F,
+     0x39,
+     0x5D,
+     0x55,
+     0x64,
+     0x55,
+     {0x14, 0x14},
+     {0x34, 0x00, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F25},
+    {"Pinsir",
+     0x1D,
+     0x41,
+     0x7D,
+     0x64,
+     0x55,
+     0x37,
+     0x37,
+     0x46,
+     {0x07, 0x07},
+     {0x0B, 0x00, 0x00, 0x00},
+     GROWTH_SLOW,
+     GENDER_F50},
+    {"Tauros",
+     0x3C,
+     0x4B,
+     0x64,
+     0x5F,
+     0x6E,
+     0x46,
+     0x28,
+     0x46,
+     {0x00, 0x00},
+     {0x21, 0x00, 0x00, 0x00},
+     GROWTH_SLOW,
+     GENDER_F0},
+    {"Magikarp",
+     0x85,
+     0x14,
+     0x0A,
+     0x37,
+     0x50,
+     0x14,
+     0x0F,
+     0x14,
+     {0x15, 0x15},
+     {0x96, 0x00, 0x00, 0x00},
+     GROWTH_SLOW,
+     GENDER_F50},
+    {"Gyarados",
+     0x16,
+     0x5F,
+     0x7D,
+     0x4F,
+     0x51,
+     0x64,
+     0x3C,
+     0x64,
+     {0x15, 0x02},
+     {0x2C, 0x52, 0x2B, 0x38},
+     GROWTH_SLOW,
+     GENDER_F50},
+    {"Lapras",
+     0x13,
+     0x82,
+     0x55,
+     0x50,
+     0x3C,
+     0x5F,
+     0x55,
+     0x5F,
+     {0x15, 0x19},
+     {0x37, 0x2D, 0x00, 0x00},
+     GROWTH_SLOW,
+     GENDER_F50},
+    {"Ditto",
+     0x4C,
+     0x30,
+     0x30,
+     0x30,
+     0x30,
+     0x30,
+     0x30,
+     0x30,
+     {0x00, 0x00},
+     {0x90, 0x00, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_UNKNOWN},
+    {"Eevee",
+     0x66,
+     0x37,
+     0x37,
+     0x32,
+     0x37,
+     0x41,
+     0x2D,
+     0x41,
+     {0x00, 0x00},
+     {0x21, 0x1C, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F12_5},
+    {"Vaporeon",
+     0x69,
+     0x82,
+     0x41,
+     0x3C,
+     0x41,
+     0x6E,
+     0x6E,
+     0x5F,
+     {0x15, 0x15},
+     {0x21, 0x1C, 0x62, 0x37},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F12_5},
+    {"Jolteon",
+     0x68,
+     0x41,
+     0x41,
+     0x3C,
+     0x82,
+     0x6E,
+     0x6E,
+     0x5F,
+     {0x17, 0x17},
+     {0x21, 0x1C, 0x62, 0x54},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F12_5},
+    {"Flareon",
+     0x67,
+     0x41,
+     0x82,
+     0x3C,
+     0x41,
+     0x6E,
+     0x5F,
+     0x6E,
+     {0x14, 0x14},
+     {0x21, 0x1C, 0x62, 0x34},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F12_5},
+    {"Porygon",
+     0xAA,
+     0x41,
+     0x3C,
+     0x46,
+     0x28,
+     0x4B,
+     0x55,
+     0x4B,
+     {0x00, 0x00},
+     {0x21, 0x9F, 0xA0, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_UNKNOWN},
+    {"Omanyte",
+     0x62,
+     0x23,
+     0x28,
+     0x64,
+     0x23,
+     0x5A,
+     0x5A,
+     0x37,
+     {0x05, 0x15},
+     {0x37, 0x6E, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F12_5},
+    {"Omastar",
+     0x63,
+     0x46,
+     0x3C,
+     0x7D,
+     0x37,
+     0x73,
+     0x73,
+     0x46,
+     {0x05, 0x15},
+     {0x37, 0x6E, 0x1E, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F12_5},
+    {"Kabuto",
+     0x5A,
+     0x1E,
+     0x50,
+     0x5A,
+     0x37,
+     0x2D,
+     0x37,
+     0x2D,
+     {0x05, 0x15},
+     {0x0A, 0x6A, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F12_5},
+    {"Kabutops",
+     0x5B,
+     0x3C,
+     0x73,
+     0x69,
+     0x50,
+     0x46,
+     0x41,
+     0x46,
+     {0x05, 0x15},
+     {0x0A, 0x6A, 0x47, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F12_5},
+    {"Aerodactyl",
+     0xAB,
+     0x50,
+     0x69,
+     0x41,
+     0x82,
+     0x3C,
+     0x3C,
+     0x4B,
+     {0x05, 0x02},
+     {0x11, 0x61, 0x00, 0x00},
+     GROWTH_SLOW,
+     GENDER_F12_5},
+    {"Snorlax",
+     0x84,
+     0xA0,
+     0x6E,
+     0x41,
+     0x1E,
+     0x41,
+     0x41,
+     0x6E,
+     {0x00, 0x00},
+     {0x1D, 0x85, 0x9C, 0x00},
+     GROWTH_SLOW,
+     GENDER_F12_5},
+    {"Articuno",
+     0x4A,
+     0x5A,
+     0x55,
+     0x64,
+     0x55,
+     0x7D,
+     0x5F,
+     0x7D,
+     {0x19, 0x02},
+     {0x40, 0x3A, 0x00, 0x00},
+     GROWTH_SLOW,
+     GENDER_UNKNOWN},
+    {"Zapdos",
+     0x4B,
+     0x5A,
+     0x5A,
+     0x55,
+     0x64,
+     0x7D,
+     0x7D,
+     0x5A,
+     {0x17, 0x02},
+     {0x54, 0x41, 0x00, 0x00},
+     GROWTH_SLOW,
+     GENDER_UNKNOWN},
+    {"Moltres",
+     0x49,
+     0x5A,
+     0x64,
+     0x5A,
+     0x5A,
+     0x7D,
+     0x7D,
+     0x55,
+     {0x14, 0x02},
+     {0x40, 0x53, 0x00, 0x00},
+     GROWTH_SLOW,
+     GENDER_UNKNOWN},
+    {"Dratini",
+     0x58,
+     0x29,
+     0x40,
+     0x2D,
+     0x32,
+     0x32,
+     0x32,
+     0x32,
+     {0x1A, 0x1A},
+     {0x23, 0x2B, 0x00, 0x00},
+     GROWTH_SLOW,
+     GENDER_F50},
+    {"Dragonair",
+     0x59,
+     0x3D,
+     0x54,
+     0x41,
+     0x46,
+     0x46,
+     0x46,
+     0x46,
+     {0x1A, 0x1A},
+     {0x23, 0x2B, 0x56, 0x00},
+     GROWTH_SLOW,
+     GENDER_F50},
+    {"Dragonite",
+     0x42,
+     0x5B,
+     0x86,
+     0x5F,
+     0x50,
+     0x64,
+     0x64,
+     0x64,
+     {0x1A, 0x02},
+     {0x23, 0x2B, 0x56, 0x61},
+     GROWTH_SLOW,
+     GENDER_F50},
+    {"Mewtwo",
+     0x83,
+     0x6A,
+     0x6E,
+     0x5A,
+     0x82,
+     0x9A,
+     0x9A,
+     0x5A,
+     {0x18, 0x18},
+     {0x5D, 0x32, 0x81, 0x5E},
+     GROWTH_SLOW,
+     GENDER_UNKNOWN},
+    {"Mew",
+     0x15,
+     0x64,
+     0x64,
+     0x64,
+     0x64,
+     0x64,
+     0x64,
+     0x64,
+     {0x18, 0x18},
+     {0x01, 0x00, 0x00, 0x00},
+     GROWTH_MEDIUM_SLOW,
+     GENDER_UNKNOWN},
+
+    /* Valueed from Gold */
+    {"Chikorita",
+     0x00,
+     0x2D,
+     0x31,
+     0x41,
+     0x2D,
+     0x00,
+     0x31,
+     0x41,
+     {0x00, 0x00},
+     {0x21, 0x2D, 0x00, 0x00},
+     GROWTH_MEDIUM_SLOW,
+     GENDER_F12_5},
+    {"Bayleef",
+     0x00,
+     0x3C,
+     0x3E,
+     0x50,
+     0x3C,
+     0x00,
+     0x3F,
+     0x50,
+     {0x00, 0x00},
+     {0x21, 0x2D, 0x4B, 0x73},
+     GROWTH_MEDIUM_SLOW,
+     GENDER_F12_5},
+    {"Meganium",
+     0x00,
+     0x50,
+     0x52,
+     0x64,
+     0x50,
+     0x00,
+     0x53,
+     0x64,
+     {0x00, 0x00},
+     {0x21, 0x2D, 0x4B, 0x73},
+     GROWTH_MEDIUM_SLOW,
+     GENDER_F12_5},
+    {"Cyndaquil",
+     0x00,
+     0x27,
+     0x34,
+     0x2B,
+     0x41,
+     0x00,
+     0x3C,
+     0x32,
+     {0x00, 0x00},
+     {0x21, 0x2B, 0x00, 0x00},
+     GROWTH_MEDIUM_SLOW,
+     GENDER_F12_5},
+    {"Quilava",
+     0x00,
+     0x3A,
+     0x40,
+     0x3A,
+     0x50,
+     0x00,
+     0x50,
+     0x41,
+     {0x00, 0x00},
+     {0x21, 0x2B, 0x6C, 0x00},
+     GROWTH_MEDIUM_SLOW,
+     GENDER_F12_5},
+    {"Typhlosion",
+     0x00,
+     0x4E,
+     0x54,
+     0x4E,
+     0x64,
+     0x00,
+     0x6D,
+     0x55,
+     {0x00, 0x00},
+     {0x21, 0x2B, 0x6C, 0x34},
+     GROWTH_MEDIUM_SLOW,
+     GENDER_F12_5},
+    {"Totodile",
+     0x00,
+     0x32,
+     0x41,
+     0x40,
+     0x2B,
+     0x00,
+     0x2C,
+     0x30,
+     {0x00, 0x00},
+     {0x0A, 0x2B, 0x00, 0x00},
+     GROWTH_MEDIUM_SLOW,
+     GENDER_F12_5},
+    {"Croconaw",
+     0x00,
+     0x41,
+     0x50,
+     0x50,
+     0x3A,
+     0x00,
+     0x3B,
+     0x3F,
+     {0x00, 0x00},
+     {0x0A, 0x2B, 0x63, 0x00},
+     GROWTH_MEDIUM_SLOW,
+     GENDER_F12_5},
+    {"Feraligatr",
+     0x00,
+     0x55,
+     0x69,
+     0x64,
+     0x4E,
+     0x00,
+     0x4F,
+     0x53,
+     {0x00, 0x00},
+     {0x0A, 0x2B, 0x63, 0x37},
+     GROWTH_MEDIUM_SLOW,
+     GENDER_F12_5},
+    {"Sentret",
+     0x00,
+     0x23,
+     0x2E,
+     0x22,
+     0x14,
+     0x00,
+     0x23,
+     0x2D,
+     {0x00, 0x00},
+     {0x21, 0x00, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Furret",
+     0x00,
+     0x55,
+     0x4C,
+     0x40,
+     0x5A,
+     0x00,
+     0x2D,
+     0x37,
+     {0x00, 0x00},
+     {0x0A, 0x6F, 0x62, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Hoothoot",
+     0x00,
+     0x3C,
+     0x1E,
+     0x1E,
+     0x32,
+     0x00,
+     0x24,
+     0x38,
+     {0x00, 0x00},
+     {0x21, 0x2D, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Noctowl",
+     0x00,
+     0x64,
+     0x32,
+     0x32,
+     0x46,
+     0x00,
+     0x4C,
+     0x60,
+     {0x00, 0x00},
+     {0x21, 0x2D, 0xC1, 0x40},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Ledyba",
+     0x00,
+     0x28,
+     0x14,
+     0x1E,
+     0x37,
+     0x00,
+     0x28,
+     0x50,
+     {0x00, 0x00},
+     {0x21, 0x00, 0x00, 0x00},
+     GROWTH_FAST,
+     GENDER_F50},
+    {"Ledian",
+     0x00,
+     0x37,
+     0x23,
+     0x32,
+     0x55,
+     0x00,
+     0x37,
+     0x6E,
+     {0x00, 0x00},
+     {0x21, 0x30, 0x00, 0x00},
+     GROWTH_FAST,
+     GENDER_F50},
+    {"Spinarak",
+     0x00,
+     0x28,
+     0x3C,
+     0x28,
+     0x1E,
+     0x00,
+     0x28,
+     0x28,
+     {0x00, 0x00},
+     {0x28, 0x51, 0x00, 0x00},
+     GROWTH_FAST,
+     GENDER_F50},
+    {"Ariados",
+     0x00,
+     0x46,
+     0x5A,
+     0x46,
+     0x28,
+     0x00,
+     0x3C,
+     0x3C,
+     {0x00, 0x00},
+     {0x28, 0x51, 0xB8, 0x84},
+     GROWTH_FAST,
+     GENDER_F50},
+    {"Crobat",
+     0x00,
+     0x55,
+     0x5A,
+     0x50,
+     0x82,
+     0x00,
+     0x46,
+     0x50,
+     {0x00, 0x00},
+     {0x67, 0x8D, 0x30, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Chinchou",
+     0x00,
+     0x4B,
+     0x26,
+     0x26,
+     0x43,
+     0x00,
+     0x38,
+     0x38,
+     {0x00, 0x00},
+     {0x91, 0x56, 0x00, 0x00},
+     GROWTH_SLOW,
+     GENDER_F50},
+    {"Lanturn",
+     0x00,
+     0x7D,
+     0x3A,
+     0x3A,
+     0x43,
+     0x00,
+     0x4C,
+     0x4C,
+     {0x00, 0x00},
+     {0x91, 0x56, 0x30, 0x00},
+     GROWTH_SLOW,
+     GENDER_F50},
+    {"Pichu",
+     0x00,
+     0x14,
+     0x28,
+     0x0F,
+     0x3C,
+     0x00,
+     0x23,
+     0x23,
+     {0x00, 0x00},
+     {0x54, 0xCC, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Cleffa",
+     0x00,
+     0x32,
+     0x19,
+     0x1C,
+     0x0F,
+     0x00,
+     0x2D,
+     0x37,
+     {0x00, 0x00},
+     {0x01, 0xCC, 0x00, 0x00},
+     GROWTH_FAST,
+     GENDER_F75},
+    {"Igglybuff",
+     0x00,
+     0x5A,
+     0x1E,
+     0x0F,
+     0x0F,
+     0x00,
+     0x28,
+     0x14,
+     {0x00, 0x00},
+     {0x2F, 0xCC, 0x0E, 0x00},
+     GROWTH_FAST,
+     GENDER_F75},
+    {"Togepi",
+     0x00,
+     0x23,
+     0x14,
+     0x41,
+     0x14,
+     0x00,
+     0x28,
+     0x41,
+     {0x00, 0x00},
+     {0x2D, 0xCC, 0x00, 0x00},
+     GROWTH_FAST,
+     GENDER_F12_5},
+    {"Togetic",
+     0x00,
+     0x37,
+     0x28,
+     0x55,
+     0x28,
+     0x00,
+     0x50,
+     0x69,
+     {0x00, 0x00},
+     {0x2D, 0xCC, 0x00, 0x00},
+     GROWTH_FAST,
+     GENDER_F12_5},
+    {"Natu",
+     0x00,
+     0x28,
+     0x32,
+     0x2D,
+     0x46,
+     0x00,
+     0x46,
+     0x2D,
+     {0x00, 0x00},
+     {0x40, 0x2B, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Xatu",
+     0x00,
+     0x41,
+     0x4B,
+     0x46,
+     0x5F,
+     0x00,
+     0x5F,
+     0x46,
+     {0x00, 0x00},
+     {0x40, 0x2B, 0x65, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Mareep",
+     0x00,
+     0x37,
+     0x28,
+     0x28,
+     0x23,
+     0x00,
+     0x41,
+     0x2D,
+     {0x00, 0x00},
+     {0x21, 0x2D, 0x00, 0x00},
+     GROWTH_MEDIUM_SLOW,
+     GENDER_F50},
+    {"Flaaffy",
+     0x00,
+     0x46,
+     0x37,
+     0x37,
+     0x2D,
+     0x00,
+     0x50,
+     0x3C,
+     {0x00, 0x00},
+     {0x21, 0x2D, 0x54, 0x00},
+     GROWTH_MEDIUM_SLOW,
+     GENDER_F50},
+    {"Ampharos",
+     0x00,
+     0x5A,
+     0x4B,
+     0x4B,
+     0x37,
+     0x00,
+     0x73,
+     0x5A,
+     {0x00, 0x00},
+     {0x21, 0x2D, 0x54, 0x56},
+     GROWTH_MEDIUM_SLOW,
+     GENDER_F50},
+    {"Bellossom",
+     0x00,
+     0x4B,
+     0x50,
+     0x55,
+     0x32,
+     0x00,
+     0x5A,
+     0x64,
+     {0x00, 0x00},
+     {0x47, 0xE6, 0x4E, 0x50},
+     GROWTH_MEDIUM_SLOW,
+     GENDER_F50},
+    {"Marill",
+     0x00,
+     0x46,
+     0x14,
+     0x32,
+     0x28,
+     0x00,
+     0x14,
+     0x32,
+     {0x00, 0x00},
+     {0x21, 0x00, 0x00, 0x00},
+     GROWTH_FAST,
+     GENDER_F50},
+    {"Azumarill",
+     0x00,
+     0x64,
+     0x32,
+     0x50,
+     0x32,
+     0x00,
+     0x32,
+     0x50,
+     {0x00, 0x00},
+     {0x21, 0x6F, 0x27, 0x37},
+     GROWTH_FAST,
+     GENDER_F50},
+    {"Sudowoodo",
+     0x00,
+     0x46,
+     0x64,
+     0x73,
+     0x1E,
+     0x00,
+     0x1E,
+     0x41,
+     {0x00, 0x00},
+     {0x58, 0x66, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Politoed",
+     0x00,
+     0x5A,
+     0x4B,
+     0x4B,
+     0x46,
+     0x00,
+     0x5A,
+     0x64,
+     {0x00, 0x00},
+     {0x37, 0x5F, 0x03, 0xC3},
+     GROWTH_MEDIUM_SLOW,
+     GENDER_F50},
+    {"Hoppip",
+     0x00,
+     0x23,
+     0x23,
+     0x28,
+     0x32,
+     0x00,
+     0x23,
+     0x37,
+     {0x00, 0x00},
+     {0x96, 0xEB, 0x00, 0x00},
+     GROWTH_MEDIUM_SLOW,
+     GENDER_F50},
+    {"Skiploom",
+     0x00,
+     0x37,
+     0x2D,
+     0x32,
+     0x50,
+     0x00,
+     0x2D,
+     0x41,
+     {0x00, 0x00},
+     {0x96, 0xEB, 0x27, 0x21},
+     GROWTH_MEDIUM_SLOW,
+     GENDER_F50},
+    {"Jumpluff",
+     0x00,
+     0x4B,
+     0x37,
+     0x46,
+     0x6E,
+     0x00,
+     0x37,
+     0x55,
+     {0x00, 0x00},
+     {0x96, 0xEB, 0x27, 0x21},
+     GROWTH_MEDIUM_SLOW,
+     GENDER_F50},
+    {"Aipom",
+     0x00,
+     0x37,
+     0x46,
+     0x37,
+     0x55,
+     0x00,
+     0x28,
+     0x37,
+     {0x00, 0x00},
+     {0x0A, 0x27, 0x00, 0x00},
+     GROWTH_FAST,
+     GENDER_F50},
+    {"Sunkern",
+     0x00,
+     0x1E,
+     0x1E,
+     0x1E,
+     0x1E,
+     0x00,
+     0x1E,
+     0x1E,
+     {0x00, 0x00},
+     {0x47, 0x00, 0x00, 0x00},
+     GROWTH_MEDIUM_SLOW,
+     GENDER_F50},
+    {"Sunflora",
+     0x00,
+     0x4B,
+     0x4B,
+     0x37,
+     0x1E,
+     0x00,
+     0x69,
+     0x55,
+     {0x00, 0x00},
+     {0x47, 0x01, 0x00, 0x00},
+     GROWTH_MEDIUM_SLOW,
+     GENDER_F50},
+    {"Yanma",
+     0x00,
+     0x41,
+     0x41,
+     0x2D,
+     0x5F,
+     0x00,
+     0x4B,
+     0x2D,
+     {0x00, 0x00},
+     {0x21, 0xC1, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Wooper",
+     0x00,
+     0x37,
+     0x2D,
+     0x2D,
+     0x0F,
+     0x00,
+     0x19,
+     0x19,
+     {0x00, 0x00},
+     {0x37, 0x27, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Quagsire",
+     0x00,
+     0x5F,
+     0x55,
+     0x55,
+     0x23,
+     0x00,
+     0x41,
+     0x41,
+     {0x00, 0x00},
+     {0x37, 0x27, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Espeon",
+     0x00,
+     0x41,
+     0x41,
+     0x3C,
+     0x6E,
+     0x00,
+     0x82,
+     0x5F,
+     {0x00, 0x00},
+     {0x21, 0x27, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F12_5},
+    {"Umbreon",
+     0x00,
+     0x5F,
+     0x41,
+     0x6E,
+     0x41,
+     0x00,
+     0x3C,
+     0x82,
+     {0x00, 0x00},
+     {0x21, 0x27, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F12_5},
+    {"Murkrow",
+     0x00,
+     0x3C,
+     0x55,
+     0x2A,
+     0x5B,
+     0x00,
+     0x55,
+     0x2A,
+     {0x00, 0x00},
+     {0x40, 0x00, 0x00, 0x00},
+     GROWTH_MEDIUM_SLOW,
+     GENDER_F50},
+    {"Slowking",
+     0x00,
+     0x5F,
+     0x4B,
+     0x50,
+     0x1E,
+     0x00,
+     0x64,
+     0x6E,
+     {0x00, 0x00},
+     {0xAE, 0x21, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Misdreavus",
+     0x00,
+     0x3C,
+     0x3C,
+     0x3C,
+     0x55,
+     0x00,
+     0x55,
+     0x55,
+     {0x00, 0x00},
+     {0x2D, 0x95, 0x00, 0x00},
+     GROWTH_FAST,
+     GENDER_F50},
+    {"Unown",
+     0x00,
+     0x30,
+     0x48,
+     0x30,
+     0x30,
+     0x00,
+     0x48,
+     0x30,
+     {0x00, 0x00},
+     {0xED, 0x00, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_UNKNOWN},
+    {"Wobbuffet",
+     0x00,
+     0xBE,
+     0x21,
+     0x3A,
+     0x21,
+     0x00,
+     0x21,
+     0x3A,
+     {0x00, 0x00},
+     {0x44, 0xF3, 0xDB, 0xC2},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Girafarig",
+     0x00,
+     0x46,
+     0x50,
+     0x41,
+     0x55,
+     0x00,
+     0x5A,
+     0x41,
+     {0x00, 0x00},
+     {0x21, 0x2D, 0x5D, 0x17},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Pineco",
+     0x00,
+     0x32,
+     0x41,
+     0x5A,
+     0x0F,
+     0x00,
+     0x23,
+     0x23,
+     {0x00, 0x00},
+     {0x21, 0xB6, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Forretress",
+     0x00,
+     0x4B,
+     0x5A,
+     0x8C,
+     0x28,
+     0x00,
+     0x3C,
+     0x3C,
+     {0x00, 0x00},
+     {0x21, 0xB6, 0x78, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Dunsparce",
+     0x00,
+     0x64,
+     0x46,
+     0x46,
+     0x2D,
+     0x00,
+     0x41,
+     0x41,
+     {0x00, 0x00},
+     {0x63, 0x00, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Gligar",
+     0x00,
+     0x41,
+     0x4B,
+     0x69,
+     0x55,
+     0x00,
+     0x23,
+     0x41,
+     {0x00, 0x00},
+     {0x28, 0x00, 0x00, 0x00},
+     GROWTH_MEDIUM_SLOW,
+     GENDER_F50},
+    {"Steelix",
+     0x00,
+     0x4B,
+     0x55,
+     0xC8,
+     0x1E,
+     0x00,
+     0x37,
+     0x41,
+     {0x00, 0x00},
+     {0x21, 0x67, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Snubbull",
+     0x00,
+     0x3C,
+     0x50,
+     0x32,
+     0x1E,
+     0x00,
+     0x28,
+     0x28,
+     {0x00, 0x00},
+     {0x21, 0xB8, 0x00, 0x00},
+     GROWTH_FAST,
+     GENDER_F75},
+    {"Granbull",
+     0x00,
+     0x5A,
+     0x78,
+     0x4B,
+     0x2D,
+     0x00,
+     0x3C,
+     0x3C,
+     {0x00, 0x00},
+     {0x21, 0xB8, 0x00, 0x00},
+     GROWTH_FAST,
+     GENDER_F75},
+    {"Qwilfish",
+     0x00,
+     0x41,
+     0x5F,
+     0x4B,
+     0x55,
+     0x00,
+     0x37,
+     0x37,
+     {0x00, 0x00},
+     {0x21, 0x28, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Scizor",
+     0x00,
+     0x46,
+     0x82,
+     0x64,
+     0x41,
+     0x00,
+     0x37,
+     0x50,
+     {0x00, 0x00},
+     {0x62, 0x2B, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Shuckle",
+     0x00,
+     0x14,
+     0x0A,
+     0xE6,
+     0x05,
+     0x00,
+     0x0A,
+     0xE6,
+     {0x00, 0x00},
+     {0x84, 0x6E, 0x00, 0x00},
+     GROWTH_MEDIUM_SLOW,
+     GENDER_F50},
+    {"Heracross",
+     0x00,
+     0x50,
+     0x7D,
+     0x4B,
+     0x55,
+     0x00,
+     0x28,
+     0x5F,
+     {0x00, 0x00},
+     {0x21, 0x2B, 0x00, 0x00},
+     GROWTH_SLOW,
+     GENDER_F50},
+    {"Sneasel",
+     0x00,
+     0x37,
+     0x5F,
+     0x37,
+     0x73,
+     0x00,
+     0x23,
+     0x4B,
+     {0x00, 0x00},
+     {0x0A, 0x2B, 0x00, 0x00},
+     GROWTH_MEDIUM_SLOW,
+     GENDER_F50},
+    {"Teddiursa",
+     0x00,
+     0x3C,
+     0x50,
+     0x32,
+     0x28,
+     0x00,
+     0x32,
+     0x32,
+     {0x00, 0x00},
+     {0x0A, 0x2B, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Ursaring",
+     0x00,
+     0x5A,
+     0x82,
+     0x4B,
+     0x37,
+     0x00,
+     0x4B,
+     0x4B,
+     {0x00, 0x00},
+     {0x0A, 0x2B, 0x7A, 0x9A},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Slugma",
+     0x00,
+     0x28,
+     0x28,
+     0x28,
+     0x14,
+     0x00,
+     0x46,
+     0x28,
+     {0x00, 0x00},
+     {0x7B, 0x00, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Magcargo",
+     0x00,
+     0x32,
+     0x32,
+     0x78,
+     0x1E,
+     0x00,
+     0x50,
+     0x50,
+     {0x00, 0x00},
+     {0x7B, 0x34, 0x58, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Swinub",
+     0x00,
+     0x32,
+     0x32,
+     0x28,
+     0x32,
+     0x00,
+     0x1E,
+     0x1E,
+     {0x00, 0x00},
+     {0x21, 0x00, 0x00, 0x00},
+     GROWTH_SLOW,
+     GENDER_F50},
+    {"Piloswine",
+     0x00,
+     0x64,
+     0x64,
+     0x50,
+     0x32,
+     0x00,
+     0x3C,
+     0x3C,
+     {0x00, 0x00},
+     {0x1E, 0xB5, 0xCB, 0x00},
+     GROWTH_SLOW,
+     GENDER_F50},
+    {"Corsola",
+     0x00,
+     0x37,
+     0x37,
+     0x55,
+     0x23,
+     0x00,
+     0x41,
+     0x55,
+     {0x00, 0x00},
+     {0x21, 0x00, 0x00, 0x00},
+     GROWTH_FAST,
+     GENDER_F75},
+    {"Remoraid",
+     0x00,
+     0x23,
+     0x41,
+     0x23,
+     0x41,
+     0x00,
+     0x41,
+     0x23,
+     {0x00, 0x00},
+     {0x37, 0x00, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Octillery",
+     0x00,
+     0x4B,
+     0x69,
+     0x4B,
+     0x2D,
+     0x00,
+     0x69,
+     0x4B,
+     {0x00, 0x00},
+     {0x37, 0x00, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Delibird",
+     0x00,
+     0x2D,
+     0x37,
+     0x2D,
+     0x4B,
+     0x00,
+     0x41,
+     0x2D,
+     {0x00, 0x00},
+     {0xD9, 0x00, 0x00, 0x00},
+     GROWTH_FAST,
+     GENDER_F50},
+    {"Mantine",
+     0x00,
+     0x41,
+     0x28,
+     0x46,
+     0x46,
+     0x00,
+     0x50,
+     0x8C,
+     {0x00, 0x00},
+     {0x21, 0x91, 0x00, 0x00},
+     GROWTH_SLOW,
+     GENDER_F50},
+    {"Skarmory",
+     0x00,
+     0x41,
+     0x50,
+     0x8C,
+     0x46,
+     0x00,
+     0x28,
+     0x46,
+     {0x00, 0x00},
+     {0x2B, 0x40, 0x00, 0x00},
+     GROWTH_SLOW,
+     GENDER_F50},
+    {"Houndour",
+     0x00,
+     0x2D,
+     0x3C,
+     0x1E,
+     0x41,
+     0x00,
+     0x50,
+     0x32,
+     {0x00, 0x00},
+     {0x2B, 0x34, 0x00, 0x00},
+     GROWTH_SLOW,
+     GENDER_F50},
+    {"Houndoom",
+     0x00,
+     0x4B,
+     0x5A,
+     0x32,
+     0x5F,
+     0x00,
+     0x6E,
+     0x50,
+     {0x00, 0x00},
+     {0x2B, 0x34, 0x00, 0x00},
+     GROWTH_SLOW,
+     GENDER_F50},
+    {"Kingdra",
+     0x00,
+     0x4B,
+     0x5F,
+     0x5F,
+     0x55,
+     0x00,
+     0x5F,
+     0x5F,
+     {0x00, 0x00},
+     {0x91, 0x6C, 0x2B, 0x37},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Phanpy",
+     0x00,
+     0x5A,
+     0x3C,
+     0x3C,
+     0x28,
+     0x00,
+     0x28,
+     0x28,
+     {0x00, 0x00},
+     {0x21, 0x2D, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Donphan",
+     0x00,
+     0x5A,
+     0x78,
+     0x78,
+     0x32,
+     0x00,
+     0x3C,
+     0x3C,
+     {0x00, 0x00},
+     {0x1E, 0x2D, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F50},
+    {"Porygon2",
+     0x00,
+     0x55,
+     0x50,
+     0x5A,
+     0x3C,
+     0x00,
+     0x69,
+     0x5F,
+     {0x00, 0x00},
+     {0xB0, 0x21, 0xA0, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_UNKNOWN},
+    {"Stantler",
+     0x00,
+     0x49,
+     0x5F,
+     0x3E,
+     0x55,
+     0x00,
+     0x55,
+     0x41,
+     {0x00, 0x00},
+     {0x21, 0x00, 0x00, 0x00},
+     GROWTH_SLOW,
+     GENDER_F50},
+    {"Smeargle",
+     0x00,
+     0x37,
+     0x14,
+     0x23,
+     0x4B,
+     0x00,
+     0x14,
+     0x2D,
+     {0x00, 0x00},
+     {0xA6, 0x00, 0x00, 0x00},
+     GROWTH_FAST,
+     GENDER_F50},
+    {"Tyrogue",
+     0x00,
+     0x23,
+     0x23,
+     0x23,
+     0x23,
+     0x00,
+     0x23,
+     0x23,
+     {0x00, 0x00},
+     {0x21, 0x00, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F0},
+    {"Hitmontop",
+     0x00,
+     0x32,
+     0x5F,
+     0x5F,
+     0x46,
+     0x00,
+     0x23,
+     0x6E,
+     {0x00, 0x00},
+     {0x1B, 0x00, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F0},
+    {"Smoochum",
+     0x00,
+     0x2D,
+     0x1E,
+     0x0F,
+     0x41,
+     0x00,
+     0x55,
+     0x41,
+     {0x00, 0x00},
+     {0x01, 0x7A, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F100},
+    {"Elekid",
+     0x00,
+     0x2D,
+     0x3F,
+     0x25,
+     0x5F,
+     0x00,
+     0x41,
+     0x37,
+     {0x00, 0x00},
+     {0x62, 0x2B, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F25},
+    {"Magby",
+     0x00,
+     0x2D,
+     0x4B,
+     0x25,
+     0x53,
+     0x00,
+     0x46,
+     0x37,
+     {0x00, 0x00},
+     {0x34, 0x00, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST,
+     GENDER_F25},
+    {"Miltank",
+     0x00,
+     0x5F,
+     0x50,
+     0x69,
+     0x64,
+     0x00,
+     0x28,
+     0x46,
+     {0x00, 0x00},
+     {0x21, 0x00, 0x00, 0x00},
+     GROWTH_SLOW,
+     GENDER_F100},
+    {"Blissey",
+     0x00,
+     0xFF,
+     0x0A,
+     0x0A,
+     0x37,
+     0x00,
+     0x4B,
+     0x87,
+     {0x00, 0x00},
+     {0x01, 0x00, 0x00, 0x00},
+     GROWTH_FAST,
+     GENDER_F100},
+    {"Raikou",
+     0x00,
+     0x5A,
+     0x55,
+     0x4B,
+     0x73,
+     0x00,
+     0x73,
+     0x64,
+     {0x00, 0x00},
+     {0x2C, 0x2B, 0x00, 0x00},
+     GROWTH_SLOW,
+     GENDER_UNKNOWN},
+    {"Entei",
+     0x00,
+     0x73,
+     0x73,
+     0x55,
+     0x64,
+     0x00,
+     0x5A,
+     0x4B,
+     {0x00, 0x00},
+     {0x2C, 0x2B, 0x00, 0x00},
+     GROWTH_SLOW,
+     GENDER_UNKNOWN},
+    {"Suicune",
+     0x00,
+     0x64,
+     0x4B,
+     0x73,
+     0x55,
+     0x00,
+     0x5A,
+     0x73,
+     {0x00, 0x00},
+     {0x2C, 0x2B, 0x00, 0x00},
+     GROWTH_SLOW,
+     GENDER_UNKNOWN},
+    {"Larvitar",
+     0x00,
+     0x32,
+     0x40,
+     0x32,
+     0x29,
+     0x00,
+     0x2D,
+     0x32,
+     {0x00, 0x00},
+     {0x2C, 0x2B, 0x00, 0x00},
+     GROWTH_SLOW,
+     GENDER_F50},
+    {"Pupitar",
+     0x00,
+     0x46,
+     0x54,
+     0x46,
+     0x33,
+     0x00,
+     0x41,
+     0x46,
+     {0x00, 0x00},
+     {0x2C, 0x2B, 0xC9, 0x67},
+     GROWTH_SLOW,
+     GENDER_F50},
+    {"Tyranitar",
+     0x00,
+     0x64,
+     0x86,
+     0x6E,
+     0x3D,
+     0x00,
+     0x5F,
+     0x64,
+     {0x00, 0x00},
+     {0x2C, 0x2B, 0xC9, 0x67},
+     GROWTH_SLOW,
+     GENDER_F50},
+    {"Lugia",
+     0x00,
+     0x6A,
+     0x5A,
+     0x82,
+     0x6E,
+     0x00,
+     0x5A,
+     0x9A,
+     {0x00, 0x00},
+     {0xB1, 0x00, 0x00, 0x00},
+     GROWTH_SLOW,
+     GENDER_UNKNOWN},
+    {"Ho-Oh",
+     0x00,
+     0x6A,
+     0x82,
+     0x5A,
+     0x5A,
+     0x00,
+     0x6E,
+     0x9A,
+     {0x00, 0x00},
+     {0xDD, 0x00, 0x00, 0x00},
+     GROWTH_SLOW,
+     GENDER_UNKNOWN},
+    {"Celebi",
+     0x00,
+     0x64,
+     0x64,
+     0x64,
+     0x64,
+     0x00,
+     0x64,
+     0x64,
+     {0x00, 0x00},
+     {0x49, 0x5D, 0x69, 0xD7},
+     GROWTH_MEDIUM_SLOW,
+     GENDER_UNKNOWN},
+    {},
+};
+
+const PokemonTable* table_pointer_get() {
+    return pokemon_table;
+}

+ 33 - 0
pokemon_table.h

@@ -0,0 +1,33 @@
+#ifndef __POKEMON_TABLE_H__
+#define __POKEMON_TABLE_H__
+
+#include <stdint.h>
+
+#include "stats.h"
+
+typedef enum {
+    GROWTH_MEDIUM_FAST = 0,
+    GROWTH_MEDIUM_SLOW = 3,
+    GROWTH_FAST = 4,
+    GROWTH_SLOW = 5,
+} Growth;
+
+typedef enum {
+    GENDER_F0 = 0x00,
+    GENDER_F12_5 = 0x1F,
+    GENDER_F25 = 0x3F,
+    GENDER_F50 = 0x7F,
+    GENDER_F75 = 0xBF,
+    GENDER_F100 = 0xFE,
+    GENDER_UNKNOWN = 0xFF,
+} Gender;
+
+typedef struct pokemon_data_table PokemonTable;
+
+int table_pokemon_pos_get(const PokemonTable* table, uint8_t index);
+uint8_t
+    table_stat_base_get(const PokemonTable* table, uint8_t num, DataStat stat, DataStatSub which);
+const char* table_stat_name_get(const PokemonTable* table, int num);
+const PokemonTable* table_pointer_get();
+
+#endif // __POKEMON_TABLE_H__

+ 88 - 0
scenes/pokemon_exit_confirm.c

@@ -0,0 +1,88 @@
+#include <gui/modules/dialog_ex.h>
+
+#include "pokemon_menu.h"
+#include "../pokemon_app.h"
+#include "../pokemon_data.h"
+
+#include "../views/select_pokemon.h"
+#include "../views/trade.h"
+
+static bool pokemon_exit_confirm_back_event_callback(void* context) {
+    UNUSED(context);
+
+    return true;
+}
+
+void pokemon_exit_confirm_dialog_callback(DialogExResult result, void* context) {
+    PokemonFap* pokemon_fap = context;
+
+    scene_manager_handle_custom_event(pokemon_fap->scene_manager, result);
+}
+
+void pokemon_exit_confirm_on_enter(void* context) {
+    PokemonFap* pokemon_fap = context;
+    DialogEx* dialog_ex = pokemon_fap->dialog_ex;
+
+    // Clean view
+    dialog_ex_reset(pokemon_fap->dialog_ex);
+
+    dialog_ex_set_left_button_text(dialog_ex, "Exit");
+    dialog_ex_set_right_button_text(dialog_ex, "Stay");
+    dialog_ex_set_header(dialog_ex, "Exit to Main Menu?", 64, 0, AlignCenter, AlignTop);
+    dialog_ex_set_text(
+        dialog_ex,
+        "Current configuration and/or\ntrade status will be lost!",
+        64,
+        12,
+        AlignCenter,
+        AlignTop);
+    dialog_ex_set_context(dialog_ex, pokemon_fap);
+    dialog_ex_set_result_callback(dialog_ex, pokemon_exit_confirm_dialog_callback);
+
+    /* Disable the existing navigation event handler to prevent handling further
+     * back events. Going back to the main menu as well as going back to the
+     * gen menu will re-enable the proper navigation handler.
+     */
+    view_dispatcher_set_navigation_event_callback(
+        pokemon_fap->view_dispatcher, pokemon_exit_confirm_back_event_callback);
+
+    view_dispatcher_add_view(
+        pokemon_fap->view_dispatcher, AppViewOpts, dialog_ex_get_view(pokemon_fap->dialog_ex));
+    view_dispatcher_switch_to_view(pokemon_fap->view_dispatcher, AppViewOpts);
+}
+
+bool pokemon_exit_confirm_on_event(void* context, SceneManagerEvent event) {
+    PokemonFap* pokemon_fap = context;
+    bool consumed = false;
+
+    if(event.type == SceneManagerEventTypeCustom) {
+        if(event.event == DialogExResultRight) {
+            consumed = scene_manager_previous_scene(pokemon_fap->scene_manager);
+        } else if(event.event == DialogExResultLeft) {
+            consumed = scene_manager_search_and_switch_to_previous_scene(
+                pokemon_fap->scene_manager, MainMenuScene);
+            /* NOTE: The above should never fail */
+            furi_check(consumed);
+
+            /* NOTE: The following might need to change when adding more 
+	     * feature support.
+	     */
+            /* Clean up PokemonData */
+            pokemon_data_free(pokemon_fap->pdata);
+
+            // Free views
+            /* These each remove themselves from the view_dispatcher */
+            select_pokemon_free(
+                pokemon_fap->view_dispatcher, AppViewSelectPokemon, pokemon_fap->select);
+            trade_free(pokemon_fap->view_dispatcher, AppViewTrade, pokemon_fap->trade);
+
+            pokemon_fap->pdata = NULL;
+            pokemon_fap->select = NULL;
+            pokemon_fap->trade = NULL;
+        }
+    } else if(event.type == SceneManagerEventTypeBack) {
+        consumed = true;
+    }
+
+    return consumed;
+}

+ 15 - 0
scenes/pokemon_exit_confirm.h

@@ -0,0 +1,15 @@
+#ifndef __POKEMON_EXIT_CONFIRM_H__
+#define __POKEMON_EXIT_CONFIRM_H__
+
+#pragma once
+
+#include <gui/modules/dialog_ex.h>
+#include <gui/scene_manager.h>
+
+void pokemon_exit_confirm_dialog_callback(DialogExResult result, void* context);
+
+void pokemon_exit_confirm_on_enter(void* context);
+
+bool pokemon_exit_confirm_on_event(void* context, SceneManagerEvent event);
+
+#endif // __POKEMON_EXIT_CONFIRM_H__

+ 232 - 0
scenes/pokemon_gen.c

@@ -0,0 +1,232 @@
+#include "../pokemon_app.h"
+#include "../pokemon_data.h"
+#include "../pokemon_char_encode.h"
+
+#include <named_list.h>
+
+#include "../views/trade.h"
+#include "../views/select_pokemon.h"
+
+#include "pokemon_menu.h"
+#include "pokemon_stats.h"
+#include "pokemon_shiny.h"
+#include "pokemon_gender.h"
+#include "pokemon_pokerus.h"
+#include "unown_form.h"
+
+static void scene_change_from_main_cb(void* context, uint32_t index) {
+    PokemonFap* pokemon_fap = (PokemonFap*)context;
+
+    /* Reuse of scenes to allow for using the same functions to set names */
+    switch(index) {
+    case SelectNicknameScene:
+    case SelectOTNameScene:
+    case SelectUnownFormScene:
+        scene_manager_set_scene_state(pokemon_fap->scene_manager, SelectNicknameScene, index);
+        break;
+    case SelectLevelScene:
+    case SelectOTIDScene:
+        scene_manager_set_scene_state(pokemon_fap->scene_manager, SelectLevelScene, index);
+        break;
+    case SelectGenderScene:
+        if(select_gender_is_static(
+               pokemon_fap->pdata,
+               table_stat_base_get(
+                   pokemon_fap->pdata->pokemon_table,
+                   pokemon_stat_get(pokemon_fap->pdata, STAT_NUM, NONE),
+                   STAT_BASE_GENDER_RATIO,
+                   NONE)))
+            return;
+        break;
+    }
+
+    /* Set the navigation handler back to the basic one in the main menu. We only
+     * want gen_back_event_callback from this menu as going back from the gen menu
+     * means we need to free pdata.
+     */
+    view_dispatcher_set_navigation_event_callback(
+        pokemon_fap->view_dispatcher, main_menu_back_event_callback);
+
+    /* Set scene state to the current index so we can have that element highlighted when
+     * we return.
+     */
+    scene_manager_set_scene_state(pokemon_fap->scene_manager, GenITradeScene, index);
+    scene_manager_next_scene(pokemon_fap->scene_manager, index);
+}
+
+bool gen_back_event_callback(void* context) {
+    furi_assert(context);
+    PokemonFap* pokemon_fap = context;
+
+    scene_manager_next_scene(pokemon_fap->scene_manager, ConfirmExitScene);
+    return true;
+}
+
+void gen_scene_on_enter(void* context) {
+    char buf[32];
+    char name_buf[11]; // All name buffers are 11 bytes at most, including term
+    PokemonFap* pokemon_fap = (PokemonFap*)context;
+    int pkmn_num;
+    uint32_t state;
+
+    // Set up trade party struct
+    if(!pokemon_fap->pdata) {
+        state = scene_manager_get_scene_state(pokemon_fap->scene_manager, GenITradeScene);
+        switch(state) {
+        case GenITradeScene:
+            state = GEN_I;
+            break;
+        case GenIITradeScene:
+            state = GEN_II;
+            break;
+        default:
+            state = 0;
+            break;
+        }
+        pokemon_fap->pdata = pokemon_data_alloc(state);
+
+        /* Clear the scene state as this is the first entry in to this scene
+	 * we definitely want to be completely reset.
+	 */
+        scene_manager_set_scene_state(pokemon_fap->scene_manager, GenITradeScene, 0);
+
+        /* Allocate select and trade views */
+        /* Allocates its own view and adds it to the main view_dispatcher */
+        pokemon_fap->select = select_pokemon_alloc(
+            pokemon_fap->pdata,
+            pokemon_fap->view_dispatcher,
+            pokemon_fap->scene_manager,
+            AppViewSelectPokemon);
+
+        // Trade View
+        /* Allocates its own view and adds it to the main view_dispatcher */
+        pokemon_fap->trade = trade_alloc(
+            pokemon_fap->pdata, &pokemon_fap->pins, pokemon_fap->view_dispatcher, AppViewTrade);
+    }
+
+    pkmn_num = pokemon_stat_get(pokemon_fap->pdata, STAT_NUM, NONE);
+
+    /* Clear the scene state of the Move scene since that is used to set the
+     * highlighted menu item.
+     * This could be done in move, but move would need its own custom exit handler
+     * which is fine but would just waste a few more bytes compared to us handling
+     * it here.
+     */
+    scene_manager_set_scene_state(pokemon_fap->scene_manager, SelectMoveScene, 0);
+    scene_manager_set_scene_state(pokemon_fap->scene_manager, SelectItemSetScene, 0);
+
+    submenu_reset(pokemon_fap->submenu);
+
+    snprintf(
+        buf,
+        sizeof(buf),
+        "Pokemon:   %s",
+        table_stat_name_get(pokemon_fap->pdata->pokemon_table, pkmn_num));
+    submenu_add_item(
+        pokemon_fap->submenu, buf, SelectPokemonScene, scene_change_from_main_cb, pokemon_fap);
+
+    pokemon_name_get(pokemon_fap->pdata, STAT_NICKNAME, name_buf, sizeof(name_buf));
+    snprintf(buf, sizeof(buf), "Nickname:  %s", name_buf);
+    submenu_add_item(
+        pokemon_fap->submenu, buf, SelectNicknameScene, scene_change_from_main_cb, pokemon_fap);
+
+    snprintf(
+        buf,
+        sizeof(buf),
+        "Level:           %d",
+        pokemon_stat_get(pokemon_fap->pdata, STAT_LEVEL, NONE));
+    submenu_add_item(
+        pokemon_fap->submenu, buf, SelectLevelScene, scene_change_from_main_cb, pokemon_fap);
+
+    if(pokemon_fap->pdata->gen == GEN_II) {
+        snprintf(
+            buf,
+            sizeof(buf),
+            "Held Item:   %s",
+            namedlist_name_get_index(
+                pokemon_fap->pdata->item_list,
+                pokemon_stat_get(pokemon_fap->pdata, STAT_HELD_ITEM, NONE)));
+        submenu_add_item(
+            pokemon_fap->submenu, buf, SelectItemScene, scene_change_from_main_cb, pokemon_fap);
+    }
+
+    submenu_add_item(
+        pokemon_fap->submenu,
+        "Select Moves",
+        SelectMoveScene,
+        scene_change_from_main_cb,
+        pokemon_fap);
+
+    if(pokemon_fap->pdata->gen == GEN_I) {
+        submenu_add_item(
+            pokemon_fap->submenu,
+            "Select Types",
+            SelectTypeScene,
+            scene_change_from_main_cb,
+            pokemon_fap);
+    }
+
+    submenu_add_item(
+        pokemon_fap->submenu,
+        namedlist_name_get_index(
+            pokemon_fap->pdata->stat_list, pokemon_stat_get(pokemon_fap->pdata, STAT_SEL, NONE)),
+        SelectStatsScene,
+        scene_change_from_main_cb,
+        pokemon_fap);
+
+    if(pokemon_fap->pdata->gen == GEN_II) {
+        snprintf(
+            buf,
+            sizeof(buf),
+            "Shiny:             %s",
+            select_shiny_is_shiny(pokemon_fap->pdata) ? "Yes" : "No");
+        submenu_add_item(
+            pokemon_fap->submenu, buf, SelectShinyScene, scene_change_from_main_cb, pokemon_fap);
+
+        snprintf(buf, sizeof(buf), "Gender:         %s", select_gender_get(pokemon_fap->pdata));
+        submenu_add_item(
+            pokemon_fap->submenu, buf, SelectGenderScene, scene_change_from_main_cb, pokemon_fap);
+
+        snprintf(buf, sizeof(buf), "Pokerus:       %s", select_pokerus_status(pokemon_fap));
+        submenu_add_item(
+            pokemon_fap->submenu, buf, SelectPokerusScene, scene_change_from_main_cb, pokemon_fap);
+
+        if(pokemon_stat_get(pokemon_fap->pdata, STAT_NUM, NONE) == 0xC8) { // Unown
+            snprintf(buf, sizeof(buf), "Unown Form: %c", unown_form_get(pokemon_fap->pdata));
+            submenu_add_item(
+                pokemon_fap->submenu,
+                buf,
+                SelectUnownFormScene,
+                scene_change_from_main_cb,
+                pokemon_fap);
+        }
+    }
+
+    snprintf(
+        buf,
+        sizeof(buf),
+        "OT ID#:          %05d",
+        pokemon_stat_get(pokemon_fap->pdata, STAT_OT_ID, NONE));
+    submenu_add_item(
+        pokemon_fap->submenu, buf, SelectOTIDScene, scene_change_from_main_cb, pokemon_fap);
+
+    pokemon_name_get(pokemon_fap->pdata, STAT_OT_NAME, name_buf, sizeof(name_buf));
+    snprintf(buf, sizeof(buf), "OT Name:      %s", name_buf);
+    submenu_add_item(
+        pokemon_fap->submenu, buf, SelectOTNameScene, scene_change_from_main_cb, pokemon_fap);
+
+    submenu_add_item(
+        pokemon_fap->submenu, "Trade PKMN", TradeScene, scene_change_from_main_cb, pokemon_fap);
+
+    /* TODO: Add Save pokemon option here */
+
+    /* HACK: No matter what gen were in, we just store the scene state in GenITradeScene */
+    submenu_set_selected_item(
+        pokemon_fap->submenu,
+        scene_manager_get_scene_state(pokemon_fap->scene_manager, GenITradeScene));
+
+    view_dispatcher_set_navigation_event_callback(
+        pokemon_fap->view_dispatcher, gen_back_event_callback);
+
+    view_dispatcher_switch_to_view(pokemon_fap->view_dispatcher, AppViewMainMenu);
+}

+ 8 - 0
scenes/pokemon_gen.h

@@ -0,0 +1,8 @@
+#ifndef POKEMON_GEN_H
+#define POKEMON_GEN_H
+
+#pragma once
+
+void gen_scene_on_enter(void* context);
+
+#endif // POKEMON_GEN_H

+ 104 - 0
scenes/pokemon_gender.c

@@ -0,0 +1,104 @@
+#include <gui/modules/submenu.h>
+
+#include "../pokemon_app.h"
+#include "../pokemon_data.h"
+#include "pokemon_menu.h"
+
+static const char* gender_str[] = {
+    "Unknown",
+    "Female",
+    "Male",
+};
+
+/* This returns a string pointer if the gender is static, NULL if it is not and
+ * the gender needs to be calculated.
+ */
+const char* select_gender_is_static(PokemonData* pdata, uint8_t ratio) {
+    switch(ratio) {
+    case 0xFF:
+        return gender_str[0];
+    case 0xFE:
+        return gender_str[1];
+    case 0x00:
+        if(pokemon_stat_get(pdata, STAT_NUM, NONE) != 0xEB) { // Tyrogue can be either gender
+            return gender_str[2];
+        }
+        break;
+    default:
+        break;
+    }
+
+    return NULL;
+}
+
+const char* select_gender_get(PokemonData* pdata) {
+    uint8_t ratio = table_stat_base_get(
+        pdata->pokemon_table,
+        pokemon_stat_get(pdata, STAT_NUM, NONE),
+        STAT_BASE_GENDER_RATIO,
+        NONE);
+    uint8_t atk_iv;
+    const char* rc;
+
+    rc = select_gender_is_static(pdata, ratio);
+    if(rc) return rc;
+
+    /* Falling through here means now we need to calculate the gender from
+     * its ratio and ATK_IV.
+     */
+    atk_iv = pokemon_stat_get(pdata, STAT_ATK_IV, NONE);
+    if(atk_iv * 17 <= ratio)
+        return gender_str[1];
+    else
+        return gender_str[2];
+}
+
+static void select_gender_selected_callback(void* context, uint32_t index) {
+    PokemonFap* pokemon_fap = (PokemonFap*)context;
+    PokemonData* pdata = pokemon_fap->pdata;
+    uint8_t ratio = table_stat_base_get(
+        pdata->pokemon_table,
+        pokemon_stat_get(pdata, STAT_NUM, NONE),
+        STAT_BASE_GENDER_RATIO,
+        NONE);
+    uint8_t atk_iv = pokemon_stat_get(pdata, STAT_ATK_IV, NONE);
+
+    /* If we need to make the pokemon a male, increase atk IV until it exceeds
+     * the gender ratio.
+     *
+     * Note that, there is no checking here for impossible situations as the
+     * scene enter function will immediately quit if its not possible to change
+     * the gender (the extremes of gender_ratio value).
+     *
+     * The check for gender is a percentage, if ATK_IV*(255/15) <= the ratio,
+     * then the pokemon is a female. The gender ratio values end up being:
+     * DEF GENDER_F0      EQU   0 percent
+     * DEF GENDER_F12_5   EQU  12 percent + 1
+     * DEF GENDER_F25     EQU  25 percent
+     * DEF GENDER_F50     EQU  50 percent
+     * DEF GENDER_F75     EQU  75 percent
+     * DEF GENDER_F100    EQU 100 percent - 1
+     * Where percent is (255/100)
+     */
+    if(index) {
+        while((atk_iv * 17) <= ratio) atk_iv++;
+    } else {
+        while((atk_iv * 17) > ratio) atk_iv--;
+    }
+
+    pokemon_stat_set(pdata, STAT_ATK_IV, NONE, atk_iv);
+
+    scene_manager_previous_scene(pokemon_fap->scene_manager);
+}
+
+void select_gender_scene_on_enter(void* context) {
+    PokemonFap* pokemon_fap = (PokemonFap*)context;
+
+    submenu_reset(pokemon_fap->submenu);
+
+    submenu_add_item(
+        pokemon_fap->submenu, "Female", 0, select_gender_selected_callback, pokemon_fap);
+
+    submenu_add_item(
+        pokemon_fap->submenu, "Male", 1, select_gender_selected_callback, pokemon_fap);
+}

+ 22 - 0
scenes/pokemon_gender.h

@@ -0,0 +1,22 @@
+#ifndef POKEMON_GENDER_H
+#define POKEMON_GENDER_H
+
+#pragma once
+
+/* The gender ratio is a bit value, and if the ATK_IV is less than or equal to
+ * the gender ratio, the gender is female.
+ *
+ * A ratio of 0xff means gender is unknown.
+ * A ratio of 0x00 is annoyingly special. It either means that pokemon can be
+ *   male only -OR- there is a very small chance the pokemon is female. The
+ *   male only pokemon need to be specifically checked for.
+ */
+
+const char* select_gender_is_static(PokemonData* pdata, uint8_t ratio);
+
+/* This will return a pointer to a string of the pokemon's current gender */
+char* select_gender_get(PokemonData* pdata);
+
+void select_gender_scene_on_enter(void* context);
+
+#endif // POKEMON_GENDER_H

+ 94 - 0
scenes/pokemon_item.c

@@ -0,0 +1,94 @@
+#include <gui/modules/submenu.h>
+#include <gui/scene_manager.h>
+#include <stdio.h>
+
+#include <named_list.h>
+
+#include "../pokemon_app.h"
+#include "../pokemon_data.h"
+#include "pokemon_menu.h"
+
+static void select_item_selected_callback(void* context, uint32_t index) {
+    PokemonFap* pokemon_fap = (PokemonFap*)context;
+    uint32_t item = scene_manager_get_scene_state(pokemon_fap->scene_manager, SelectItemSetScene);
+
+    pokemon_stat_set(pokemon_fap->pdata, STAT_HELD_ITEM, item, index);
+
+    FURI_LOG_D(
+        TAG,
+        "[item] Set item %s",
+        namedlist_name_get_index(
+            pokemon_fap->pdata->item_list,
+            pokemon_stat_get(pokemon_fap->pdata, STAT_HELD_ITEM, item)));
+
+    /* Move back to Gen menu. This assumes this submenu is only ever used in Gen II */
+    scene_manager_search_and_switch_to_previous_scene(pokemon_fap->scene_manager, GenIITradeScene);
+}
+
+static void select_item_index_callback(void* context, uint32_t index) {
+    PokemonFap* pokemon_fap = (PokemonFap*)context;
+
+    /* Move to next scene */
+    scene_manager_set_scene_state(pokemon_fap->scene_manager, SelectItemSetScene, index);
+    scene_manager_next_scene(pokemon_fap->scene_manager, SelectItemSetScene);
+}
+
+void select_item_scene_on_enter(void* context) {
+    furi_assert(context);
+    PokemonFap* pokemon_fap = (PokemonFap*)context;
+    int i;
+    const char* name;
+    char letter[2] = {'\0'};
+
+    submenu_reset(pokemon_fap->submenu);
+
+    /* The item list should always start with No Item, put that at the start
+     * for quick access.
+     */
+    submenu_add_item(
+        pokemon_fap->submenu,
+        namedlist_name_get_index(pokemon_fap->pdata->item_list, 0),
+        0,
+        select_item_selected_callback,
+        pokemon_fap);
+
+    for(i = 1;; i++) {
+        name = namedlist_name_get_pos(pokemon_fap->pdata->item_list, i);
+        if(name == NULL) break;
+        if(name[0] != letter[0]) {
+            letter[0] = name[0];
+            submenu_add_item(
+                pokemon_fap->submenu, letter, letter[0], select_item_index_callback, pokemon_fap);
+        }
+    }
+
+    submenu_set_selected_item(
+        pokemon_fap->submenu,
+        scene_manager_get_scene_state(pokemon_fap->scene_manager, SelectItemSetScene));
+    scene_manager_set_scene_state(pokemon_fap->scene_manager, SelectItemSetScene, 0);
+}
+
+void select_item_set_scene_on_enter(void* context) {
+    PokemonFap* pokemon_fap = (PokemonFap*)context;
+    int i;
+    const char* name;
+    char letter =
+        (char)scene_manager_get_scene_state(pokemon_fap->scene_manager, SelectItemSetScene);
+
+    /* Populate submenu with all items that start with `letter` */
+    /* NOTE! Start with pos of 1 in the item list since 0 should always be no item! */
+    submenu_reset(pokemon_fap->submenu);
+    for(i = 1;; i++) {
+        name = namedlist_name_get_pos(pokemon_fap->pdata->item_list, i);
+        if(name == NULL) break;
+        if(name[0] == letter &&
+           (pokemon_fap->pdata->gen & namedlist_gen_get_pos(pokemon_fap->pdata->item_list, i))) {
+            submenu_add_item(
+                pokemon_fap->submenu,
+                name,
+                namedlist_index_get(pokemon_fap->pdata->item_list, i),
+                select_item_selected_callback,
+                pokemon_fap);
+        }
+    }
+}

+ 10 - 0
scenes/pokemon_item.h

@@ -0,0 +1,10 @@
+#ifndef POKEMON_ITEM_H
+#define POKEMON_ITEM_H
+
+#pragma once
+
+void select_item_scene_on_enter(void* context);
+
+void select_item_set_scene_on_enter(void* context);
+
+#endif // POKEMON_ITEM_H

+ 0 - 60
scenes/pokemon_level.c

@@ -1,60 +0,0 @@
-#include <furi.h>
-#include <gui/modules/text_input.h>
-#include <gui/view_dispatcher.h>
-#include <stdlib.h>
-
-#include "../pokemon_app.h"
-#include "pokemon_menu.h"
-
-static char level_buf[4];
-
-static bool select_level_input_validator(const char* text, FuriString* error, void* context) {
-    PokemonFap* pokemon_fap = (PokemonFap*)context;
-    int level_val;
-    bool rc = true;
-
-    level_val = atoi(text);
-    if(level_val < 2 || level_val > 100) {
-        furi_string_printf(error, "Level must\nbe a number\nbetween\n2-100!\n");
-        rc = false;
-    } else {
-        pokemon_fap->trade_block->party[0].level = level_val;
-        pokemon_fap->trade_block->party[0].level_again = level_val;
-    }
-
-    return rc;
-}
-
-static void select_level_input_callback(void* context) {
-    PokemonFap* pokemon_fap = (PokemonFap*)context;
-
-    /* Recalculate all stats from updated level */
-    pokemon_trade_block_recalculate_stats_from_level(pokemon_fap);
-    scene_manager_previous_scene(pokemon_fap->scene_manager);
-}
-
-void select_level_scene_on_exit(void* context) {
-    PokemonFap* pokemon_fap = (PokemonFap*)context;
-
-    view_dispatcher_switch_to_view(pokemon_fap->view_dispatcher, AppViewMainMenu);
-    view_dispatcher_remove_view(pokemon_fap->view_dispatcher, AppViewOpts);
-}
-
-void select_level_scene_on_enter(void* context) {
-    PokemonFap* pokemon_fap = (PokemonFap*)context;
-
-    text_input_reset(pokemon_fap->text_input);
-    text_input_set_validator(pokemon_fap->text_input, select_level_input_validator, pokemon_fap);
-    text_input_set_result_callback(
-        pokemon_fap->text_input,
-        select_level_input_callback,
-        pokemon_fap,
-        level_buf,
-        sizeof(level_buf),
-        true);
-    text_input_set_header_text(pokemon_fap->text_input, "Enter level (numbers only):");
-
-    view_dispatcher_add_view(
-        pokemon_fap->view_dispatcher, AppViewOpts, text_input_get_view(pokemon_fap->text_input));
-    view_dispatcher_switch_to_view(pokemon_fap->view_dispatcher, AppViewOpts);
-}

+ 0 - 9
scenes/pokemon_level.h

@@ -1,9 +0,0 @@
-#ifndef POKEMON_LEVEL_H
-#define POKEMON_LEVEL_H
-
-#pragma once
-
-void select_level_scene_on_enter(void* context);
-void select_level_scene_on_exit(void* context);
-
-#endif // POKEMON_LEVEL_H

+ 94 - 98
scenes/pokemon_menu.c

@@ -1,21 +1,31 @@
 #include "../pokemon_app.h"
 #include "../pokemon_app.h"
+#include "../pokemon_data.h"
 #include "../pokemon_char_encode.h"
 #include "../pokemon_char_encode.h"
 
 
 #include "pokemon_menu.h"
 #include "pokemon_menu.h"
+#include "pokemon_gen.h"
 #include "pokemon_select.h"
 #include "pokemon_select.h"
-#include "pokemon_nickname.h"
-#include "pokemon_level.h"
+#include "pokemon_name_input.h"
+#include "pokemon_number_input.h"
 #include "pokemon_move.h"
 #include "pokemon_move.h"
+#include "pokemon_item.h"
 #include "pokemon_type.h"
 #include "pokemon_type.h"
 #include "pokemon_stats.h"
 #include "pokemon_stats.h"
-#include "pokemon_ot_id.h"
-#include "pokemon_ot_name.h"
+#include "pokemon_shiny.h"
+#include "pokemon_gender.h"
+#include "pokemon_pokerus.h"
 #include "pokemon_trade.h"
 #include "pokemon_trade.h"
 #include "pokemon_pins.h"
 #include "pokemon_pins.h"
+#include "pokemon_exit_confirm.h"
 
 
 static void scene_change_from_main_cb(void* context, uint32_t index) {
 static void scene_change_from_main_cb(void* context, uint32_t index) {
     PokemonFap* pokemon_fap = (PokemonFap*)context;
     PokemonFap* pokemon_fap = (PokemonFap*)context;
 
 
+    /* The same trade scene is used for both gen i and ii. Set the real index to
+     * scene's state.
+     */
+    scene_manager_set_scene_state(pokemon_fap->scene_manager, GenITradeScene, index);
+
     /* Set scene state to the current index so we can have that element highlighted when
     /* Set scene state to the current index so we can have that element highlighted when
      * we return.
      * we return.
      */
      */
@@ -30,75 +40,26 @@ bool main_menu_back_event_callback(void* context) {
 }
 }
 
 
 void main_menu_scene_on_enter(void* context) {
 void main_menu_scene_on_enter(void* context) {
-    char buf[32];
-    char name_buf[11]; // All name buffers are 11 bytes at most, including term
     PokemonFap* pokemon_fap = (PokemonFap*)context;
     PokemonFap* pokemon_fap = (PokemonFap*)context;
 
 
-    /* Clear the scene state of the Move scene since that is used to set the
-     * highlighted meny item.
-     */
-    scene_manager_set_scene_state(pokemon_fap->scene_manager, SelectMoveScene, 0);
-
-    /* HACK: Since we may have come from trade view, we cannot assume that
-     * pokemon_fap->curr_pokemon is correct.
-     * The proper way to do this would be to instead of tracking curr_pokemon
-     * separately, have it always be derived fro the current trade_block.
-     */
-    pokemon_fap->curr_pokemon = pokemon_table_get_num_from_index(
-        pokemon_fap->pokemon_table, pokemon_fap->trade_block->party_members[0]);
-
     submenu_reset(pokemon_fap->submenu);
     submenu_reset(pokemon_fap->submenu);
+    submenu_set_header(pokemon_fap->submenu, "Pokemon Trade Tool");
 
 
-    snprintf(
-        buf,
-        sizeof(buf),
-        "Pokemon:   %s",
-        pokemon_fap->pokemon_table[pokemon_fap->curr_pokemon].name);
-    submenu_add_item(
-        pokemon_fap->submenu, buf, SelectPokemonScene, scene_change_from_main_cb, pokemon_fap);
-    pokemon_encoded_array_to_str(
-        name_buf, (uint8_t*)pokemon_fap->trade_block->nickname, sizeof(name_buf));
-    snprintf(buf, sizeof(buf), "Nickname:  %s", name_buf);
-    submenu_add_item(
-        pokemon_fap->submenu, buf, SelectNicknameScene, scene_change_from_main_cb, pokemon_fap);
-    snprintf(buf, sizeof(buf), "Level:           %d", pokemon_fap->trade_block->party[0].level);
-    submenu_add_item(
-        pokemon_fap->submenu, buf, SelectLevelScene, scene_change_from_main_cb, pokemon_fap);
-    submenu_add_item(
-        pokemon_fap->submenu,
-        "Select Moves",
-        SelectMoveScene,
-        scene_change_from_main_cb,
-        pokemon_fap);
     submenu_add_item(
     submenu_add_item(
         pokemon_fap->submenu,
         pokemon_fap->submenu,
-        "Select Types",
-        SelectTypeScene,
+        "Gen I    (R/B/Y non-JPN)",
+        GenITradeScene,
         scene_change_from_main_cb,
         scene_change_from_main_cb,
         pokemon_fap);
         pokemon_fap);
     submenu_add_item(
     submenu_add_item(
         pokemon_fap->submenu,
         pokemon_fap->submenu,
-        stats_text[pokemon_fap->curr_stats],
-        SelectStatsScene,
+        "Gen II   (G/S/C non-JPN)",
+        GenIITradeScene,
         scene_change_from_main_cb,
         scene_change_from_main_cb,
         pokemon_fap);
         pokemon_fap);
-    snprintf(
-        buf,
-        sizeof(buf),
-        "OT ID#:          %05d",
-        __builtin_bswap16(pokemon_fap->trade_block->party[0].ot_id));
-    submenu_add_item(
-        pokemon_fap->submenu, buf, SelectOTIDScene, scene_change_from_main_cb, pokemon_fap);
-    pokemon_encoded_array_to_str(
-        name_buf, (uint8_t*)pokemon_fap->trade_block->ot_name, sizeof(name_buf));
-    snprintf(buf, sizeof(buf), "OT Name:      %s", name_buf);
-    submenu_add_item(
-        pokemon_fap->submenu, buf, SelectOTNameScene, scene_change_from_main_cb, pokemon_fap);
-    submenu_add_item(
-        pokemon_fap->submenu, "Trade PKMN", TradeScene, scene_change_from_main_cb, pokemon_fap);
     submenu_add_item(
     submenu_add_item(
         pokemon_fap->submenu,
         pokemon_fap->submenu,
-        "Select Pinout",
+        "Select EXT Pinout",
         SelectPinsScene,
         SelectPinsScene,
         scene_change_from_main_cb,
         scene_change_from_main_cb,
         pokemon_fap);
         pokemon_fap);
@@ -109,6 +70,7 @@ void main_menu_scene_on_enter(void* context) {
 
 
     view_dispatcher_set_navigation_event_callback(
     view_dispatcher_set_navigation_event_callback(
         pokemon_fap->view_dispatcher, main_menu_back_event_callback);
         pokemon_fap->view_dispatcher, main_menu_back_event_callback);
+
     view_dispatcher_switch_to_view(pokemon_fap->view_dispatcher, AppViewMainMenu);
     view_dispatcher_switch_to_view(pokemon_fap->view_dispatcher, AppViewMainMenu);
 }
 }
 
 
@@ -122,52 +84,86 @@ void null_scene_on_exit(void* context) {
     UNUSED(context);
     UNUSED(context);
 }
 }
 
 
+void generic_scene_on_exit(void* context) {
+    PokemonFap* pokemon_fap = (PokemonFap*)context;
+
+    view_dispatcher_switch_to_view(pokemon_fap->view_dispatcher, AppViewMainMenu);
+    view_dispatcher_remove_view(pokemon_fap->view_dispatcher, AppViewOpts);
+}
+
 void (*const pokemon_scene_on_enter_handlers[])(void*) = {
 void (*const pokemon_scene_on_enter_handlers[])(void*) = {
-    main_menu_scene_on_enter,
-    select_pokemon_scene_on_enter,
-    select_nickname_scene_on_enter,
-    select_level_scene_on_enter,
-    select_move_scene_on_enter,
-    select_move_index_scene_on_enter,
-    select_move_set_scene_on_enter,
-    select_type_scene_on_enter,
-    select_stats_scene_on_enter,
-    select_ot_id_scene_on_enter,
-    select_ot_name_scene_on_enter,
-    trade_scene_on_enter,
-    select_pins_scene_on_enter,
+    main_menu_scene_on_enter, //MainMenuScene,
+    gen_scene_on_enter, //GenITradeScene,
+    gen_scene_on_enter, //GenIITradeScene,
+    select_pokemon_scene_on_enter, //SelectPokemonScene,
+    select_name_scene_on_enter, //SelectNicknameScene,
+    select_number_scene_on_enter, //SelectLevelScene,
+    select_move_scene_on_enter, //SelectMoveScene,
+    select_move_index_scene_on_enter, //SelectMoveIndexScene,
+    select_move_set_scene_on_enter, //SelectMoveSetScene,
+    select_item_scene_on_enter, //SelectItemScene,
+    select_item_set_scene_on_enter, //SelectItemSetScene,
+    select_type_scene_on_enter, //SelectTypeScene,
+    select_stats_scene_on_enter, //SelectStatsScene,
+    select_shiny_scene_on_enter, //SelectShinyScene,
+    select_gender_scene_on_enter, //SelectGenderScene,
+    select_pokerus_scene_on_enter, //SelectPokerusScene,
+    select_name_scene_on_enter, //SelectUnownFormScene,
+    select_number_scene_on_enter, //SelectOTIDScene,
+    select_name_scene_on_enter, //SelectOTNameScene,
+    trade_scene_on_enter, //TradeScene,
+    select_pins_scene_on_enter, //SelectPinsScene,
+    pokemon_exit_confirm_on_enter, //ConfirmExitScene,
 };
 };
 
 
 void (*const pokemon_scene_on_exit_handlers[])(void*) = {
 void (*const pokemon_scene_on_exit_handlers[])(void*) = {
-    null_scene_on_exit,
-    select_pokemon_scene_on_exit,
-    select_nickname_scene_on_exit,
-    select_level_scene_on_exit,
-    null_scene_on_exit,
-    null_scene_on_exit,
-    null_scene_on_exit,
-    select_type_scene_on_exit,
-    null_scene_on_exit,
-    select_ot_id_scene_on_exit,
-    select_ot_name_scene_on_exit,
-    null_scene_on_exit,
-    select_pins_scene_on_exit,
+    null_scene_on_exit, //MainMenuScene,
+    null_scene_on_exit, //GenITradeScene,
+    null_scene_on_exit, //GenIITradeScene,
+    null_scene_on_exit, //SelectPokemonScene,
+    generic_scene_on_exit, //SelectNicknameScene,
+    generic_scene_on_exit, //SelectLevelScene,
+    null_scene_on_exit, //SelectMoveScene,
+    null_scene_on_exit, //SelectMoveIndexScene,
+    null_scene_on_exit, //SelectMoveSetScene,
+    null_scene_on_exit, //SelectItemScene,
+    null_scene_on_exit, //SelectItemSetScene,
+    generic_scene_on_exit, //SelectTypeScene,
+    null_scene_on_exit, //SelectStatsScene,
+    null_scene_on_exit, //SelectShinyScene,
+    null_scene_on_exit, //SelectGenderScene,
+    generic_scene_on_exit, //SelectPokerusScene,
+    generic_scene_on_exit, //SelectUnownFormScene,
+    generic_scene_on_exit, //SelectOTIDScene,
+    generic_scene_on_exit, //SelectOTNameScene,
+    null_scene_on_exit, //TradeScene,
+    generic_scene_on_exit, //SelectPinsScene,
+    generic_scene_on_exit, //ConfirmExitScene,
 };
 };
 
 
 bool (*const pokemon_scene_on_event_handlers[])(void*, SceneManagerEvent) = {
 bool (*const pokemon_scene_on_event_handlers[])(void*, SceneManagerEvent) = {
-    null_scene_on_event,
-    null_scene_on_event,
-    null_scene_on_event,
-    null_scene_on_event,
-    null_scene_on_event,
-    null_scene_on_event,
-    null_scene_on_event,
-    null_scene_on_event,
-    null_scene_on_event,
-    null_scene_on_event,
-    null_scene_on_event,
-    null_scene_on_event,
-    null_scene_on_event,
+    null_scene_on_event, //MainMenuScene,
+    null_scene_on_event, //GenITradeScene,
+    null_scene_on_event, //GenIITradeScene,
+    null_scene_on_event, //SelectPokemonScene,
+    null_scene_on_event, //SelectNicknameScene,
+    null_scene_on_event, //SelectLevelScene,
+    null_scene_on_event, //SelectMoveScene,
+    null_scene_on_event, //SelectMoveIndexScene,
+    null_scene_on_event, //SelectMoveSetScene,
+    null_scene_on_event, //SelectItemScene,
+    null_scene_on_event, //SelectItemSetScene,
+    null_scene_on_event, //SelectTypeScene,
+    null_scene_on_event, //SelectStatsScene,
+    null_scene_on_event, //SelectShinyScene,
+    null_scene_on_event, //SelectGenderScene,
+    null_scene_on_event, //SelectPokerusScene,
+    null_scene_on_event, //SelectUnownFormScene,
+    null_scene_on_event, //SelectOTIDScene,
+    null_scene_on_event, //SelectOTNameScene,
+    null_scene_on_event, //TradeScene,
+    null_scene_on_event, //SelectPinsScene,
+    pokemon_exit_confirm_on_event, //ConfirmExitScene,
 };
 };
 
 
 const SceneManagerHandlers pokemon_scene_manager_handlers = {
 const SceneManagerHandlers pokemon_scene_manager_handlers = {

+ 11 - 0
scenes/pokemon_menu.h

@@ -7,21 +7,32 @@
 
 
 typedef enum {
 typedef enum {
     MainMenuScene,
     MainMenuScene,
+    GenITradeScene, // Formerly main menu scene
+    GenIITradeScene,
     SelectPokemonScene,
     SelectPokemonScene,
     SelectNicknameScene,
     SelectNicknameScene,
     SelectLevelScene,
     SelectLevelScene,
     SelectMoveScene,
     SelectMoveScene,
     SelectMoveIndexScene,
     SelectMoveIndexScene,
     SelectMoveSetScene,
     SelectMoveSetScene,
+    SelectItemScene,
+    SelectItemSetScene,
     SelectTypeScene,
     SelectTypeScene,
     SelectStatsScene,
     SelectStatsScene,
+    SelectShinyScene,
+    SelectGenderScene,
+    SelectPokerusScene,
+    SelectUnownFormScene,
     SelectOTIDScene,
     SelectOTIDScene,
     SelectOTNameScene,
     SelectOTNameScene,
     TradeScene,
     TradeScene,
     SelectPinsScene,
     SelectPinsScene,
+    ConfirmExitScene,
     SceneCount,
     SceneCount,
 } AppScene;
 } AppScene;
 
 
 extern const SceneManagerHandlers pokemon_scene_manager_handlers;
 extern const SceneManagerHandlers pokemon_scene_manager_handlers;
 
 
+bool main_menu_back_event_callback(void* context);
+
 #endif // POKEMON_MENU_H
 #endif // POKEMON_MENU_H

+ 51 - 44
scenes/pokemon_move.c

@@ -2,24 +2,31 @@
 #include <gui/scene_manager.h>
 #include <gui/scene_manager.h>
 #include <stdio.h>
 #include <stdio.h>
 
 
+#include <named_list.h>
+
 #include "../pokemon_app.h"
 #include "../pokemon_app.h"
+#include "../pokemon_data.h"
 #include "pokemon_menu.h"
 #include "pokemon_menu.h"
 
 
 static void select_move_selected_callback(void* context, uint32_t index) {
 static void select_move_selected_callback(void* context, uint32_t index) {
     PokemonFap* pokemon_fap = (PokemonFap*)context;
     PokemonFap* pokemon_fap = (PokemonFap*)context;
     uint32_t move = scene_manager_get_scene_state(pokemon_fap->scene_manager, SelectMoveScene);
     uint32_t move = scene_manager_get_scene_state(pokemon_fap->scene_manager, SelectMoveScene);
+    uint8_t num = pokemon_stat_get(pokemon_fap->pdata, STAT_NUM, NONE);
 
 
     if(index == UINT32_MAX) {
     if(index == UINT32_MAX) {
-        pokemon_fap->trade_block->party[0].move[move] =
-            pokemon_fap->pokemon_table[pokemon_fap->curr_pokemon].move[move];
+        pokemon_stat_set(
+            pokemon_fap->pdata,
+            STAT_MOVE,
+            move,
+            table_stat_base_get(pokemon_fap->pdata->pokemon_table, num, STAT_MOVE, move));
     } else {
     } else {
-        pokemon_fap->trade_block->party[0].move[move] = (uint8_t)index;
+        pokemon_stat_set(pokemon_fap->pdata, STAT_MOVE, move, index);
     }
     }
     FURI_LOG_D(
     FURI_LOG_D(
         TAG,
         TAG,
         "[move] Set move %s to %d",
         "[move] Set move %s to %d",
-        pokemon_named_list_get_name_from_index(
-            pokemon_fap->move_list, pokemon_fap->trade_block->party[0].move[move]),
+        namedlist_name_get_index(
+            pokemon_fap->pdata->move_list, pokemon_stat_get(pokemon_fap->pdata, STAT_MOVE, move)),
         (int)move);
         (int)move);
 
 
     /* Move back to move menu */
     /* Move back to move menu */
@@ -45,36 +52,24 @@ static void select_move_number_callback(void* context, uint32_t index) {
 }
 }
 
 
 void select_move_scene_on_enter(void* context) {
 void select_move_scene_on_enter(void* context) {
+    furi_assert(context);
     PokemonFap* pokemon_fap = (PokemonFap*)context;
     PokemonFap* pokemon_fap = (PokemonFap*)context;
-    uint8_t* pkmn_move = pokemon_fap->trade_block->party[0].move;
     char buf[64];
     char buf[64];
+    int i;
 
 
     submenu_reset(pokemon_fap->submenu);
     submenu_reset(pokemon_fap->submenu);
 
 
-    snprintf(
-        buf,
-        sizeof(buf),
-        "Move 1:         %s",
-        pokemon_named_list_get_name_from_index(pokemon_fap->move_list, pkmn_move[0]));
-    submenu_add_item(pokemon_fap->submenu, buf, 0, select_move_number_callback, pokemon_fap);
-    snprintf(
-        buf,
-        sizeof(buf),
-        "Move 2:        %s",
-        pokemon_named_list_get_name_from_index(pokemon_fap->move_list, pkmn_move[1]));
-    submenu_add_item(pokemon_fap->submenu, buf, 1, select_move_number_callback, pokemon_fap);
-    snprintf(
-        buf,
-        sizeof(buf),
-        "Move 3:        %s",
-        pokemon_named_list_get_name_from_index(pokemon_fap->move_list, pkmn_move[2]));
-    submenu_add_item(pokemon_fap->submenu, buf, 2, select_move_number_callback, pokemon_fap);
-    snprintf(
-        buf,
-        sizeof(buf),
-        "Move 4:        %s",
-        pokemon_named_list_get_name_from_index(pokemon_fap->move_list, pkmn_move[3]));
-    submenu_add_item(pokemon_fap->submenu, buf, 3, select_move_number_callback, pokemon_fap);
+    for(i = 0; i < 4; i++) {
+        snprintf(
+            buf,
+            sizeof(buf),
+            "Move %d:         %s",
+            i + 1,
+            namedlist_name_get_index(
+                pokemon_fap->pdata->move_list,
+                pokemon_stat_get(pokemon_fap->pdata, STAT_MOVE, i)));
+        submenu_add_item(pokemon_fap->submenu, buf, i, select_move_number_callback, pokemon_fap);
+    }
 
 
     /* TODO: Add a "Default all moves" item? */
     /* TODO: Add a "Default all moves" item? */
 
 
@@ -91,10 +86,8 @@ void select_move_index_scene_on_enter(void* context) {
     int i;
     int i;
     char letter[2] = {'\0'};
     char letter[2] = {'\0'};
     char buf[32];
     char buf[32];
-    int curr_pokemon = pokemon_fap->curr_pokemon;
+    const char* name;
     uint32_t move_num = scene_manager_get_scene_state(pokemon_fap->scene_manager, SelectMoveScene);
     uint32_t move_num = scene_manager_get_scene_state(pokemon_fap->scene_manager, SelectMoveScene);
-    uint8_t default_move = pokemon_fap->pokemon_table[curr_pokemon].move[move_num];
-    const NamedList* move_list = pokemon_fap->move_list;
 
 
     submenu_reset(pokemon_fap->submenu);
     submenu_reset(pokemon_fap->submenu);
     /* The move list should always start with No Move, put that at the start
     /* The move list should always start with No Move, put that at the start
@@ -102,8 +95,8 @@ void select_move_index_scene_on_enter(void* context) {
      */
      */
     submenu_add_item(
     submenu_add_item(
         pokemon_fap->submenu,
         pokemon_fap->submenu,
-        move_list[0].name,
-        move_list[0].index,
+        namedlist_name_get_index(pokemon_fap->pdata->move_list, 0),
+        0,
         select_move_selected_callback,
         select_move_selected_callback,
         pokemon_fap);
         pokemon_fap);
 
 
@@ -112,15 +105,26 @@ void select_move_index_scene_on_enter(void* context) {
         buf,
         buf,
         sizeof(buf),
         sizeof(buf),
         "Default [%s]",
         "Default [%s]",
-        pokemon_named_list_get_name_from_index(pokemon_fap->move_list, default_move));
+        namedlist_name_get_index(
+            pokemon_fap->pdata->move_list,
+            table_stat_base_get(
+                pokemon_fap->pdata->pokemon_table,
+                pokemon_stat_get(pokemon_fap->pdata, STAT_NUM, NONE),
+                STAT_MOVE,
+                move_num)));
     submenu_add_item(
     submenu_add_item(
         pokemon_fap->submenu, buf, UINT32_MAX, select_move_selected_callback, pokemon_fap);
         pokemon_fap->submenu, buf, UINT32_MAX, select_move_selected_callback, pokemon_fap);
 
 
     /* Now, walk through the list and make a submenu item for each move's starting letter */
     /* Now, walk through the list and make a submenu item for each move's starting letter */
     for(i = 1;; i++) {
     for(i = 1;; i++) {
-        if(move_list[i].name == NULL) break;
-        if(toupper(move_list[i].name[0]) != toupper(letter[0])) {
-            letter[0] = toupper(move_list[i].name[0]);
+        name = namedlist_name_get_pos(pokemon_fap->pdata->move_list, i);
+        if(name == NULL) break;
+        /* TODO: Add check here for generation match. Currently, this will populate
+	 * the letters that have any move associated with them, even if not for the
+	 * generation currently being used.
+	 */
+        if(name[0] != letter[0]) {
+            letter[0] = name[0];
             submenu_add_item(
             submenu_add_item(
                 pokemon_fap->submenu, letter, letter[0], select_move_index_callback, pokemon_fap);
                 pokemon_fap->submenu, letter, letter[0], select_move_index_callback, pokemon_fap);
         }
         }
@@ -134,6 +138,7 @@ void select_move_index_scene_on_enter(void* context) {
 void select_move_set_scene_on_enter(void* context) {
 void select_move_set_scene_on_enter(void* context) {
     PokemonFap* pokemon_fap = (PokemonFap*)context;
     PokemonFap* pokemon_fap = (PokemonFap*)context;
     int i;
     int i;
+    const char* name;
     char letter =
     char letter =
         (char)scene_manager_get_scene_state(pokemon_fap->scene_manager, SelectMoveIndexScene);
         (char)scene_manager_get_scene_state(pokemon_fap->scene_manager, SelectMoveIndexScene);
 
 
@@ -141,14 +146,16 @@ void select_move_set_scene_on_enter(void* context) {
     /* NOTE! Start with index of 1 in the move list since 0 should always be no move! */
     /* NOTE! Start with index of 1 in the move list since 0 should always be no move! */
     submenu_reset(pokemon_fap->submenu);
     submenu_reset(pokemon_fap->submenu);
     for(i = 1;; i++) {
     for(i = 1;; i++) {
-        if(pokemon_fap->move_list[i].name == NULL) break;
-        if(toupper(pokemon_fap->move_list[i].name[0]) == toupper(letter)) {
+        name = namedlist_name_get_pos(pokemon_fap->pdata->move_list, i);
+        if(name == NULL) break;
+        if(name[0] == letter &&
+           (pokemon_fap->pdata->gen & namedlist_gen_get_pos(pokemon_fap->pdata->move_list, i))) {
             submenu_add_item(
             submenu_add_item(
                 pokemon_fap->submenu,
                 pokemon_fap->submenu,
-                pokemon_fap->move_list[i].name,
-                pokemon_fap->move_list[i].index,
+                name,
+                namedlist_index_get(pokemon_fap->pdata->move_list, i),
                 select_move_selected_callback,
                 select_move_selected_callback,
                 pokemon_fap);
                 pokemon_fap);
-        }
+        };
     }
     }
 }
 }

+ 124 - 0
scenes/pokemon_name_input.c

@@ -0,0 +1,124 @@
+#include <ctype.h>
+#include <furi.h>
+#include <gui/modules/text_input.h>
+#include <gui/view_dispatcher.h>
+#include <stdlib.h>
+
+#include "../pokemon_app.h"
+#include "../pokemon_data.h"
+#include "../pokemon_char_encode.h"
+#include "pokemon_menu.h"
+#include "unown_form.h"
+
+static char name_buf[LEN_NAME_BUF];
+
+/* NOTE:
+ * It would be nice if we could cleanly default to the pokemon's name as their
+ * name. The issue is that if you enter a blank line to text input, it does
+ * call this function, but returning true does nothing. However, I've found that
+ * if you check for the first char of the buffer being \0, you can then set the
+ * buffer and then return true. This has the effect of staying in the text_input
+ * screen, but, prepopulating the text entry with the buffer AND staying on the
+ * save button.
+ */
+static bool select_name_input_validator(const char* text, FuriString* error, void* context) {
+    PokemonFap* pokemon_fap = (PokemonFap*)context;
+    uint32_t state =
+        scene_manager_get_scene_state(pokemon_fap->scene_manager, SelectNicknameScene);
+    unsigned int i;
+
+    /* A blank field for the pokemon nickname means revert to default name */
+    if(text[0] == '\0' && state == SelectNicknameScene) {
+        /* Get the pokemon's name and populate our buffer with it */
+        /* TODO: Nidoran M/F are still a problem with this. */
+        pokemon_default_nickname_set(name_buf, pokemon_fap->pdata, sizeof(name_buf));
+        return true;
+    }
+
+    /* Check to ensure no digits in OT name/nickname */
+    for(i = 0; i < strlen(text); i++) {
+        if(isdigit((unsigned int)text[i])) {
+            furi_string_printf(error, "Name cannot\ncontain\nnumbers!");
+            return false;
+        }
+    }
+
+    /* Check for Unown setting is a character */
+    if(state == SelectUnownFormScene) {
+        if(!isalpha((int)text[0])) {
+            furi_string_printf(error, "Form must\nbe a single\nletter!");
+            return false;
+        }
+    }
+
+    switch(state) {
+    case SelectNicknameScene:
+        pokemon_name_set(pokemon_fap->pdata, STAT_NICKNAME, (char*)text);
+        break;
+    case SelectOTNameScene:
+        pokemon_name_set(pokemon_fap->pdata, STAT_OT_NAME, (char*)text);
+        break;
+    case SelectUnownFormScene:
+        unown_form_set(pokemon_fap->pdata, text[0]);
+        break;
+    default:
+        furi_crash("Invalid scene");
+        break;
+    }
+
+    return true;
+}
+
+static void select_name_input_callback(void* context) {
+    PokemonFap* pokemon_fap = (PokemonFap*)context;
+
+    scene_manager_previous_scene(pokemon_fap->scene_manager);
+}
+
+void select_name_scene_on_enter(void* context) {
+    PokemonFap* pokemon_fap = (PokemonFap*)context;
+    uint32_t state =
+        scene_manager_get_scene_state(pokemon_fap->scene_manager, SelectNicknameScene);
+    char* header;
+    int len;
+    DataStat stat;
+
+    switch(state) {
+    case SelectNicknameScene:
+        header = "Nickname (none for default)";
+        len = LEN_NICKNAME;
+        stat = STAT_NICKNAME;
+        break;
+    case SelectOTNameScene:
+        header = "Enter OT Name";
+        len = LEN_OT_NAME;
+        stat = STAT_OT_NAME;
+        break;
+    case SelectUnownFormScene:
+        header = "Enter Unown Letter Form";
+        len = 2;
+        stat = STAT_OT_NAME;
+        break;
+    default:
+        furi_crash("Name: invalid state");
+        break;
+    }
+
+    if(state == SelectUnownFormScene) {
+        /* Put the current letter in the buffer */
+        name_buf[0] = unown_form_get(pokemon_fap->pdata);
+        name_buf[1] = '\0';
+    } else {
+        pokemon_name_get(pokemon_fap->pdata, stat, name_buf, len);
+    }
+
+    text_input_reset(pokemon_fap->text_input);
+    text_input_set_validator(pokemon_fap->text_input, select_name_input_validator, pokemon_fap);
+    text_input_set_result_callback(
+        pokemon_fap->text_input, select_name_input_callback, pokemon_fap, name_buf, len, true);
+    text_input_set_header_text(pokemon_fap->text_input, header);
+
+    view_dispatcher_add_view(
+        pokemon_fap->view_dispatcher, AppViewOpts, text_input_get_view(pokemon_fap->text_input));
+    view_dispatcher_switch_to_view(pokemon_fap->view_dispatcher, AppViewOpts);
+}

+ 8 - 0
scenes/pokemon_name_input.h

@@ -0,0 +1,8 @@
+#ifndef POKEMON_NAME_H
+#define POKEMON_NAME_H
+
+#pragma once
+
+void select_name_scene_on_enter(void* context);
+
+#endif // POKEMON_NAME_H

+ 0 - 85
scenes/pokemon_nickname.c

@@ -1,85 +0,0 @@
-#include <ctype.h>
-#include <furi.h>
-#include <gui/modules/text_input.h>
-#include <gui/view_dispatcher.h>
-#include <stdlib.h>
-
-#include "../pokemon_app.h"
-#include "../pokemon_char_encode.h"
-#include "pokemon_menu.h"
-
-static char name_buf[11];
-
-/* NOTE:
- * It would be nice if we could cleanly default to the pokemon's name as their
- * nickname. The issue is that if you enter a blank line to text input, it does
- * call this function, but returning true does nothing. However, I've found that
- * if you check for the first char of the buffer being \0, you can then set the
- * buffer and then return true. This has the effect of staying in the text_input
- * screen, but, prepopulating the text entry with the buffer AND staying on the
- * save button.
- */
-static bool select_nickname_input_validator(const char* text, FuriString* error, void* context) {
-    PokemonFap* pokemon_fap = (PokemonFap*)context;
-    bool rc = true;
-    unsigned int i;
-
-    if(text[0] == '\0') {
-        /* Get the pokemon's name and populate our buffer with it */
-        /* XXX: Nidoran M/F are still a problem with this. */
-        pokemon_trade_block_set_default_name(name_buf, pokemon_fap, sizeof(name_buf));
-        return true;
-    }
-
-    for(i = 0; i < strlen(text); i++) {
-        if(isdigit((unsigned int)text[i])) {
-            furi_string_printf(error, "Name cannot\ncontain\nnumbers!");
-            rc = false;
-        }
-    }
-
-    if(rc == true) {
-        /* Clear existing nickname in trade block*/
-        memset(pokemon_fap->trade_block->nickname, TERM_, sizeof(struct name));
-
-        /* Encode string to nickname */
-        pokemon_str_to_encoded_array(
-            (uint8_t*)pokemon_fap->trade_block->nickname, (char*)text, strlen(text));
-        FURI_LOG_D(TAG, "[nickname] Set nickname to %s", text);
-    }
-
-    return rc;
-}
-
-static void select_nickname_input_callback(void* context) {
-    PokemonFap* pokemon_fap = (PokemonFap*)context;
-
-    scene_manager_previous_scene(pokemon_fap->scene_manager);
-}
-
-void select_nickname_scene_on_exit(void* context) {
-    PokemonFap* pokemon_fap = (PokemonFap*)context;
-
-    view_dispatcher_switch_to_view(pokemon_fap->view_dispatcher, AppViewMainMenu);
-    view_dispatcher_remove_view(pokemon_fap->view_dispatcher, AppViewOpts);
-}
-
-void select_nickname_scene_on_enter(void* context) {
-    PokemonFap* pokemon_fap = (PokemonFap*)context;
-
-    text_input_reset(pokemon_fap->text_input);
-    text_input_set_validator(
-        pokemon_fap->text_input, select_nickname_input_validator, pokemon_fap);
-    text_input_set_result_callback(
-        pokemon_fap->text_input,
-        select_nickname_input_callback,
-        pokemon_fap,
-        name_buf,
-        sizeof(name_buf),
-        true);
-    text_input_set_header_text(pokemon_fap->text_input, "Nickname (none for default)");
-
-    view_dispatcher_add_view(
-        pokemon_fap->view_dispatcher, AppViewOpts, text_input_get_view(pokemon_fap->text_input));
-    view_dispatcher_switch_to_view(pokemon_fap->view_dispatcher, AppViewOpts);
-}

+ 0 - 9
scenes/pokemon_nickname.h

@@ -1,9 +0,0 @@
-#ifndef POKEMON_NICKNAME_H
-#define POKEMON_NICKNAME_H
-
-#pragma once
-
-void select_nickname_scene_on_enter(void* context);
-void select_nickname_scene_on_exit(void* context);
-
-#endif // POKEMON_NICKNAME_H

+ 102 - 0
scenes/pokemon_number_input.c

@@ -0,0 +1,102 @@
+#include <furi.h>
+#include <gui/modules/text_input.h>
+#include <gui/view_dispatcher.h>
+#include <stdlib.h>
+
+#include "../pokemon_app.h"
+#include "../pokemon_data.h"
+#include "pokemon_menu.h"
+
+static char number_buf[LEN_NUM_BUF];
+
+static bool select_number_input_validator(const char* text, FuriString* error, void* context) {
+    PokemonFap* pokemon_fap = (PokemonFap*)context;
+    uint32_t state = scene_manager_get_scene_state(pokemon_fap->scene_manager, SelectLevelScene);
+    int number;
+    char* error_str;
+    int min;
+    int max;
+    DataStat stat;
+    bool rc = true;
+    unsigned int i;
+
+    switch(state) {
+    case SelectLevelScene:
+        error_str = "Level must\nbe a number\nbetween\n2-100!";
+        min = 2;
+        max = 100;
+        stat = STAT_LEVEL;
+        break;
+    case SelectOTIDScene:
+        error_str = "OT ID must\nbe between\n0-65535!";
+        min = 0;
+        max = 65535;
+        stat = STAT_OT_ID;
+        break;
+    default:
+        return false;
+    }
+
+    /* Need to check each byte to ensure is not alpha. atoi returns 0 which is
+     * technically a valid OTID, so we need to separately check for alpha chars.
+     */
+    for(i = 0; i < sizeof(text); i++) {
+        if(!isdigit((unsigned int)text[i])) {
+            if(text[i] == '\0') break;
+            rc = false;
+            break;
+        }
+    }
+
+    number = atoi(text);
+    if(number < min || number > max || rc == false) {
+        furi_string_printf(error, error_str);
+        rc = false;
+    } else {
+        pokemon_stat_set(pokemon_fap->pdata, stat, NONE, number);
+    }
+
+    return rc;
+}
+
+static void select_number_input_callback(void* context) {
+    PokemonFap* pokemon_fap = (PokemonFap*)context;
+
+    scene_manager_previous_scene(pokemon_fap->scene_manager);
+}
+
+void select_number_scene_on_enter(void* context) {
+    PokemonFap* pokemon_fap = (PokemonFap*)context;
+    char* header;
+    uint32_t state = scene_manager_get_scene_state(pokemon_fap->scene_manager, SelectLevelScene);
+    int len;
+    DataStat stat;
+
+    switch(state) {
+    case SelectLevelScene:
+        header = "Enter level (numbers only)";
+        len = LEN_LEVEL;
+        stat = STAT_LEVEL;
+        break;
+    case SelectOTIDScene:
+        header = "Enter OT ID (numbers only)";
+        len = LEN_OT_ID;
+        stat = STAT_OT_ID;
+        break;
+    default:
+        furi_crash("Num: invalid state");
+        break;
+    }
+
+    snprintf(number_buf, len, "%d", pokemon_stat_get(pokemon_fap->pdata, stat, NONE));
+
+    text_input_reset(pokemon_fap->text_input);
+    text_input_set_validator(pokemon_fap->text_input, select_number_input_validator, pokemon_fap);
+    text_input_set_result_callback(
+        pokemon_fap->text_input, select_number_input_callback, pokemon_fap, number_buf, len, true);
+    text_input_set_header_text(pokemon_fap->text_input, header);
+
+    view_dispatcher_add_view(
+        pokemon_fap->view_dispatcher, AppViewOpts, text_input_get_view(pokemon_fap->text_input));
+    view_dispatcher_switch_to_view(pokemon_fap->view_dispatcher, AppViewOpts);
+}

+ 8 - 0
scenes/pokemon_number_input.h

@@ -0,0 +1,8 @@
+#ifndef POKEMON_NUMBER_INPUT_H
+#define POKEMON_NUMBER_INPUT_H
+
+#pragma once
+
+void select_number_scene_on_enter(void* context);
+
+#endif // POKEMON_NUMBER_INPUT_H

+ 0 - 74
scenes/pokemon_ot_id.c

@@ -1,74 +0,0 @@
-#include <ctype.h>
-#include <furi.h>
-#include <gui/modules/text_input.h>
-#include <gui/view_dispatcher.h>
-#include <stdlib.h>
-
-#include "../pokemon_app.h"
-#include "pokemon_menu.h"
-
-static char ot_id_buf[6];
-
-static bool select_ot_id_input_validator(const char* text, FuriString* error, void* context) {
-    PokemonFap* pokemon_fap = (PokemonFap*)context;
-    int ot_id;
-    uint16_t ot_id_16;
-    bool rc = true;
-    unsigned int i;
-
-    /* Need to check each byte to ensure is not alpha. atoi returns 0 which is
-     * technically a valid ID, so we need to separately check for alpha chars.
-     */
-    for(i = 0; i < sizeof(ot_id_buf); i++) {
-        if(!isdigit((unsigned int)text[i])) {
-            if(text[i] == '\0') break;
-            rc = false;
-            break;
-        }
-    }
-
-    ot_id = atoi(text);
-    if(ot_id < 0 || ot_id > 65535 || rc == false) {
-        furi_string_printf(error, "OT ID must\nbe between\n0-65535!");
-        rc = false;
-    } else {
-        ot_id_16 = __builtin_bswap16((uint16_t)ot_id);
-        pokemon_fap->trade_block->party[0].ot_id = ot_id_16;
-    }
-
-    FURI_LOG_D(TAG, "[ot_id] Set OT ID to %05d", (uint16_t)ot_id);
-
-    return rc;
-}
-
-static void select_ot_id_input_callback(void* context) {
-    PokemonFap* pokemon_fap = (PokemonFap*)context;
-
-    scene_manager_previous_scene(pokemon_fap->scene_manager);
-}
-
-void select_ot_id_scene_on_exit(void* context) {
-    PokemonFap* pokemon_fap = (PokemonFap*)context;
-
-    view_dispatcher_switch_to_view(pokemon_fap->view_dispatcher, AppViewMainMenu);
-    view_dispatcher_remove_view(pokemon_fap->view_dispatcher, AppViewOpts);
-}
-
-void select_ot_id_scene_on_enter(void* context) {
-    PokemonFap* pokemon_fap = (PokemonFap*)context;
-
-    text_input_reset(pokemon_fap->text_input);
-    text_input_set_validator(pokemon_fap->text_input, select_ot_id_input_validator, pokemon_fap);
-    text_input_set_result_callback(
-        pokemon_fap->text_input,
-        select_ot_id_input_callback,
-        pokemon_fap,
-        ot_id_buf,
-        sizeof(ot_id_buf),
-        true);
-    text_input_set_header_text(pokemon_fap->text_input, "Enter OT ID (numbers only):");
-
-    view_dispatcher_add_view(
-        pokemon_fap->view_dispatcher, AppViewOpts, text_input_get_view(pokemon_fap->text_input));
-    view_dispatcher_switch_to_view(pokemon_fap->view_dispatcher, AppViewOpts);
-}

+ 0 - 9
scenes/pokemon_ot_id.h

@@ -1,9 +0,0 @@
-#ifndef POKEMON_OT_ID_H
-#define POKEMON_OT_ID_H
-
-#pragma once
-
-void select_ot_id_scene_on_enter(void* context);
-void select_ot_id_scene_on_exit(void* context);
-
-#endif // POKEMON_OT_ID_H

+ 0 - 70
scenes/pokemon_ot_name.c

@@ -1,70 +0,0 @@
-#include <ctype.h>
-#include <furi.h>
-#include <gui/modules/text_input.h>
-#include <gui/view_dispatcher.h>
-#include <stdlib.h>
-
-#include "../pokemon_app.h"
-#include "../pokemon_char_encode.h"
-#include "pokemon_menu.h"
-
-static char ot_name_buf[8];
-
-static bool select_ot_name_input_validator(const char* text, FuriString* error, void* context) {
-    PokemonFap* pokemon_fap = (PokemonFap*)context;
-    bool rc = true;
-    unsigned int i;
-
-    // OT name is 7 chars max on gen 1, so only take that and then fill the rest of the 11 bytes with term
-
-    for(i = 0; i < sizeof(ot_name_buf); i++) {
-        if(isdigit((unsigned int)text[i])) {
-            furi_string_printf(error, "Name cannot\ncontain\nnumbers!");
-            rc = false;
-        }
-    }
-
-    if(rc == true) {
-        /* Clear existing OT Name in trade block*/
-        memset(pokemon_fap->trade_block->ot_name, TERM_, sizeof(struct name));
-
-        /* Encode string to OT Name */
-        pokemon_str_to_encoded_array(
-            (uint8_t*)pokemon_fap->trade_block->ot_name, (char*)text, strlen(text));
-        FURI_LOG_D(TAG, "[ot_name] Set OT name to %s", text);
-    }
-
-    return rc;
-}
-
-static void select_ot_name_input_callback(void* context) {
-    PokemonFap* pokemon_fap = (PokemonFap*)context;
-
-    scene_manager_previous_scene(pokemon_fap->scene_manager);
-}
-
-void select_ot_name_scene_on_exit(void* context) {
-    PokemonFap* pokemon_fap = (PokemonFap*)context;
-
-    view_dispatcher_switch_to_view(pokemon_fap->view_dispatcher, AppViewMainMenu);
-    view_dispatcher_remove_view(pokemon_fap->view_dispatcher, AppViewOpts);
-}
-
-void select_ot_name_scene_on_enter(void* context) {
-    PokemonFap* pokemon_fap = (PokemonFap*)context;
-
-    text_input_reset(pokemon_fap->text_input);
-    text_input_set_validator(pokemon_fap->text_input, select_ot_name_input_validator, pokemon_fap);
-    text_input_set_result_callback(
-        pokemon_fap->text_input,
-        select_ot_name_input_callback,
-        pokemon_fap,
-        ot_name_buf,
-        sizeof(ot_name_buf),
-        true);
-    text_input_set_header_text(pokemon_fap->text_input, "Enter OT Name");
-
-    view_dispatcher_add_view(
-        pokemon_fap->view_dispatcher, AppViewOpts, text_input_get_view(pokemon_fap->text_input));
-    view_dispatcher_switch_to_view(pokemon_fap->view_dispatcher, AppViewOpts);
-}

+ 0 - 9
scenes/pokemon_ot_name.h

@@ -1,9 +0,0 @@
-#ifndef POKEMON_OT_NAME_H
-#define POKEMON_OT_NAME_H
-
-#pragma once
-
-void select_ot_name_scene_on_enter(void* context);
-void select_ot_name_scene_on_exit(void* context);
-
-#endif // POKEMON_OT_NAME_H

+ 1 - 7
scenes/pokemon_pins.c

@@ -9,6 +9,7 @@ struct named_pins {
     const GpioPin* pin;
     const GpioPin* pin;
 };
 };
 
 
+/* XXX: These exist already in Flipper API */
 static const struct named_pins named_pins[] = {
 static const struct named_pins named_pins[] = {
     {"PA7", &gpio_ext_pa7},
     {"PA7", &gpio_ext_pa7},
     {"PA6", &gpio_ext_pa6},
     {"PA6", &gpio_ext_pa6},
@@ -145,13 +146,6 @@ static void select_pins_rebuild_list(PokemonFap* pokemon_fap) {
     variable_item_set_current_value_text(builder.clk, named_pins[builder.clk_index].text);
     variable_item_set_current_value_text(builder.clk, named_pins[builder.clk_index].text);
 }
 }
 
 
-void select_pins_scene_on_exit(void* context) {
-    PokemonFap* pokemon_fap = (PokemonFap*)context;
-
-    view_dispatcher_switch_to_view(pokemon_fap->view_dispatcher, AppViewMainMenu);
-    view_dispatcher_remove_view(pokemon_fap->view_dispatcher, AppViewOpts);
-}
-
 void select_pins_scene_on_enter(void* context) {
 void select_pins_scene_on_enter(void* context) {
     PokemonFap* pokemon_fap = (PokemonFap*)context;
     PokemonFap* pokemon_fap = (PokemonFap*)context;
 
 

+ 0 - 1
scenes/pokemon_pins.h

@@ -4,6 +4,5 @@
 #pragma once
 #pragma once
 
 
 void select_pins_scene_on_enter(void* context);
 void select_pins_scene_on_enter(void* context);
-void select_pins_scene_on_exit(void* context);
 
 
 #endif // POKEMON_PINS_H
 #endif // POKEMON_PINS_H

+ 172 - 0
scenes/pokemon_pokerus.c

@@ -0,0 +1,172 @@
+#include <gui/modules/variable_item_list.h>
+#include <furi.h>
+
+#include "../pokemon_app.h"
+#include "pokemon_menu.h"
+
+static const char* pokerus_states[] = {
+    "Clean",
+    "Infected",
+    "Cured",
+    "",
+};
+
+static const char* strains[] = {
+    "None",
+    "A",
+    "B",
+    "C",
+    "D",
+    "",
+};
+
+const char* select_pokerus_status(PokemonFap* pokemon_fap) {
+    uint8_t pokerus;
+
+    pokerus = pokemon_stat_get(pokemon_fap->pdata, STAT_POKERUS, NONE);
+
+    if(pokerus == 0x00) return pokerus_states[0];
+    if((pokerus & 0x0f) != 0x00) return pokerus_states[1];
+    return pokerus_states[2];
+}
+
+struct pokerus_itemlist {
+    VariableItem* strain;
+    VariableItem* days;
+};
+
+static struct pokerus_itemlist pokerus = {0};
+static void select_pokerus_rebuild_list(PokemonFap* pokemon_fap);
+
+static void select_strain_callback(VariableItem* item) {
+    uint8_t index = variable_item_get_current_value_index(item);
+    uint8_t pokerus;
+    PokemonFap* pokemon_fap = variable_item_get_context(item);
+
+    /* Need to read/modify/write the existing stat */
+    pokerus = pokemon_stat_get(pokemon_fap->pdata, STAT_POKERUS, NONE);
+    pokerus &= 0x0f;
+
+    /* Need to set the new text from the mangled index */
+    variable_item_set_current_value_text(item, strains[index]);
+
+    /* demangle the index to the value we need to set in trade struct */
+    if(index == 0)
+        ;
+    else if(index == 0x01)
+        index = 0x04; // Map this back to the A strain
+    else
+        index--;
+    pokerus |= (index << 4);
+    if((pokerus & 0xf0) == 0x00) pokerus = 0;
+    pokemon_stat_set(pokemon_fap->pdata, STAT_POKERUS, NONE, pokerus);
+
+    select_pokerus_rebuild_list(pokemon_fap);
+    variable_item_list_set_selected_item(pokemon_fap->variable_item_list, 0);
+}
+
+static void select_days_callback(VariableItem* item) {
+    uint8_t index = variable_item_get_current_value_index(item);
+    uint8_t pokerus;
+    PokemonFap* pokemon_fap = variable_item_get_context(item);
+
+    /* Need to read/modify/write the existing stat */
+    pokerus = pokemon_stat_get(pokemon_fap->pdata, STAT_POKERUS, NONE);
+    pokerus &= 0xf0;
+    pokerus |= index;
+    pokemon_stat_set(pokemon_fap->pdata, STAT_POKERUS, NONE, pokerus);
+
+    select_pokerus_rebuild_list(pokemon_fap);
+    variable_item_list_set_selected_item(pokemon_fap->variable_item_list, 1);
+}
+
+static void select_pokerus_rebuild_list(PokemonFap* pokemon_fap) {
+    uint8_t strain;
+    uint8_t days;
+    FuriString* daystring = NULL;
+
+    days = pokemon_stat_get(pokemon_fap->pdata, STAT_POKERUS, NONE);
+    strain = (days >> 4);
+    days &= 0x0f;
+
+    variable_item_list_reset(pokemon_fap->variable_item_list);
+
+    pokerus.strain = variable_item_list_add(
+        pokemon_fap->variable_item_list, "Strain:", 5, select_strain_callback, pokemon_fap);
+    pokerus.days = variable_item_list_add(
+        pokemon_fap->variable_item_list,
+        "Days remain:",
+        (strain == 0 ? 0 : 16),
+        select_days_callback,
+        pokemon_fap);
+
+    /* Strain is a bit weird in that there are only 4 strains, but, a strain of
+     * 0 with a days remaining of 0 means the pokemon never had the pokerus.
+     * To combat this, we only ever set nibble values 4-7 for the 4 strains,
+     * with a value of 0 being specifically reserved for having never had it.
+     */
+    /* A
+     * 0000
+     * 0100
+     * 1000
+     * 1100
+     *
+     * B
+     * 0001
+     * 0101
+     * 1001
+     * 1101
+     *
+     * C
+     * 0010
+     * 0110
+     * 1010
+     * 1110
+     *
+     * D
+     * 0011
+     * 0111
+     * 1011
+     * 1111
+     *
+     * So, if the whole thing is 0, then it should be considered "Clean"
+     * If the lower bits are cleared, but any of the upper bits are set, modify
+     * it to be equal to 0100 for our housekeeping.
+     * Anything else, we just clear the upper bits and are now in a known good
+     * state.
+     *
+     * So everything is in order, at this point, make a value of 0x04 == 0x1 "A",
+     * leave 0x0 as 0, and add 1 to the remaining.
+     *
+     * When setting, we need to translate this back to the above bit values.
+     */
+    if(strain == 0)
+        ;
+    else if(((strain & 0x03) == 0) && ((strain & 0xc0) != 0))
+        strain = 0x01; // This would be A
+    else {
+        strain &= 0x03;
+        strain++;
+    }
+
+    daystring = furi_string_alloc_printf("%d", days);
+
+    variable_item_set_current_value_index(pokerus.strain, strain);
+    variable_item_set_current_value_text(pokerus.strain, strains[strain]);
+
+    variable_item_set_current_value_index(pokerus.days, (strain == 0 ? 0 : days));
+    variable_item_set_current_value_text(pokerus.days, furi_string_get_cstr(daystring));
+    furi_string_free(daystring);
+}
+
+void select_pokerus_scene_on_enter(void* context) {
+    PokemonFap* pokemon_fap = (PokemonFap*)context;
+
+    select_pokerus_rebuild_list(pokemon_fap);
+
+    view_dispatcher_add_view(
+        pokemon_fap->view_dispatcher,
+        AppViewOpts,
+        variable_item_list_get_view(pokemon_fap->variable_item_list));
+    view_dispatcher_switch_to_view(pokemon_fap->view_dispatcher, AppViewOpts);
+}

+ 11 - 0
scenes/pokemon_pokerus.h

@@ -0,0 +1,11 @@
+#ifndef __POKEMON_POKERUS_H__
+#define __POKEMON_POKERUS_H__
+
+#include "../pokemon_app.h"
+
+#pragma once
+
+void select_pokerus_scene_on_enter(void* context);
+const char* select_pokerus_status(PokemonFap* pokemon_fap);
+
+#endif // __POKEMON_POKERUS_H__

+ 0 - 13
scenes/pokemon_select.c

@@ -7,16 +7,3 @@ void select_pokemon_scene_on_enter(void* context) {
     // at runtime rather than at the start of the whole application
     // at runtime rather than at the start of the whole application
     view_dispatcher_switch_to_view(pokemon_fap->view_dispatcher, AppViewSelectPokemon);
     view_dispatcher_switch_to_view(pokemon_fap->view_dispatcher, AppViewSelectPokemon);
 }
 }
-
-void select_pokemon_scene_on_exit(void* context) {
-    PokemonFap* pokemon_fap = (PokemonFap*)context;
-
-    /* If a new pokemon was selected, then recalculate all of the trade_block
-     * values for the first pokemon in the party.
-     */
-    /* XXX: Find a way to see if exit was caused by an OK or a Back input? */
-    if(pokemon_fap->pokemon_table[pokemon_fap->curr_pokemon].index !=
-       pokemon_fap->trade_block->party[0].index) {
-        pokemon_trade_block_recalculate(pokemon_fap);
-    }
-}

+ 0 - 1
scenes/pokemon_select.h

@@ -4,6 +4,5 @@
 #pragma once
 #pragma once
 
 
 void select_pokemon_scene_on_enter(void* context);
 void select_pokemon_scene_on_enter(void* context);
-void select_pokemon_scene_on_exit(void* context);
 
 
 #endif // POKEMON_SELECT_H
 #endif // POKEMON_SELECT_H

+ 81 - 0
scenes/pokemon_shiny.c

@@ -0,0 +1,81 @@
+#include <gui/modules/submenu.h>
+
+#include "../pokemon_app.h"
+#include "../pokemon_data.h"
+
+#include "pokemon_menu.h"
+
+/* This just assumes gen ii for now */
+/* For a Gen II pokemon to be shiny, the following must be met:
+ * Spd, Def, and Spc must all be 10
+ * Atk must be 2, 3, 6, 7, 10, 11, 14, or 15
+ */
+bool select_shiny_is_shiny(PokemonData* pdata) {
+    uint8_t atk_iv = pokemon_stat_get(pdata, STAT_ATK_IV, NONE);
+    uint8_t def_iv = pokemon_stat_get(pdata, STAT_DEF_IV, NONE);
+    uint8_t spd_iv = pokemon_stat_get(pdata, STAT_SPD_IV, NONE);
+    uint8_t spc_iv = pokemon_stat_get(pdata, STAT_SPC_IV, NONE);
+    bool rc = 1;
+
+    if(spd_iv != 10) rc = 0;
+    if(def_iv != 10) rc = 0;
+    if(spc_iv != 10) rc = 0;
+    switch(atk_iv) {
+    case 0:
+    case 1:
+    case 4:
+    case 5:
+    case 8:
+    case 9:
+    case 12:
+    case 13:
+        rc = 0;
+        break;
+    default:
+        break;
+    }
+
+    return rc;
+}
+
+static void select_shiny_selected_callback(void* context, uint32_t index) {
+    PokemonFap* pokemon_fap = (PokemonFap*)context;
+    PokemonData* pdata = pokemon_fap->pdata;
+
+    if(!index) {
+        do {
+            /* First, reset the IV to the selected stat */
+            pokemon_stat_set(pdata, STAT_SEL, NONE, pokemon_stat_get(pdata, STAT_SEL, NONE));
+
+            /* Next, ensure the current IVs wouldn't make the pokemon shiny */
+        } while(select_shiny_is_shiny(pdata));
+    } else {
+        /* Set Def, Spd, Spc to 10 */
+        pokemon_stat_set(pdata, STAT_DEF_IV, NONE, 10);
+        pokemon_stat_set(pdata, STAT_SPD_IV, NONE, 10);
+        pokemon_stat_set(pdata, STAT_SPC_IV, NONE, 10);
+
+        /* Increase ATK IV until we hit a shiny number. Note that, this only
+         * affects IVs that are randomly generated, max IV will already be set
+         * at 15 which will make it shiny.
+         */
+        while(!select_shiny_is_shiny(pdata)) {
+            pokemon_stat_set(
+                pdata, STAT_ATK_IV, NONE, pokemon_stat_get(pdata, STAT_ATK_IV, NONE) + 1);
+        }
+    }
+
+    scene_manager_previous_scene(pokemon_fap->scene_manager);
+}
+
+void select_shiny_scene_on_enter(void* context) {
+    PokemonFap* pokemon_fap = (PokemonFap*)context;
+
+    submenu_reset(pokemon_fap->submenu);
+
+    submenu_add_item(
+        pokemon_fap->submenu, "Shiny", 1, select_shiny_selected_callback, pokemon_fap);
+
+    submenu_add_item(
+        pokemon_fap->submenu, "Not Shiny", 0, select_shiny_selected_callback, pokemon_fap);
+}

+ 9 - 0
scenes/pokemon_shiny.h

@@ -0,0 +1,9 @@
+#ifndef POKEMON_SHINY_H
+#define POKEMON_SHINY_H
+
+#pragma once
+
+void select_shiny_scene_on_enter(void* context);
+bool select_shiny_is_shiny(PokemonData* pdata);
+
+#endif // POKEMON_SHINY_H

+ 14 - 26
scenes/pokemon_stats.c

@@ -1,43 +1,31 @@
 #include <gui/modules/submenu.h>
 #include <gui/modules/submenu.h>
 
 
+#include <named_list.h>
+
 #include "../pokemon_app.h"
 #include "../pokemon_app.h"
+#include "../pokemon_data.h"
 #include "pokemon_menu.h"
 #include "pokemon_menu.h"
 
 
-const char* stats_text[6] = {
-    "Random IV, Zero EV",
-    "Random IV, Max EV / Level",
-    "Random IV, Max EV",
-    "Max IV, Zero EV",
-    "Max IV, Max EV / Level",
-    "Max IV, Max EV",
-};
-
 static void select_stats_selected_callback(void* context, uint32_t index) {
 static void select_stats_selected_callback(void* context, uint32_t index) {
     PokemonFap* pokemon_fap = (PokemonFap*)context;
     PokemonFap* pokemon_fap = (PokemonFap*)context;
 
 
-    pokemon_fap->curr_stats = index;
-
-    pokemon_trade_block_recalculate_stats_from_level(pokemon_fap);
-
-    FURI_LOG_D(TAG, "[stats] Set stats to %s", stats_text[index]);
+    pokemon_stat_set(pokemon_fap->pdata, STAT_SEL, NONE, index);
 
 
     scene_manager_previous_scene(pokemon_fap->scene_manager);
     scene_manager_previous_scene(pokemon_fap->scene_manager);
 }
 }
 
 
 void select_stats_scene_on_enter(void* context) {
 void select_stats_scene_on_enter(void* context) {
     PokemonFap* pokemon_fap = (PokemonFap*)context;
     PokemonFap* pokemon_fap = (PokemonFap*)context;
+    int i;
 
 
     submenu_reset(pokemon_fap->submenu);
     submenu_reset(pokemon_fap->submenu);
-    submenu_add_item(
-        pokemon_fap->submenu, stats_text[0], 0, select_stats_selected_callback, pokemon_fap);
-    submenu_add_item(
-        pokemon_fap->submenu, stats_text[1], 1, select_stats_selected_callback, pokemon_fap);
-    submenu_add_item(
-        pokemon_fap->submenu, stats_text[2], 2, select_stats_selected_callback, pokemon_fap);
-    submenu_add_item(
-        pokemon_fap->submenu, stats_text[3], 3, select_stats_selected_callback, pokemon_fap);
-    submenu_add_item(
-        pokemon_fap->submenu, stats_text[4], 4, select_stats_selected_callback, pokemon_fap);
-    submenu_add_item(
-        pokemon_fap->submenu, stats_text[5], 5, select_stats_selected_callback, pokemon_fap);
+    /* TODO: This is a magic number that this scene shouldn't need to know about */
+    for(i = 0; i < 6; i++) {
+        submenu_add_item(
+            pokemon_fap->submenu,
+            namedlist_name_get_index(pokemon_fap->pdata->stat_list, i),
+            i,
+            select_stats_selected_callback,
+            pokemon_fap);
+    }
 }
 }

+ 0 - 1
scenes/pokemon_stats.h

@@ -3,7 +3,6 @@
 
 
 #pragma once
 #pragma once
 
 
-extern const char* stats_text[6];
 void select_stats_scene_on_enter(void* context);
 void select_stats_scene_on_enter(void* context);
 
 
 #endif // POKEMON_STATS_H
 #endif // POKEMON_STATS_H

+ 45 - 50
scenes/pokemon_type.c

@@ -1,8 +1,22 @@
 #include <gui/modules/variable_item_list.h>
 #include <gui/modules/variable_item_list.h>
 
 
+#include <named_list.h>
+
 #include "../pokemon_app.h"
 #include "../pokemon_app.h"
+#include "../pokemon_data.h"
 #include "pokemon_menu.h"
 #include "pokemon_menu.h"
 
 
+struct type_cb {
+    DataStatSub type;
+    PokemonFap* pokemon_fap;
+};
+
+static struct type_cb type_cb[] = {
+    {TYPE_0, NULL},
+    {TYPE_1, NULL},
+    {},
+};
+
 /* TODO: In the future I would like to be able to set the types and then
 /* TODO: In the future I would like to be able to set the types and then
  * require a "save" button to save them. This would require tracking of
  * require a "save" button to save them. This would require tracking of
  * the two different VariableItems in a way that I don't know how to do
  * the two different VariableItems in a way that I don't know how to do
@@ -12,66 +26,47 @@
  * an OK press or something to save both. But thats a problem for another
  * an OK press or something to save both. But thats a problem for another
  * day.
  * day.
  */
  */
-static void select_type_1_callback(VariableItem* item) {
-    PokemonFap* pokemon_fap = variable_item_get_context(item);
-    uint8_t index = variable_item_get_current_value_index(item);
-
-    variable_item_set_current_value_text(item, pokemon_fap->type_list[index].name);
-    pokemon_fap->trade_block->party[0].type[0] = pokemon_fap->type_list[index].index;
-
-    FURI_LOG_D(
-        TAG,
-        "[type] Set type1 to %s",
-        pokemon_named_list_get_name_from_index(
-            pokemon_fap->type_list, pokemon_fap->type_list[index].index));
-}
-
-static void select_type_2_callback(VariableItem* item) {
-    PokemonFap* pokemon_fap = variable_item_get_context(item);
-    uint8_t index = variable_item_get_current_value_index(item);
-
-    variable_item_set_current_value_text(item, pokemon_fap->type_list[index].name);
-    pokemon_fap->trade_block->party[0].type[1] = pokemon_fap->type_list[index].index;
+static void select_type_callback(VariableItem* item) {
+    struct type_cb* context = variable_item_get_context(item);
+    uint8_t pos = variable_item_get_current_value_index(item);
 
 
-    FURI_LOG_D(
-        TAG,
-        "[type] Set type2 to %s",
-        pokemon_named_list_get_name_from_index(
-            pokemon_fap->type_list, pokemon_fap->type_list[index].index));
-}
-
-void select_type_scene_on_exit(void* context) {
-    PokemonFap* pokemon_fap = (PokemonFap*)context;
-
-    view_dispatcher_switch_to_view(pokemon_fap->view_dispatcher, AppViewMainMenu);
-    view_dispatcher_remove_view(pokemon_fap->view_dispatcher, AppViewOpts);
+    variable_item_set_current_value_text(
+        item, namedlist_name_get_pos(context->pokemon_fap->pdata->type_list, pos));
+    pokemon_stat_set(
+        context->pokemon_fap->pdata,
+        STAT_TYPE,
+        context->type,
+        namedlist_index_get(context->pokemon_fap->pdata->type_list, pos));
 }
 }
 
 
 void select_type_scene_on_enter(void* context) {
 void select_type_scene_on_enter(void* context) {
     PokemonFap* pokemon_fap = (PokemonFap*)context;
     PokemonFap* pokemon_fap = (PokemonFap*)context;
-    VariableItem* type1;
-    VariableItem* type2;
-    int curr_pokemon_type1 = pokemon_fap->trade_block->party[0].type[0];
-    int curr_pokemon_type2 = pokemon_fap->trade_block->party[0].type[1];
-    int num_types = pokemon_named_list_get_num_elements(pokemon_fap->type_list);
-    const NamedList* type_list = pokemon_fap->type_list;
+    VariableItem* vitype[2];
+    char* strings[2] = {"Type 1:", "Type 2:"};
+    int type;
+    int num_types = namedlist_cnt(pokemon_fap->pdata->type_list);
+    int pos;
+    int i;
 
 
     variable_item_list_reset(pokemon_fap->variable_item_list);
     variable_item_list_reset(pokemon_fap->variable_item_list);
 
 
-    type1 = variable_item_list_add(
-        pokemon_fap->variable_item_list, "Type 1:", num_types, select_type_1_callback, pokemon_fap);
-    type2 = variable_item_list_add(
-        pokemon_fap->variable_item_list, "Type 2:", num_types, select_type_2_callback, pokemon_fap);
+    /* NOTE: 2 is a magic number, but pretty obvious */
+    for(i = 0; i < 2; i++) {
+        type_cb[i].pokemon_fap = pokemon_fap;
+        type = pokemon_stat_get(pokemon_fap->pdata, STAT_TYPE, i);
+        pos = namedlist_pos_get(pokemon_fap->pdata->type_list, type);
 
 
-    variable_item_set_current_value_index(
-        type1, pokemon_named_list_get_list_pos_from_index(type_list, curr_pokemon_type1));
-    variable_item_set_current_value_text(
-        type1, pokemon_named_list_get_name_from_index(type_list, curr_pokemon_type1));
+        vitype[i] = variable_item_list_add(
+            pokemon_fap->variable_item_list,
+            strings[i],
+            num_types,
+            select_type_callback,
+            &type_cb[i]);
 
 
-    variable_item_set_current_value_index(
-        type2, pokemon_named_list_get_list_pos_from_index(type_list, curr_pokemon_type2));
-    variable_item_set_current_value_text(
-        type2, pokemon_named_list_get_name_from_index(type_list, curr_pokemon_type2));
+        variable_item_set_current_value_index(vitype[i], pos);
+        variable_item_set_current_value_text(
+            vitype[i], namedlist_name_get_pos(pokemon_fap->pdata->type_list, pos));
+    }
 
 
     view_dispatcher_add_view(
     view_dispatcher_add_view(
         pokemon_fap->view_dispatcher,
         pokemon_fap->view_dispatcher,

+ 0 - 1
scenes/pokemon_type.h

@@ -4,6 +4,5 @@
 #pragma once
 #pragma once
 
 
 void select_type_scene_on_enter(void* context);
 void select_type_scene_on_enter(void* context);
-void select_type_scene_on_exit(void* context);
 
 
 #endif // POKEMON_TYPE_H
 #endif // POKEMON_TYPE_H

+ 73 - 0
scenes/unown_form.c

@@ -0,0 +1,73 @@
+#include <stdint.h>
+#include "../pokemon_data.h"
+
+#include "unown_form.h"
+
+/* This is used to get the current IVs from the trade struct.
+ * Unown form is calculated by taking the middle bytes of each nibble of IV,
+ * pressing them in order to a single byte, and dividing that by 10 (rounded
+ * down/floor). This will create a value from 0 to 25 that is a 1:1 mapping
+ * of the English alphabet and is how Unown forms are represented.
+ *
+ * C integer division truncates to 0 rather than does any proper rounding.
+ *
+ * https://bulbapedia.bulbagarden.net/wiki/Individual_values#Unown's_letter
+ */
+static uint8_t unown_ivs_get(PokemonData* pdata) {
+    furi_assert(pdata);
+    uint16_t ivs = pokemon_stat_get(pdata, STAT_IV, NONE);
+    uint8_t ivs_mid;
+
+    ivs_mid =
+        (((ivs & 0x6000) >> 7) | ((ivs & 0x0600) >> 5) | ((ivs & 0x0060) >> 3) |
+         ((ivs & 0x0006) >> 1));
+
+    return ivs_mid;
+}
+
+static void unown_ivs_set(PokemonData* pdata, uint8_t ivs_mid) {
+    furi_assert(pdata);
+    uint16_t ivs = pokemon_stat_get(pdata, STAT_IV, NONE);
+
+    /* Clear the middle bits of each nibble */
+    ivs &= ~(0x6666);
+
+    /* Set the updated ivs_mid in to those cleared bits */
+    ivs |=
+        (((ivs_mid & 0xC0) << 7) | ((ivs_mid & 0x30) << 5) | ((ivs_mid & 0x0C) << 3) |
+         ((ivs_mid & 0x03) << 1));
+    pokemon_stat_set(pdata, STAT_IV, NONE, ivs);
+}
+
+char unown_form_get(PokemonData* pdata) {
+    uint8_t form = unown_ivs_get(pdata);
+
+    /* The forumula is specifically the center two bits of each IV slapped
+     * together and floor(/10)
+     */
+    form /= 10;
+    form += 'A';
+
+    return form;
+}
+
+/* Try and get to the desired form by adding/subtracting the current IVs */
+void unown_form_set(PokemonData* pdata, char letter) {
+    uint8_t ivs = unown_ivs_get(pdata);
+    uint8_t form;
+
+    letter = toupper(letter);
+    furi_check(isalpha(letter));
+
+    while(1) {
+        form = ((ivs / 10) + 'A');
+        if(form == letter) break;
+        if(form > letter)
+            ivs--;
+        else
+            ivs++;
+    }
+
+    /* form is now the target letter, set IVs back up */
+    unown_ivs_set(pdata, ivs);
+}

+ 13 - 0
scenes/unown_form.h

@@ -0,0 +1,13 @@
+#ifndef __UNOWN_FORM_H__
+#define __UNOWN_FORM_H__
+
+#pragma once
+
+#include "../pokemon_data.h"
+
+/* Returns ascii char, or 0 if unown is not the current pokemon */
+char unown_form_get(PokemonData* pdata);
+
+void unown_form_set(PokemonData* pdata, char letter);
+
+#endif // __UNOWN_FORM_H__

BIN
sprites/000.png


+ 0 - 0
assets/bulbasaur.png → sprites/001.png


+ 0 - 0
assets/ivysaur.png → sprites/002.png


+ 0 - 0
assets/venusaur.png → sprites/003.png


+ 0 - 0
assets/charmander.png → sprites/004.png


+ 0 - 0
assets/charmeleon.png → sprites/005.png


+ 0 - 0
assets/charizard.png → sprites/006.png


+ 0 - 0
assets/squirtle.png → sprites/007.png


+ 0 - 0
assets/wartortle.png → sprites/008.png


+ 0 - 0
assets/blastoise.png → sprites/009.png


+ 0 - 0
assets/caterpie.png → sprites/010.png


+ 0 - 0
assets/metapod.png → sprites/011.png


+ 0 - 0
assets/butterfree.png → sprites/012.png


+ 0 - 0
assets/weedle.png → sprites/013.png


+ 0 - 0
assets/kakuna.png → sprites/014.png


+ 0 - 0
assets/beedrill.png → sprites/015.png


+ 0 - 0
assets/pidgey.png → sprites/016.png


+ 0 - 0
assets/pidgeotto.png → sprites/017.png


+ 0 - 0
assets/pidgeot.png → sprites/018.png


+ 0 - 0
assets/rattata.png → sprites/019.png


+ 0 - 0
assets/raticate.png → sprites/020.png


+ 0 - 0
assets/spearow.png → sprites/021.png


+ 0 - 0
assets/fearow.png → sprites/022.png


+ 0 - 0
assets/ekans.png → sprites/023.png


+ 0 - 0
assets/arbok.png → sprites/024.png


+ 0 - 0
assets/pikachu.png → sprites/025.png


+ 0 - 0
assets/raichu.png → sprites/026.png


+ 0 - 0
assets/sandshrew.png → sprites/027.png


+ 0 - 0
assets/sandslash.png → sprites/028.png


+ 0 - 0
assets/nidoranf.png → sprites/029.png


+ 0 - 0
assets/nidorina.png → sprites/030.png


+ 0 - 0
assets/nidoqueen.png → sprites/031.png


+ 0 - 0
assets/nidoranm.png → sprites/032.png


+ 0 - 0
assets/nidorino.png → sprites/033.png


+ 0 - 0
assets/nidoking.png → sprites/034.png


Некоторые файлы не были показаны из-за большого количества измененных файлов