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

pokemon trading update (refactor PR)

https://github.com/kbembedded/Flipper-Zero-Game-Boy-Pokemon-Trading/tree/code-refactor
MX 2 лет назад
Родитель
Сommit
81c2264c1f
52 измененных файлов с 4327 добавлено и 3170 удалено
  1. 0 45
      .github/ISSUE_TEMPLATE/01_bug_report.yml
  2. 0 21
      .github/ISSUE_TEMPLATE/02_implemented.yml
  3. 58 41
      README.md
  4. 0 249
      README_es.md
  5. 46 0
      TODO.md
  6. 16 0
      changelog.md
  7. 2207 0
      pokemon_app.c
  8. 0 323
      pokemon_app.cpp
  9. 89 72
      pokemon_app.h
  10. 314 0
      pokemon_char_encode.c
  11. 110 0
      pokemon_char_encode.h
  12. 37 271
      pokemon_data.h
  13. 59 0
      scenes/pokemon_level.c
  14. 9 0
      scenes/pokemon_level.h
  15. 164 0
      scenes/pokemon_menu.c
  16. 26 0
      scenes/pokemon_menu.h
  17. 148 0
      scenes/pokemon_move.c
  18. 12 0
      scenes/pokemon_move.h
  19. 78 0
      scenes/pokemon_nickname.c
  20. 9 0
      scenes/pokemon_nickname.h
  21. 72 0
      scenes/pokemon_ot_id.c
  22. 9 0
      scenes/pokemon_ot_id.h
  23. 73 0
      scenes/pokemon_ot_name.c
  24. 9 0
      scenes/pokemon_ot_name.h
  25. 22 0
      scenes/pokemon_select.c
  26. 9 0
      scenes/pokemon_select.h
  27. 41 0
      scenes/pokemon_stats.c
  28. 9 0
      scenes/pokemon_stats.h
  29. 9 0
      scenes/pokemon_trade.c
  30. 8 0
      scenes/pokemon_trade.h
  31. 69 0
      scenes/pokemon_type.c
  32. 9 0
      scenes/pokemon_type.h
  33. 0 170
      views/select_level.cpp
  34. 0 25
      views/select_level.hpp
  35. 0 172
      views/select_move1.cpp
  36. 0 25
      views/select_move1.hpp
  37. 0 173
      views/select_move2.cpp
  38. 0 25
      views/select_move2.hpp
  39. 0 174
      views/select_move3.cpp
  40. 0 25
      views/select_move3.hpp
  41. 0 174
      views/select_move4.cpp
  42. 0 25
      views/select_move4.hpp
  43. 125 0
      views/select_pokemon.c
  44. 0 161
      views/select_pokemon.cpp
  45. 13 0
      views/select_pokemon.h
  46. 0 25
      views/select_pokemon.hpp
  47. 0 195
      views/select_stats.cpp
  48. 0 25
      views/select_stats.hpp
  49. 455 0
      views/trade.c
  50. 0 682
      views/trade.cpp
  51. 13 0
      views/trade.h
  52. 0 72
      views/trade.hpp

+ 0 - 45
.github/ISSUE_TEMPLATE/01_bug_report.yml

@@ -1,45 +0,0 @@
-name: Bug report
-description: File a bug reports regarding the app.
-labels: ["bug"]
-body:
-  - type: markdown
-    attributes:
-      value: |
-        Thank you for taking the time to fill out an issue, this template is meant for any issues related to application.
-  - type: textarea
-    id: description
-    attributes:
-      label: Describe the bug.
-      description: "A clear and concise description of what the bug is."
-    validations:
-      required: true
-  - type: textarea
-    id: repro
-    attributes:
-      label: Reproduction
-      description: "How can this bug be reproduced?"
-      placeholder: |
-        1. Switch on...
-        2. Press button '....'
-        3. Wait for the moon phase
-        4. It burns
-    validations:
-      required: true
-  - type: input
-    id: target
-    attributes:
-      label: Target Framework
-      description: Specify the target
-      # Target seems to be largely ignored by outside sources.
-  - type: textarea
-    id: logs
-    attributes:
-      label: Logs
-      description: Attach your debug logs here
-      render: Text
-      # Avoid rendering as Markdown here.
-  - type: textarea
-    id: anything-else
-    attributes:
-      label: Anything else?
-      description: Let us know if you have anything else to share.

+ 0 - 21
.github/ISSUE_TEMPLATE/02_implemented.yml

@@ -1,21 +0,0 @@
-name: I implemented it
-description: Share your process.
-labels: ["implemented"]
-body:
-- type: markdown
-  attributes:
-    value: |
-      Thank you for taking the time to fill out. If you have already implemented the application, could you share which Flipper Zero framework you have used? Additionally, could you specify the required type of Game Boy and cartridge? Lastly, it would be great if you could share some images of the process.
-- type: textarea
-  id: desc
-  attributes:
-    label: "Describe the process."
-    description: |
-      Feel free to describe in as much detail as you wish.
-  validations:
-    required: true
-- type: input
-  id: type
-  attributes:
-    label: Game boy
-    description: (Color, Pocket, Advance)

+ 58 - 41
README.md

@@ -9,9 +9,9 @@
 
 <div align="center">
 
-**FW Official** | **FW Unleashed** | **FW RogueMaster**
-:- | :- | :- 
-[![FlipC.org](https://flipc.org/EstebanFuentealba/Flipper-Zero-Game-Boy-Pokemon-Trading/badge?branch=main)](https://flipc.org/EstebanFuentealba/Flipper-Zero-Game-Boy-Pokemon-Trading?branch=main)|[![FlipC.org](https://flipc.org/EstebanFuentealba/Flipper-Zero-Game-Boy-Pokemon-Trading/badge?branch=main&firmware=unleashed)](https://flipc.org/EstebanFuentealba/Flipper-Zero-Game-Boy-Pokemon-Trading?branch=main&firmware=unleashed)|[![FlipC.org](https://flipc.org/EstebanFuentealba/Flipper-Zero-Game-Boy-Pokemon-Trading/badge?branch=main&firmware=roguemaster)](https://flipc.org/EstebanFuentealba/Flipper-Zero-Game-Boy-Pokemon-Trading?branch=main&firmware=roguemaster)
+**FW Official** | **FW Unleashed** | **FW RogueMaster** | **FW Xtreme**
+:- | :- | :- | :- 
+[![FlipC.org](https://flipc.org/EstebanFuentealba/Flipper-Zero-Game-Boy-Pokemon-Trading/badge?branch=main)](https://flipc.org/EstebanFuentealba/Flipper-Zero-Game-Boy-Pokemon-Trading?branch=main)|[![FlipC.org](https://flipc.org/EstebanFuentealba/Flipper-Zero-Game-Boy-Pokemon-Trading/badge?branch=main&firmware=unleashed)](https://flipc.org/EstebanFuentealba/Flipper-Zero-Game-Boy-Pokemon-Trading?branch=main&firmware=unleashed)|[![FlipC.org](https://flipc.org/EstebanFuentealba/Flipper-Zero-Game-Boy-Pokemon-Trading/badge?branch=main&firmware=roguemaster)](https://flipc.org/EstebanFuentealba/Flipper-Zero-Game-Boy-Pokemon-Trading?branch=main&firmware=roguemaster)|[![FlipC.org](https://flipc.org/EstebanFuentealba/Flipper-Zero-Game-Boy-Pokemon-Trading/badge?branch=main&firmware=xtreme)](https://flipc.org/EstebanFuentealba/Flipper-Zero-Game-Boy-Pokemon-Trading?branch=main&firmware=xtreme)
 </div>
 
 ## Introduction
@@ -26,10 +26,10 @@ This project is intended to be overlayed on top of an existing firmware repo,  i
 
 - Clone the [Flipper Zero firmware repository](https://github.com/flipperdevices/flipperzero-firmware). Refer to [this tutorial](https://github.com/jamisonderek/flipper-zero-tutorials/tree/main/firmware/updating/README.md) for updating the firmware.
 - Copy the "pokemon" folder into the `/applications_user/pokemon` folder in your firmware.
-- Run the command `fbt launch_app` to run it on your Flipper Zero.
+- Run the command `fbt launch` to run it on your Flipper Zero.
 
 ```bash
-./fbt launch_app APPSRC=pokemon
+./fbt launch APPSRC=pokemon
 ```
 
 - NOTE: If you only want to generate the fap file, you must run the following command.
@@ -50,9 +50,9 @@ These instructions assume that you are starting at the Flipper Zero desktop. Oth
 
 - Press the `OK` button on the Flipper to open the main menu.
 - Choose `Applications` from the menu.
-- Choose `Game Boy` from the submenu.
+- Choose `GPIO` from the submenu.
 - Choose `Pokemon Trading`
-- The Flipper Zero should show the selection of Pokemon that you want to trade, and by default, it appears as bulbasaur.
+- The Flipper Zero will show the main menu of the application. The first option is to select the Pokemon to trade.
 
     <p align='center'>
         <br />
@@ -62,74 +62,94 @@ These instructions assume that you are starting at the Flipper Zero desktop. Oth
 
 - Press the `LEFT`/`RIGHT` buttons to paginate the selection of Pokemon by 1.
 - Press the `UP`/`DOWN` buttons to paginate the selection of Pokemon by 10.
-- Press the `OK` button to select the Pokemon to trade.
+- Press the `OK` button to select the Pokemon to trade and return to the main menu
 
     <p align='center'>
          <br />
         <img src="./docs/images/flipper-zero-flat-2.png" width="400" /><br />
     </p>
 
-- The Flipper Zero should show the selection of the Pokemon's level that you want to trade, and by default, it appears as level 1.
+- The traded Pokemon's nickname can be set. When a Pokemon is selected, the nickname defaults to the species name in all caps. This mimics a Pokemon without a customized nickname. In order to reset this nickname to its default, clear the text entry field, press `OK` on the `Save` button. This will fill the text box with the default name. Press `Save` again to set this name.
+  - **Note:** The Nidoran♀ and Nidoran♂ names will not properly render. This is because the Flipper currently cannot print unicode characters to screen. Following the above instructions will fill the text entry field with `NIDORAN ` with a space after it. This space is the unrenderable ♀/♂ symbol. Once traded, it will be correctly named.
+  - **Note:** Only alphanumeric characters are supported in the Pokemon's nickname at this time.
 
+    <p align='center'>
+        <br />
+        <img src="./docs/images/flipper-zero-flat-1-1.png" width="400" />
+        <br />
+    </p>
+
+- The Pokemon's level can be adjusted as well by hitting `OK` on the level option. The minimum level is `2` and the maximum is `100`. The level is input via a text box. (Levels below 2 cause an underflow glitch in Gen I games that would cause the level to jump to 100, so if you want this just set the Pokemon's level to 100)
+    
     <p align='center'>
         <br />
         <img src="./docs/images/flipper-zero-flat-3.png" width="400" />
         <br />
     </p>
 
-- Press the `LEFT`/`RIGHT` buttons to paginate the selection of the Pokemon's level by 1.
-- Press the `UP`/`DOWN` buttons to paginate the selection of the Pokemon's level by 10.
-- Press the `OK` button to select the Pokemon's level to trade.
+- The `Select Moves` menu is used to pick the set the traded Pokemon's moves. They are pre-populated with the moveset that the Pokemon would know at level 1. Selecting a move slot will bring up an alphabetical index of moves. Additionally, `No Move` and `Default` can be quickliy selected. Note that any move after the first `No Move` is ignored. 
 
     <p align='center'>
         <br />
-        <img src="./docs/images/flipper-zero-flat-4.png" width="400" />
+        <img src="./docs/images/flipper-zero-flat-7.png" width="400" />
         <br />
     </p>
 
-- The Flipper Zero should show the selection for the Pokemon's moves that you want to trade, this will happen 4 times with default bing no move.
-
     <p align='center'>
         <br />
-        <img src="./docs/images/flipper-zero-flat-5.png" width="400" />
+        <img src="./docs/images/flipper-zero-flat-8.png" width="400" />
         <br />
     </p>
 
-- Press the `LEFT`/`RIGHT` buttons to paginate the selection of the Pokemon's current move selection by 1.
-- Press the `UP`/`DOWN` buttons to paginate the selection of the Pokemon's current move selection by 10.
-- Press the `OK` button to select the Pokemon's current move selection to trade.
+- The `Select Types` menu can change the traded Pokemon's types. The type(s) are pre-set to what the selected Pokemon normally is.
+  - Pokemon with a single type will have the same type set for both types.
+  - **Note:** Unlike other menus, changing either type immediately saves it. Pressing `Back` will keep any changes. This will be addressed in a later version. If you need to revert to the default types, a different Pokemon can be selected and the desired Pokemon re-selected.
+  - **Note:** When changing the type(s), the Pokemon's in-game stats will _NOT_ reflect the chosen type(s). Additionally, these may be overwritten back to default in-game if the Pokemon uses a move that affects types (e.g. `Transform`) or the Pokemon evolves.
 
     <p align='center'>
         <br />
-        <img src="./docs/images/flipper-zero-flat-6.png" width="400" />
+        <img src="./docs/images/flipper-zero-flat-8-1.png" width="400" />
         <br />
     </p>
 
-- The Flipper Zero should show the selection for the Pokemon's stats that you want to trade, and by default, it appears as random IV and zero EV.
-
+- The Pokemon's stats can also be influenced. The current settings are:
+  - `Random IV, Zero EV` Mimics stats of a caught wild Pokemon.
+  - `Random IV, Max EV / Level` IV is randomized, but EV is set to the maximum a trained Pokemon could be for its current level.
+  - `Randon IV, Max EV` IV is randomized, EV is set to the abosolute max for a perfectly trained Pokemon.
+  - `Max IV, Zero EV` Mimics stats of a caught wild Pokemon, but with the maximum IV possible.
+  - `Max IV, Max EV / Level` IV is max, EV is set to the maximum a trained Pokemon could be for its current level.
+  - `Max IV, Max EV` Absolutely perfect and overly powerful Pokemon.
+ 
     <p align='center'>
         <br />
-        <img src="./docs/images/flipper-zero-flat-7.png" width="400" />
+        <img src="./docs/images/flipper-zero-flat-5.png" width="400" />
         <br />
     </p>
 
-- Press the `RIGHT`/`UP` buttons to paginate the selection of the Pokemon's stats selection by 1 positively.
-- Press the `LEFT`/`DOWN` buttons to paginate the selection of the Pokemon's stats selection by 1 negatively.
-- Press the `OK` button to select the Pokemon's current move selection to trade.
+- The `OT ID#` and `OT Name` of the Pokemon can also be set. The `OT ID#` must be between `0` and `65535`. Setting the `OT ID#` and `OT Name` to the same as your current trainer's causes the game to believe it was a wild caught Pokemon and not one that was traded. This means high level Pokmon will still obey you without badges, but, will not get the experience boost of a traded Pokemon.
 
     <p align='center'>
         <br />
-        <img src="./docs/images/flipper-zero-flat-8.png" width="400" />
+        <img src="./docs/images/flipper-zero-flat-6.png" width="400" /><br />
+    </p>
+
+    <p align='center'>
         <br />
+        <img src="./docs/images/flipper-zero-flat-6-1.png" width="400" /><br />
     </p>
 
-- The Flipper Zero will display the view to connect the Game Boy.
+- Finally, select `Trade PKMN` to start the trade process.
 
     <p align='center'>
         <br />
-        <img src="./docs/images/flipper-zero-flat-9.png" width="400" /><br />
+        <img src="./docs/images/flipper-zero-flat-6-2.png" width="400" /><br />
     </p>
 
+    <p align='center'>
+        <br />
+        <img src="./docs/images/flipper-zero-flat-9.png" width="400" /><br />
+    </p>
+    
 - On your Game Boy, you should connect the  **Game Link Cable** to the Game Boy and in the game, go to the nearest  **Pokemon Center**.
 
     <p align='center'>
@@ -301,19 +321,10 @@ For each image, the color `#aaa` was transformed to `#fff` so that Flipper Zero
 - Game Boy Color (GBC)
 - Game Boy Advance (GBA)
 
-## Implemented by
+## Contributors
 <a href="https://github.com/EstebanFuentealba/Flipper-Zero-Game-Boy-Pokemon-Trading/">EstebanFuentealba</a><br />
-<a href="https://github.com/R4g3D/Flipper-Zero-Game-Boy-Pokemon-Trading/">R4g3D</a>
-
-## TODO
-- [x] Change the default traded Pokemon's naming to be no nickname
-- [x] Add view to allow the traded Pokemon's level to be chosen between 3 and 100
-- [x] Add view to allow the traded Pokemon's hidden stats to be chosen (IV and EV) from some options
-- [x] Add view to allow the traded Pokemon's moveset to be chosen (all 4 moves) allowing no move as an option
-- [ ] Debug traded Pokemon level issue where after a battle the Pokemon's level drops (doesn't affect all traded Pokemon)
-- [x] Add images for the level selection screen, stats selection screen, and move selection screens as per the original README
-- [ ] Optimise the level selection screen to be a number slider input instead of the current slideshow style selector
-- [ ] Add a view to allow for a custom Pokemon nickname (11 chars, 10 chars max used, fill and terminate with TERM_)
+<a href="https://github.com/R4g3D/Flipper-Zero-Game-Boy-Pokemon-Trading/">R4g3D</a><br />
+<a href="https://github.com/kbembedded/Flipper-Zero-Game-Boy-Pokemon-Trading/">kbembedded</a>
 
 ## Links
 
@@ -325,3 +336,9 @@ For each image, the color `#aaa` was transformed to `#fff` so that Flipper Zero
 - [Disassembly of Pokemon Yellow](https://github.com/pret/pokeyellow)
 - [Arduino-Spoofing-Gameboy-Pokemon-Trades](https://github.com/EstebanFuentealba/Arduino-Spoofing-Gameboy-Pokemon-Trades)
 - [🎮 Gameboy link cable breakout PCB](https://github.com/Palmr/gb-link-cable)
+
+<p align='center'>
+<br />
+<br />
+From Talcahuano 🇨🇱 with ❤ 
+</p>

+ 0 - 249
README_es.md

@@ -1,249 +0,0 @@
-# 🐬 Flipper Zero - Pokemon Trading in Game Boy
-
-<p align="center">
-<a target="_blank" href="https://www.reddit.com/r/flipperzero/comments/121ncot/flipper_zero_game_boy_pokemon_trading/">
-  <img align="center" alt="Flipper Zero - Pokemon Trading Game Boy" src="./docs/images/youtube.png" />
-  <br />
-</p>
-<div align="center">
-
-**FW Official** | **FW Unleashed** | **FW RogueMaster**
-:- | :- | :- 
-[![FlipC.org](https://flipc.org/EstebanFuentealba/Flipper-Zero-Game-Boy-Pokemon-Trading/badge?branch=main)](https://flipc.org/EstebanFuentealba/Flipper-Zero-Game-Boy-Pokemon-Trading?branch=main)|[![FlipC.org](https://flipc.org/EstebanFuentealba/Flipper-Zero-Game-Boy-Pokemon-Trading/badge?branch=main&firmware=unleashed)](https://flipc.org/EstebanFuentealba/Flipper-Zero-Game-Boy-Pokemon-Trading?branch=main&firmware=unleashed)|[![FlipC.org](https://flipc.org/EstebanFuentealba/Flipper-Zero-Game-Boy-Pokemon-Trading/badge?branch=main&firmware=roguemaster)](https://flipc.org/EstebanFuentealba/Flipper-Zero-Game-Boy-Pokemon-Trading?branch=main&firmware=roguemaster)
-</div>
-
-## Introducción
-
-Esta es una aplicación de intercambio de Pokemon's desde de Flipper Zero a Game Boy [(Generación I)](https://bulbapedia.bulbagarden.net/wiki/Generation_I). Flipper Zero emula un Game Boy "Esclavo" conectado a **Cable Game Link** para poder intercambiar cualquier Pokemon de la Primera Generación (Red, Blue, Yellow) a un Game Boy Real.
-
-Es una Prueba de concepto (POC) para utilizar vistas, GPIO y FURI (Flipper Universal Registry Implementation).
-
-## Instrucciones de instalación
-
-Este proyecto está destinado a ser superpuesto encima de un repositorio de firmware existente, en mi caso la versión **Release 0.79.1**.
-
-- Clona el [Repositorio del firmware de Flipper Zero](https://github.com/flipperdevices/flipperzero-firmware). Consulta este [tutorial](https://github.com/jamisonderek/flipper-zero-tutorials/tree/main/firmware/updating/README.md) para actualizar el firmware.
-- Copia la [carpeta "pokemon"](..) en la carpeta `/applications_user/pokemon` del firmware que clonaste.
-- Corre el comando `fbt launch_app` para correr en tu Flipper Zero.
-
-```bash
-./fbt launch_app APPSRC=pokemon
-```
-
-- NOTA: Si sólo quieres generar el archivo `fap` debes correr el siguiente comando.
-
-```bash
-./fbt fap_pokemon
-```
-
-y usa [**qFlipper**](https://flipperzero.one/update) para copiar el archivo **pokemon.fap** generado a la carpeta `SD Card/apps/Game Boy`.
-
-<p align='center'>
-<img src="./docs/images/qFlipper.png" width="400" /><br />
-</p>
-
-## Instrucciones de Uso
-
-Estas instrucciones asumen que está comenzando en el escritorio de Flipper Zero. De lo contrario, presione el botón Atrás hasta que esté en el escritorio.
-
-- Presione el botón `OK` en el flipper para abrir el menú principal.
-- Elija `Aplicaciones` en el menú.
-- Elija `Game Boy` en el submenú.
-- Elija `Pokemon Trading`
-- El Flipper Zero debe mostrar la selección de Pokemon que se desea intercambiar y por defecto parece bulbasaur.
-
-    <p align='center'>
-        <br />
-        <img src="./docs/images/flipper-zero-flat-1.png" width="400" />
-        <br />
-    </p>
-
-- Pulse los botones `IZQUIERDA`/`DERECHA` para paginar de 1 en 1 la selección de Pokemon.
-- Pulse los botones `ARRIBA`/`ABAJO` para paginar de 10 en 10 la selección de Pokemon.
-- Pulse el botón `OK` para seleccionar el Pokemon a Intercambiar.
-    <p align='center'>
-         <br />
-        <img src="./docs/images/flipper-zero-flat-2.png" width="400" /><br />
-    </p>
-- En el Flipper Zero se muestra la vista para conectar el Game Boy.
-    <p align='center'>
-        <br />
-        <img src="./docs/images/flipper-zero-flat-3.png" width="400" /><br />
-    </p>
-- En tu Game Boy debes conectar el **Cable Game Link** al Game Boy, en el juego dirigirte a un **Centro Pokémon** que tengas más cercano.
-    <p align='center'>
-        <br />
-        <img src="./docs/images/game_boy_pokemon_center.png" width="400" /><br />
-    </p>
-- Habla con la chica que está en el mostrador de la derecha. La chica nos dirá que para poder jugar antes tendremos que salvar el juego, le contestaremos que _SI_ pulsando el botón _A_.
-    <p align='center'>
-        <br />
-        <img src="./docs/images/game_boy_save.png" width="400" /><br />
-    </p>
-- El Flipper Zero nos mostrará que estámos conectados.
-    <p align='center'>
-        <br />
-        <img src="./docs/images/flipper-zero-flat-4.png" width="400" /><br />
-    </p>
-- En el Game Boy nos preguntará que opción queremos y Seleccionamos **CENT. CAMBIO**.
-    <p align='center'>
-        <br />
-        <img src="./docs/images/game_boy_save_trade.png" width="400" /><br />
-    </p>
-- Entrarás a la sala de Intercambio donde debes presionar el botón A del Game Boy en el lado de tu mesa.
-    <p align='center'>
-        <br />
-        <img src="./docs/images/game_boy_trade_room_2.png" width="400" /><br />
-    </p>
-- Flipper Zero quedará en una pantalla de espera con el Pokemon que seleccionaste.
-    <p align='center'>
-        <br />
-        <img src="./docs/images/flipper-zero-flat-5.png" width="400" /><br />
-    </p>
-- Se te mostrarán tus Pokemon y el Pokemon que seleccionaste en el Flipper Zero, en este Caso **Mew**. Debes seleccionar el pokemon que quieres intercambiar y presionar **TRATO**.
-    <p align='center'>
-        <br />
-        <img src="./docs/images/game_boy_trade_list_select_trade.png" width="400" /><br />
-    </p>
-- Debes confirmar el intercambio seleccionado **TRATO**.
-    <p align='center'>
-        <br />
-        <img src="./docs/images/game_boy_trade_list_select_trade_confirm.png" width="400" /><br />
-    </p>
-- Flipper Zero quedará en una pantalla de espera con el Pokemon que seleccionaste.
-    <p align='center'>
-        <br />
-        <img src="./docs/images/flipper-zero-flat-6.png" width="400" /><br />
-    </p>
-- Finalmente comenzará el intercambio de Pokemon desde **Flipper Zero** al **Game Boy**.
-    <p align='center'>
-        <br />
-        <img src="./docs/images/flipper-zero-flat-7.png" width="400" /><br />
-    </p>
-- **NOTA**: Si al final del Intercambio se te bloquea el Flipper Zero debes Rebootear presionando la combinación de teclas <img src="./docs/images/left.png" /> IZQUIERDA + <img src="./docs/images/back.png" /> ATRAS
-    <p align='center'>
-        <br />
-        <img src="./docs/images/reboot.png" width="400" /><br />
-    </p>
-
-## ¿Cómo trabaja?
-
-El método utilizado para comunicar 2 Game Boy se basa en el protocolo SPI, que es un protocolo de comunicación serial muy simple en el que un dispositivo maestro se comunica con uno o más dispositivos esclavos. El protocolo es bidireccional y sincrónico, y utiliza tres señales básicas:
-
-- Una señal de reloj (CLK).
-- Una señal de salida (Serial Out o SO).
-- Una señal de entrada (Serial In o SI).
-
-En el Game Boy, los juegos almacenan los datos en un registro de cambio interno que se utiliza para enviar y recibir información. El protocolo SPI utilizado por el Game Boy utiliza la señal de reloj para indicar cuándo se transfieren los datos.
-
-El protocolo de enlace de Game Boy es síncrono y requiere que el dispositivo esclavo responda al mismo ritmo que el dispositivo maestro. El dispositivo maestro suministra un reloj de 8KHz (velocidad de transferencia de datos de 1KB/s). La ventana de tiempo para responder es de solo **~120μs**. Sin embargo, el dispositivo esclavo no tiene restricciones y puede responder cuando recibe los datos. El reloj puede variar y no hay un límite inferior.
-
-<p align='center'>
-<br />
-<img src="./docs/images/gb_spi.png" width="400" /><br />
-</p>
-
-_Una transferencia de ejemplo de GB SPI. Aquí, el maestro envía 0xD9 (217) y el esclavo envía 0x45 (69)._
-
-<br />
-
-Se puede conocer mas al respecto en el siguiente Video [**Analyzing the Different Versions of the Link Cable**](https://youtu.be/h1KKkCfzOws?t=151).
-
-## Placa para Flipper Zero con Socket PortData EXT Link
-
-Para la placa del Fipper Zero se utilizó un [PortData EXT Link](https://es.aliexpress.com/item/1005004116983895.html) y una [place de prototipo](https://es.aliexpress.com/item/32478242317.html) de 2x8.
-
-<p align='center'>
-<br />
-<img src="./docs/images/EXT-Link.png" width="400" /><br />
-</p>
-
-_PortData EXT Link para Game Boy Color, Game Boy Pocket, GBC, GBP, GBL._
-
-<p align='center'>
-<br />
-<img src="./docs/images/pcb.png" width="400" /><br />
-</p>
-<p align='center'>
-<br />
-<img src="./docs/images/flipper-zero-pcb.png" width="400" /><br />
-</p>
-Usé una resistencia de 33kΩ en CLK, pero es opcional, se puede conectar directamente.
-
-## Conexión: Flipper Zero GPIO - Game Boy
-
-Se deben conectar los Pines de la siguiente manera
-
-<p align='center'>
-<br />
-<img src="./docs/images/wgbl-0.png" width="400" /><br />
-</p>
-
-<picture>
-    <source media="(prefers-color-scheme: dark)" srcset="./docs/images/GPIO-GBPIN_light-v2.png">
-    <source media="(prefers-color-scheme: light)" srcset="./docs/images/GPIO-GBPIN-v2.png">
-    <img
-        alt="Connect Flipper Zero GPIO to Game Boy Pins"
-        src="./docs/images/GPIO-GBPIN-v2.png">
-</picture>
-
-| Cable Game Link (Socket) | Flipper Zero GPIO |
-| ------------------------ | ----------------- |
-| 6 (GND)                  | 8 (GND)           |
-| 5 (CLK)                  | 6 (B2)            |
-| 3 (SI)                   | 7 (C3)            |
-| 2 (SO)                   | 5 (B3)            |
-
-
-## Conectar a Flipper Zero sin Socket PortData EXT Link
-
-Pudes cortar un cable directamente sin usar el socket pero debes tener en cuenta que el es un cable cruzado SI-SO.
-
-<p align='center'>
-<br />
-<img src="./docs/images/cut-cable-v3.png" width="400" /><br />
-</p>
-
-*"Cable Game Link" cortado y conectado directamente a los pines de Flipper Zero.*
-
-
-**NOTA**: No guiarse por los colores porque dependiendo del fabricante estos pueden cambiar, con un multímetro medir continuidad e identificar que cable es de que pin
-
-
-## GUI
-
-Para generar la Interfaz gráfica se utilizó la herramienta [**FUI-Editor**](https://ilin.pt/stuff/fui-editor/).
-Además se utilizaron los sprites originales del juego _Pokemon Yellow_ que se encuentran en el repositorio [**Disassembly of Pokemon Yellow**](https://github.com/pret/pokeyellow/tree/master/gfx/pokemon/front).
-
-De cada imagen se transformó el color `#aaa` a `#fff` para que Flipper Zero la renderizara bien. Para eso se utilizó un **Batch** para [Photopea](https://www.photopea.com/), el editor de imagenes online.
-
-##  Implementado en
-- Game Boy Color (GBC)
-- Game Boy Advance (GBA)
-
-## Implementado por
-<a href="https://github.com/EstebanFuentealba/Flipper-Zero-Game-Boy-Pokemon-Trading/issues?q=is%3Aissue+label%3AImplemented+is%3Aclosed+is%3Aopen+" target="_blank"><img src="./docs/images/implemented.svg" /></a>
-
-## TODO
-
-- [ ] Refactorizar el código
-- [ ] Al salir de la app el botón `OK` deja de funcionar por lo que hay que reiniciarlo 🤔
-- [ ] Setear a cada pokemon sus características, ataques, niveles por defecto
-- [ ] Mejorar animaciones
-
-## Links
-
-- [Flipper Zero firmware source code](https://github.com/flipperdevices/flipperzero-firmware)
-- Adan Scotney's pokemon [trade protocol specification](http://www.adanscotney.com/2014/01/spoofing-pokemon-trades-with-stellaris.html) and implementation
-- Derek Jamison - [Youtube Channel](https://www.youtube.com/@MrDerekJamison)
-- Matt Penny - [GBPlay Blog](https://blog.gbplay.io/)
-- [Pokémon data structure (Generation I)](<https://bulbapedia.bulbagarden.net/wiki/Pok%C3%A9mon_data_structure_(Generation_I)>)
-- [Disassembly of Pokemon Yellow](https://github.com/pret/pokeyellow)
-- [Arduino-Spoofing-Gameboy-Pokemon-Trades](https://github.com/EstebanFuentealba/Arduino-Spoofing-Gameboy-Pokemon-Trades)
-- [🎮 Gameboy link cable breakout PCB](https://github.com/Palmr/gb-link-cable)
-
-<p align='center'>
-<br />
-<br />
-Desde Talcahuano 🇨🇱 con ❤ 
-</p>

+ 46 - 0
TODO.md

@@ -0,0 +1,46 @@
+# Flipper Zero Game Boy Pokemon Trading
+`
+- Configure Gen 1 traded pokemon  
+  - Pokemon Nickname  
+    - [x] Change the default traded Pokemon's naming to be no nickname  
+    - [x] Add a view to allow for a custom Pokemon nickname (11 chars, 10 chars max used, fill and terminate with TERM_)  
+    - [ ] Figure out how to implement Nidoran male/female symbol  
+      - Previous commits used unicode escape codes which I had issues compiling for some reason, but the actual unicode chars were fine  
+      - Could make a special case for just those two  
+  - Stats  
+    - [x] Add view to allow the traded Pokemon's level to be chosen between 2 and 100  
+    - [x] Add view to allow the traded Pokemon's hidden stats to be chosen (IV and EV) from some options  
+      - [ ] Are there any better ways to present these options?  
+    - [ ] Debug traded Pokemon level issue where after a battle the Pokemon's level drops (doesn't affect all traded Pokemon)  
+    - [ ] Optimise the level selection screen to be a number slider input instead of the current slideshow style selector (Implemented as text input that only accepts numbers)  
+  - Moves  
+    - [x] Add view to allow the traded Pokemon's moveset to be chosen (all 4 moves) allowing no move as an option  
+    - [ ] Find a way to get faster scrolling through the move select submenu  
+    - [ ] Implement a way to denote that any moves after the first No Move are also No Move?  
+      - Hide all moves beyond the first No Moves?  
+      - Promote moves? e.g. if move 1 is set, 2 unset, if user sets move 3 then promote it to move 2  
+      - Automatically clear moves beyond the first no move? surprising but that is what the game does  
+  - Types  
+    - [x] Support setting pokemon type(s)  
+    - [ ] Implement a save/revert to default workflow on the select types scene  
+  - Trade  
+    - [ ] Investigate Trade screens not always blinking  
+  - UI  
+    - [ ] Find a way to line up submenu items so the main menu looks cleaner  
+      - They currently _mostly_ line up thanks to some manual spacing, but tabs don't appear to be supported to force that alignment  
+      - Alternatively may need to implement our own view to make this pretty  
+- Documentation  
+  - [x] Add images for the level selection screen, stats selection screen, and move selection screens as per the original README  
+- Codebase  
+  - [ ] Reimplement Logging calls  
+  - [ ] Clean up the codebase as it is now, there are a lot of optimizations in speed and code complexity that can be made, especially in added code in pokemon_app and maybe some code reduction/reuse in scenes  
+  - [ ] Consider using a single View in main app struct and only allocate a view as needed to reduce memory footprint  
+
+- Future Wants  
+  - [ ] Trading to Gen II games with both Gen I and Gen II pokemon  
+  - [ ] Enable IR mystery gift usage in Gen II using Flipper  
+  - [ ] Be able to set up mutiple pokemon to be able to trade more than one per trip to trade center  
+  - [ ] Be able to trade back and forth for e.g. trading a pokemon that evolves only when traded  
+  - [ ] Would Separateing out link cable states result in a cleaner API?  
+  - [ ] Implement some simple logic to be able to "battle" the Flipper?  
+  - [ ] There was a suggestion to be able to trade in a pokemon to harvest OT name and ID on the flipper and set it to that.  

+ 16 - 0
changelog.md

@@ -0,0 +1,16 @@
+# Changelog - Patch Notes
+
+## Version 1.2.3
+- **Refactor and UI cleanup:** Convert to Flipper Zero UI modules for simpler interface, reduce binary size.
+- **Add Features:** Add ability to set custom pokemon nickname or default, add ability to set OT name and ID, add ability to select pokemon type(s). Note that, an evolution as well as a couple of different attacks will cause this to be overwritten with the pokemon's default values.
+- **Bug Fixes:** Fix strange issue with exp gain causing traded pokemon to de-level and result in incorrect stats.
+
+## Version 1.2.2
+- **Extended Functionality:** Add support to set level, select moves, set up EV/IV to a few predefined configurations, set up stats based on level and EV/IV settings, set nickname to default pokemon name.
+
+## Version 1.2.1
+- **Add github action to build**
+
+## Version 1.2.0
+- **Cleanup data structs:** This refactors the main data blocks for defining pokemon, the icon, their species/hex value, as well as the large trade array in to more human friendly structs. Laying some groundwork to be able to adjust pokemon details pre-trade by @kbembedded .
+- **Bug Fixes:** Fix furi crash, Fixes #9 by @kbembedded .

+ 2207 - 0
pokemon_app.c

@@ -0,0 +1,2207 @@
+#include <furi_hal_light.h>
+#include <math.h>
+#include <pokemon_icons.h>
+
+#include "scenes/pokemon_menu.h"
+#include "views/trade.h"
+#include "views/select_pokemon.h"
+#include "pokemon_app.h"
+#include "pokemon_char_encode.h"
+
+const PokemonTable pokemon_table[] = {
+    /* Values for base_*, moves, etc., pulled directly from a copy of Pokemon Blue */
+    {"Bulbasaur",
+     &I_bulbasaur,
+     0x99,
+     0x2D,
+     0x31,
+     0x31,
+     0x2D,
+     0x41,
+     {0x16, 0x03},
+     {0x21, 0x2D, 0x00, 0x00},
+     GROWTH_MEDIUM_SLOW},
+    {"Ivysaur",
+     &I_ivysaur,
+     0x09,
+     0x3C,
+     0x3E,
+     0x3F,
+     0x3C,
+     0x50,
+     {0x16, 0x03},
+     {0x21, 0x2D, 0x49, 0x00},
+     GROWTH_MEDIUM_SLOW},
+    {"Venusaur",
+     &I_venusaur,
+     0x9A,
+     0x50,
+     0x52,
+     0x53,
+     0x50,
+     0x64,
+     {0x16, 0x03},
+     {0x21, 0x2D, 0x49, 0x16},
+     GROWTH_MEDIUM_SLOW},
+    {"Charmander",
+     &I_charmander,
+     0xB0,
+     0x27,
+     0x34,
+     0x2B,
+     0x41,
+     0x32,
+     {0x14, 0x14},
+     {0x0A, 0x2D, 0x00, 0x00},
+     GROWTH_MEDIUM_SLOW},
+    {"Charmeleon",
+     &I_charmeleon,
+     0xB2,
+     0x3A,
+     0x40,
+     0x3A,
+     0x50,
+     0x41,
+     {0x14, 0x14},
+     {0x0A, 0x2D, 0x34, 0x00},
+     GROWTH_MEDIUM_SLOW},
+    {"Charizard",
+     &I_charizard,
+     0xB4,
+     0x4E,
+     0x54,
+     0x4E,
+     0x64,
+     0x55,
+     {0x14, 0x02},
+     {0x0A, 0x2D, 0x34, 0x2B},
+     GROWTH_MEDIUM_SLOW},
+    {"Squirtle",
+     &I_squirtle,
+     0xB1,
+     0x2C,
+     0x30,
+     0x41,
+     0x2B,
+     0x32,
+     {0x15, 0x15},
+     {0x21, 0x27, 0x00, 0x00},
+     GROWTH_MEDIUM_SLOW},
+    {"Wartortle",
+     &I_wartortle,
+     0xB3,
+     0x3B,
+     0x3F,
+     0x50,
+     0x3A,
+     0x41,
+     {0x15, 0x15},
+     {0x21, 0x27, 0x91, 0x00},
+     GROWTH_MEDIUM_SLOW},
+    {"Blastoise",
+     &I_blastoise,
+     0x1C,
+     0x4F,
+     0x53,
+     0x64,
+     0x4E,
+     0x55,
+     {0x15, 0x15},
+     {0x21, 0x27, 0x91, 0x37},
+     GROWTH_MEDIUM_SLOW},
+    {"Caterpie",
+     &I_caterpie,
+     0x7B,
+     0x2D,
+     0x1E,
+     0x23,
+     0x2D,
+     0x14,
+     {0x07, 0x07},
+     {0x21, 0x51, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST},
+    {"Metapod",
+     &I_metapod,
+     0x7C,
+     0x32,
+     0x14,
+     0x37,
+     0x1E,
+     0x19,
+     {0x07, 0x07},
+     {0x6A, 0x00, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST},
+    {"Butterfree",
+     &I_butterfree,
+     0x7D,
+     0x3C,
+     0x2D,
+     0x32,
+     0x46,
+     0x50,
+     {0x07, 0x02},
+     {0x5D, 0x00, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST},
+    {"Weedle",
+     &I_weedle,
+     0x70,
+     0x28,
+     0x23,
+     0x1E,
+     0x32,
+     0x14,
+     {0x07, 0x03},
+     {0x28, 0x51, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST},
+    {"Kakuna",
+     &I_kakuna,
+     0x71,
+     0x2D,
+     0x19,
+     0x32,
+     0x23,
+     0x19,
+     {0x07, 0x03},
+     {0x6A, 0x00, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST},
+    {"Beedrill",
+     &I_beedrill,
+     0x72,
+     0x41,
+     0x50,
+     0x28,
+     0x4B,
+     0x2D,
+     {0x07, 0x03},
+     {0x1F, 0x00, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST},
+    {"Pidgey",
+     &I_pidgey,
+     0x24,
+     0x28,
+     0x2D,
+     0x28,
+     0x38,
+     0x23,
+     {0x00, 0x02},
+     {0x10, 0x00, 0x00, 0x00},
+     GROWTH_MEDIUM_SLOW},
+    {"Pidgeotto",
+     &I_pidgeotto,
+     0x96,
+     0x3F,
+     0x3C,
+     0x37,
+     0x47,
+     0x32,
+     {0x00, 0x02},
+     {0x10, 0x1C, 0x00, 0x00},
+     GROWTH_MEDIUM_SLOW},
+    {"Pidgeot",
+     &I_pidgeot,
+     0x97,
+     0x53,
+     0x50,
+     0x4B,
+     0x5B,
+     0x46,
+     {0x00, 0x02},
+     {0x10, 0x1C, 0x62, 0x00},
+     GROWTH_MEDIUM_SLOW},
+    {"Rattata",
+     &I_rattata,
+     0xA5,
+     0x1E,
+     0x38,
+     0x23,
+     0x48,
+     0x19,
+     {0x00, 0x00},
+     {0x21, 0x27, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST},
+    {"Raticate",
+     &I_raticate,
+     0xA6,
+     0x37,
+     0x51,
+     0x3C,
+     0x61,
+     0x32,
+     {0x00, 0x00},
+     {0x21, 0x27, 0x62, 0x00},
+     GROWTH_MEDIUM_FAST},
+    {"Spearow",
+     &I_spearow,
+     0x05,
+     0x28,
+     0x3C,
+     0x1E,
+     0x46,
+     0x1F,
+     {0x00, 0x02},
+     {0x40, 0x2D, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST},
+    {"Fearow",
+     &I_fearow,
+     0x23,
+     0x41,
+     0x5A,
+     0x41,
+     0x64,
+     0x3D,
+     {0x00, 0x02},
+     {0x40, 0x2D, 0x2B, 0x00},
+     GROWTH_MEDIUM_FAST},
+    {"Ekans",
+     &I_ekans,
+     0x6C,
+     0x23,
+     0x3C,
+     0x2C,
+     0x37,
+     0x28,
+     {0x03, 0x03},
+     {0x23, 0x2B, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST},
+    {"Arbok",
+     &I_arbok,
+     0x2D,
+     0x3C,
+     0x55,
+     0x45,
+     0x50,
+     0x41,
+     {0x03, 0x03},
+     {0x23, 0x2B, 0x28, 0x00},
+     GROWTH_MEDIUM_FAST},
+    {"Pikachu",
+     &I_pikachu,
+     0x54,
+     0x23,
+     0x37,
+     0x1E,
+     0x5A,
+     0x32,
+     {0x17, 0x17},
+     {0x54, 0x2D, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST},
+    {"Raichu",
+     &I_raichu,
+     0x55,
+     0x3C,
+     0x5A,
+     0x37,
+     0x64,
+     0x5A,
+     {0x17, 0x17},
+     {0x54, 0x2D, 0x56, 0x00},
+     GROWTH_MEDIUM_FAST},
+    {"Sandshrew",
+     &I_sandshrew,
+     0x60,
+     0x32,
+     0x4B,
+     0x55,
+     0x28,
+     0x1E,
+     {0x04, 0x04},
+     {0x0A, 0x00, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST},
+    {"Sandslash",
+     &I_sandslash,
+     0x61,
+     0x4B,
+     0x64,
+     0x6E,
+     0x41,
+     0x37,
+     {0x04, 0x04},
+     {0x0A, 0x1C, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST},
+    {"Nidoran\200",
+     &I_nidoranf,
+     0x0F,
+     0x37,
+     0x2F,
+     0x34,
+     0x29,
+     0x28,
+     {0x03, 0x03},
+     {0x2D, 0x21, 0x00, 0x00},
+     GROWTH_MEDIUM_SLOW},
+    {"Nidorina",
+     &I_nidorina,
+     0xA8,
+     0x46,
+     0x3E,
+     0x43,
+     0x38,
+     0x37,
+     {0x03, 0x03},
+     {0x2D, 0x21, 0x0A, 0x00},
+     GROWTH_MEDIUM_SLOW},
+    {"Nidoqueen",
+     &I_nidoqueen,
+     0x10,
+     0x5A,
+     0x52,
+     0x57,
+     0x4C,
+     0x4B,
+     {0x03, 0x04},
+     {0x21, 0x0A, 0x27, 0x22},
+     GROWTH_MEDIUM_SLOW},
+    {"Nidoran\201",
+     &I_nidoranm,
+     0x03,
+     0x2E,
+     0x39,
+     0x28,
+     0x32,
+     0x28,
+     {0x03, 0x03},
+     {0x2B, 0x21, 0x00, 0x00},
+     GROWTH_MEDIUM_SLOW},
+    {"Nidorino",
+     &I_nidorino,
+     0xA7,
+     0x3D,
+     0x48,
+     0x39,
+     0x41,
+     0x37,
+     {0x03, 0x03},
+     {0x2B, 0x21, 0x1E, 0x00},
+     GROWTH_MEDIUM_SLOW},
+    {"Nidoking",
+     &I_nidoking,
+     0x07,
+     0x51,
+     0x5C,
+     0x4D,
+     0x55,
+     0x4B,
+     {0x03, 0x04},
+     {0x21, 0x1E, 0x28, 0x25},
+     GROWTH_MEDIUM_SLOW},
+    {"Clefairy",
+     &I_clefairy,
+     0x04,
+     0x46,
+     0x2D,
+     0x30,
+     0x23,
+     0x3C,
+     {0x00, 0x00},
+     {0x01, 0x2D, 0x00, 0x00},
+     GROWTH_FAST},
+    {"Clefable",
+     &I_clefable,
+     0x8E,
+     0x5F,
+     0x46,
+     0x49,
+     0x3C,
+     0x55,
+     {0x00, 0x00},
+     {0x2F, 0x03, 0x6B, 0x76},
+     GROWTH_FAST},
+    {"Vulpix",
+     &I_vulpix,
+     0x52,
+     0x26,
+     0x29,
+     0x28,
+     0x41,
+     0x41,
+     {0x14, 0x14},
+     {0x34, 0x27, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST},
+    {"Ninetales",
+     &I_ninetales,
+     0x53,
+     0x49,
+     0x4C,
+     0x4B,
+     0x64,
+     0x64,
+     {0x14, 0x14},
+     {0x34, 0x27, 0x62, 0x2E},
+     GROWTH_MEDIUM_FAST},
+    {"Jigglypuff",
+     &I_jigglypuff,
+     0x64,
+     0x73,
+     0x2D,
+     0x14,
+     0x14,
+     0x19,
+     {0x00, 0x00},
+     {0x2F, 0x00, 0x00, 0x00},
+     GROWTH_FAST},
+    {"Wigglytuff",
+     &I_wigglytuff,
+     0x65,
+     0x8C,
+     0x46,
+     0x2D,
+     0x2D,
+     0x32,
+     {0x00, 0x00},
+     {0x2F, 0x32, 0x6F, 0x03},
+     GROWTH_FAST},
+    {"Zubat",
+     &I_zubat,
+     0x6B,
+     0x28,
+     0x2D,
+     0x23,
+     0x37,
+     0x28,
+     {0x03, 0x02},
+     {0x8D, 0x00, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST},
+    {"Golbat",
+     &I_golbat,
+     0x82,
+     0x4B,
+     0x50,
+     0x46,
+     0x5A,
+     0x4B,
+     {0x03, 0x02},
+     {0x8D, 0x67, 0x2C, 0x00},
+     GROWTH_MEDIUM_FAST},
+    {"Oddish",
+     &I_oddish,
+     0xB9,
+     0x2D,
+     0x32,
+     0x37,
+     0x1E,
+     0x4B,
+     {0x16, 0x03},
+     {0x47, 0x00, 0x00, 0x00},
+     GROWTH_MEDIUM_SLOW},
+    {"Gloom",
+     &I_gloom,
+     0xBA,
+     0x3C,
+     0x41,
+     0x46,
+     0x28,
+     0x55,
+     {0x16, 0x03},
+     {0x47, 0x4D, 0x4E, 0x00},
+     GROWTH_MEDIUM_SLOW},
+    {"Vileplume",
+     &I_vileplume,
+     0xBB,
+     0x4B,
+     0x50,
+     0x55,
+     0x32,
+     0x64,
+     {0x16, 0x03},
+     {0x4E, 0x4F, 0x33, 0x50},
+     GROWTH_MEDIUM_SLOW},
+    {"Paras",
+     &I_paras,
+     0x6D,
+     0x23,
+     0x46,
+     0x37,
+     0x19,
+     0x37,
+     {0x07, 0x16},
+     {0x0A, 0x00, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST},
+    {"Parasect",
+     &I_parasect,
+     0x2E,
+     0x3C,
+     0x5F,
+     0x50,
+     0x1E,
+     0x50,
+     {0x07, 0x16},
+     {0x0A, 0x4E, 0x8D, 0x00},
+     GROWTH_MEDIUM_FAST},
+    {"Venonat",
+     &I_venonat,
+     0x41,
+     0x3C,
+     0x37,
+     0x32,
+     0x2D,
+     0x28,
+     {0x07, 0x03},
+     {0x21, 0x32, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST},
+    {"Venomoth",
+     &I_venomoth,
+     0x77,
+     0x46,
+     0x41,
+     0x3C,
+     0x5A,
+     0x5A,
+     {0x07, 0x03},
+     {0x21, 0x32, 0x4D, 0x8D},
+     GROWTH_MEDIUM_FAST},
+    {"Diglett",
+     &I_diglett,
+     0x3B,
+     0x0A,
+     0x37,
+     0x19,
+     0x5F,
+     0x2D,
+     {0x04, 0x04},
+     {0x0A, 0x00, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST},
+    {"Dugtrio",
+     &I_dugtrio,
+     0x76,
+     0x23,
+     0x50,
+     0x32,
+     0x78,
+     0x46,
+     {0x04, 0x04},
+     {0x0A, 0x2D, 0x5B, 0x00},
+     GROWTH_MEDIUM_FAST},
+    {"Meowth",
+     &I_meowth,
+     0x4D,
+     0x28,
+     0x2D,
+     0x23,
+     0x5A,
+     0x28,
+     {0x00, 0x00},
+     {0x0A, 0x2D, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST},
+    {"Persian",
+     &I_persian,
+     0x90,
+     0x41,
+     0x46,
+     0x3C,
+     0x73,
+     0x41,
+     {0x00, 0x00},
+     {0x0A, 0x2D, 0x2C, 0x67},
+     GROWTH_MEDIUM_FAST},
+    {"Psyduck",
+     &I_psyduck,
+     0x2F,
+     0x32,
+     0x34,
+     0x30,
+     0x37,
+     0x32,
+     {0x15, 0x15},
+     {0x0A, 0x00, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST},
+    {"Golduck",
+     &I_golduck,
+     0x80,
+     0x50,
+     0x52,
+     0x4E,
+     0x55,
+     0x50,
+     {0x15, 0x15},
+     {0x0A, 0x27, 0x32, 0x00},
+     GROWTH_MEDIUM_FAST},
+    {"Mankey",
+     &I_mankey,
+     0x39,
+     0x28,
+     0x50,
+     0x23,
+     0x46,
+     0x23,
+     {0x01, 0x01},
+     {0x0A, 0x2B, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST},
+    {"Primeape",
+     &I_primeape,
+     0x75,
+     0x41,
+     0x69,
+     0x3C,
+     0x5F,
+     0x3C,
+     {0x01, 0x01},
+     {0x0A, 0x2B, 0x02, 0x9A},
+     GROWTH_MEDIUM_FAST},
+    {"Growlithe",
+     &I_growlithe,
+     0x21,
+     0x37,
+     0x46,
+     0x2D,
+     0x3C,
+     0x32,
+     {0x14, 0x14},
+     {0x2C, 0x2E, 0x00, 0x00},
+     GROWTH_SLOW},
+    {"Arcanine",
+     &I_arcanine,
+     0x14,
+     0x5A,
+     0x6E,
+     0x50,
+     0x5F,
+     0x50,
+     {0x14, 0x14},
+     {0x2E, 0x34, 0x2B, 0x24},
+     GROWTH_SLOW},
+    {"Poliwag",
+     &I_poliwag,
+     0x47,
+     0x28,
+     0x32,
+     0x28,
+     0x5A,
+     0x28,
+     {0x15, 0x15},
+     {0x91, 0x00, 0x00, 0x00},
+     GROWTH_MEDIUM_SLOW},
+    {"Poliwhirl",
+     &I_poliwhirl,
+     0x6E,
+     0x41,
+     0x41,
+     0x41,
+     0x5A,
+     0x32,
+     {0x15, 0x15},
+     {0x91, 0x5F, 0x37, 0x00},
+     GROWTH_MEDIUM_SLOW},
+    {"Poliwrath",
+     &I_poliwrath,
+     0x6F,
+     0x5A,
+     0x55,
+     0x5F,
+     0x46,
+     0x46,
+     {0x15, 0x01},
+     {0x5F, 0x37, 0x03, 0x22},
+     GROWTH_MEDIUM_SLOW},
+    {"Abra",
+     &I_abra,
+     0x94,
+     0x19,
+     0x14,
+     0x0F,
+     0x5A,
+     0x69,
+     {0x18, 0x18},
+     {0x64, 0x00, 0x00, 0x00},
+     GROWTH_MEDIUM_SLOW},
+    {"Kadabra",
+     &I_kadabra,
+     0x26,
+     0x28,
+     0x23,
+     0x1E,
+     0x69,
+     0x78,
+     {0x18, 0x18},
+     {0x64, 0x5D, 0x32, 0x00},
+     GROWTH_MEDIUM_SLOW},
+    {"Alakazam",
+     &I_alakazam,
+     0x95,
+     0x37,
+     0x32,
+     0x2D,
+     0x78,
+     0x87,
+     {0x18, 0x18},
+     {0x64, 0x5D, 0x32, 0x00},
+     GROWTH_MEDIUM_SLOW},
+    {"Machop",
+     &I_machop,
+     0x6A,
+     0x46,
+     0x50,
+     0x32,
+     0x23,
+     0x23,
+     {0x01, 0x01},
+     {0x02, 0x00, 0x00, 0x00},
+     GROWTH_MEDIUM_SLOW},
+    {"Machoke",
+     &I_machoke,
+     0x29,
+     0x50,
+     0x64,
+     0x46,
+     0x2D,
+     0x32,
+     {0x01, 0x01},
+     {0x02, 0x43, 0x2B, 0x00},
+     GROWTH_MEDIUM_SLOW},
+    {"Machamp",
+     &I_machamp,
+     0x7E,
+     0x5A,
+     0x82,
+     0x50,
+     0x37,
+     0x41,
+     {0x01, 0x01},
+     {0x02, 0x43, 0x2B, 0x00},
+     GROWTH_MEDIUM_SLOW},
+    {"Bellsprout",
+     &I_bellsprout,
+     0xBC,
+     0x32,
+     0x4B,
+     0x23,
+     0x28,
+     0x46,
+     {0x16, 0x03},
+     {0x16, 0x4A, 0x00, 0x00},
+     GROWTH_MEDIUM_SLOW},
+    {"Weepinbell",
+     &I_weepinbell,
+     0xBD,
+     0x41,
+     0x5A,
+     0x32,
+     0x37,
+     0x55,
+     {0x16, 0x03},
+     {0x16, 0x4A, 0x23, 0x00},
+     GROWTH_MEDIUM_SLOW},
+    {"Victreebel",
+     &I_victreebel,
+     0xBE,
+     0x50,
+     0x69,
+     0x41,
+     0x46,
+     0x64,
+     {0x16, 0x03},
+     {0x4F, 0x4E, 0x33, 0x4B},
+     GROWTH_MEDIUM_SLOW},
+    {"Tentacool",
+     &I_tentacool,
+     0x18,
+     0x28,
+     0x28,
+     0x23,
+     0x46,
+     0x64,
+     {0x15, 0x03},
+     {0x33, 0x00, 0x00, 0x00},
+     GROWTH_SLOW},
+    {"Tentacruel",
+     &I_tentacruel,
+     0x9B,
+     0x50,
+     0x46,
+     0x41,
+     0x64,
+     0x78,
+     {0x15, 0x03},
+     {0x33, 0x30, 0x23, 0x00},
+     GROWTH_SLOW},
+    {"Geodude",
+     &I_geodude,
+     0xA9,
+     0x28,
+     0x50,
+     0x64,
+     0x14,
+     0x1E,
+     {0x05, 0x04},
+     {0x21, 0x00, 0x00, 0x00},
+     GROWTH_MEDIUM_SLOW},
+    {"Graveler",
+     &I_graveler,
+     0x27,
+     0x37,
+     0x5F,
+     0x73,
+     0x23,
+     0x2D,
+     {0x05, 0x04},
+     {0x21, 0x6F, 0x00, 0x00},
+     GROWTH_MEDIUM_SLOW},
+    {"Golem",
+     &I_golem,
+     0x31,
+     0x50,
+     0x6E,
+     0x82,
+     0x2D,
+     0x37,
+     {0x05, 0x04},
+     {0x21, 0x6F, 0x00, 0x00},
+     GROWTH_MEDIUM_SLOW},
+    {"Ponyta",
+     &I_ponyta,
+     0xA3,
+     0x32,
+     0x55,
+     0x37,
+     0x5A,
+     0x41,
+     {0x14, 0x14},
+     {0x34, 0x00, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST},
+    {"Rapidash",
+     &I_rapidash,
+     0xA4,
+     0x41,
+     0x64,
+     0x46,
+     0x69,
+     0x50,
+     {0x14, 0x14},
+     {0x34, 0x27, 0x17, 0x2D},
+     GROWTH_MEDIUM_FAST},
+    {"Slowpoke",
+     &I_slowpoke,
+     0x25,
+     0x5A,
+     0x41,
+     0x41,
+     0x0F,
+     0x28,
+     {0x15, 0x18},
+     {0x5D, 0x00, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST},
+    {"Slowbro",
+     &I_slowbro,
+     0x08,
+     0x5F,
+     0x4B,
+     0x6E,
+     0x1E,
+     0x50,
+     {0x15, 0x18},
+     {0x5D, 0x32, 0x1D, 0x00},
+     GROWTH_MEDIUM_FAST},
+    {"Magnemite",
+     &I_magnemite,
+     0xAD,
+     0x19,
+     0x23,
+     0x46,
+     0x2D,
+     0x5F,
+     {0x17, 0x17},
+     {0x21, 0x00, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST},
+    {"Magneton",
+     &I_magneton,
+     0x36,
+     0x32,
+     0x3C,
+     0x5F,
+     0x46,
+     0x78,
+     {0x17, 0x17},
+     {0x21, 0x31, 0x54, 0x00},
+     GROWTH_MEDIUM_FAST},
+    {"Farfetch'd",
+     &I_farfetchd,
+     0x40,
+     0x34,
+     0x41,
+     0x37,
+     0x3C,
+     0x3A,
+     {0x00, 0x02},
+     {0x40, 0x1C, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST},
+    {"Doduo",
+     &I_doduo,
+     0x46,
+     0x23,
+     0x55,
+     0x2D,
+     0x4B,
+     0x23,
+     {0x00, 0x02},
+     {0x40, 0x00, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST},
+    {"Dodrio",
+     &I_dodrio,
+     0x74,
+     0x3C,
+     0x6E,
+     0x46,
+     0x64,
+     0x3C,
+     {0x00, 0x02},
+     {0x40, 0x2D, 0x1F, 0x00},
+     GROWTH_MEDIUM_FAST},
+    {"Seel",
+     &I_seel,
+     0x3A,
+     0x41,
+     0x2D,
+     0x37,
+     0x2D,
+     0x46,
+     {0x15, 0x15},
+     {0x1D, 0x00, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST},
+    {"Dewgong",
+     &I_dewgong,
+     0x78,
+     0x5A,
+     0x46,
+     0x50,
+     0x46,
+     0x5F,
+     {0x15, 0x19},
+     {0x1D, 0x2D, 0x3E, 0x00},
+     GROWTH_MEDIUM_FAST},
+    {"Grimer",
+     &I_grimer,
+     0x0D,
+     0x50,
+     0x50,
+     0x32,
+     0x19,
+     0x28,
+     {0x03, 0x03},
+     {0x01, 0x32, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST},
+    {"Muk",
+     &I_muk,
+     0x88,
+     0x69,
+     0x69,
+     0x4B,
+     0x32,
+     0x41,
+     {0x03, 0x03},
+     {0x01, 0x32, 0x8B, 0x00},
+     GROWTH_MEDIUM_FAST},
+    {"Shellder",
+     &I_shellder,
+     0x17,
+     0x1E,
+     0x41,
+     0x64,
+     0x28,
+     0x2D,
+     {0x15, 0x15},
+     {0x21, 0x6E, 0x00, 0x00},
+     GROWTH_SLOW},
+    {"Cloyster",
+     &I_cloyster,
+     0x8B,
+     0x32,
+     0x5F,
+     0xB4,
+     0x46,
+     0x55,
+     {0x15, 0x19},
+     {0x6E, 0x30, 0x80, 0x3E},
+     GROWTH_SLOW},
+    {"Gastly",
+     &I_gastly,
+     0x19,
+     0x1E,
+     0x23,
+     0x1E,
+     0x50,
+     0x64,
+     {0x08, 0x03},
+     {0x7A, 0x6D, 0x65, 0x00},
+     GROWTH_MEDIUM_SLOW},
+    {"Haunter",
+     &I_haunter,
+     0x93,
+     0x2D,
+     0x32,
+     0x2D,
+     0x5F,
+     0x73,
+     {0x08, 0x03},
+     {0x7A, 0x6D, 0x65, 0x00},
+     GROWTH_MEDIUM_SLOW},
+    {"Gengar",
+     &I_gengar,
+     0x0E,
+     0x3C,
+     0x41,
+     0x3C,
+     0x6E,
+     0x82,
+     {0x08, 0x03},
+     {0x7A, 0x6D, 0x65, 0x00},
+     GROWTH_MEDIUM_SLOW},
+    {"Onix",
+     &I_onix,
+     0x22,
+     0x23,
+     0x2D,
+     0xA0,
+     0x46,
+     0x1E,
+     {0x05, 0x04},
+     {0x21, 0x67, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST},
+    {"Drowzee",
+     &I_drowzee,
+     0x30,
+     0x3C,
+     0x30,
+     0x2D,
+     0x2A,
+     0x5A,
+     {0x18, 0x18},
+     {0x01, 0x5F, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST},
+    {"Hypno",
+     &I_hypno,
+     0x81,
+     0x55,
+     0x49,
+     0x46,
+     0x43,
+     0x73,
+     {0x18, 0x18},
+     {0x01, 0x5F, 0x32, 0x5D},
+     GROWTH_MEDIUM_FAST},
+    {"Krabby",
+     &I_krabby,
+     0x4E,
+     0x1E,
+     0x69,
+     0x5A,
+     0x32,
+     0x19,
+     {0x15, 0x15},
+     {0x91, 0x2B, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST},
+    {"Kingler",
+     &I_kingler,
+     0x8A,
+     0x37,
+     0x82,
+     0x73,
+     0x4B,
+     0x32,
+     {0x15, 0x15},
+     {0x91, 0x2B, 0x0B, 0x00},
+     GROWTH_MEDIUM_FAST},
+    {"Voltorb",
+     &I_voltorb,
+     0x06,
+     0x28,
+     0x1E,
+     0x32,
+     0x64,
+     0x37,
+     {0x17, 0x17},
+     {0x21, 0x67, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST},
+    {"Electrode",
+     &I_electrode,
+     0x8D,
+     0x3C,
+     0x32,
+     0x46,
+     0x8C,
+     0x50,
+     {0x17, 0x17},
+     {0x21, 0x67, 0x31, 0x00},
+     GROWTH_MEDIUM_FAST},
+    {"Exeggcute",
+     &I_exeggcute,
+     0x0C,
+     0x3C,
+     0x28,
+     0x50,
+     0x28,
+     0x3C,
+     {0x16, 0x18},
+     {0x8C, 0x5F, 0x00, 0x00},
+     GROWTH_SLOW},
+    {"Exeggutor",
+     &I_exeggutor,
+     0x0A,
+     0x5F,
+     0x5F,
+     0x55,
+     0x37,
+     0x7D,
+     {0x16, 0x18},
+     {0x8C, 0x5F, 0x00, 0x00},
+     GROWTH_SLOW},
+    {"Cubone",
+     &I_cubone,
+     0x11,
+     0x32,
+     0x32,
+     0x5F,
+     0x23,
+     0x28,
+     {0x04, 0x04},
+     {0x7D, 0x2D, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST},
+    {"Marowak",
+     &I_marowak,
+     0x91,
+     0x3C,
+     0x50,
+     0x6E,
+     0x2D,
+     0x32,
+     {0x04, 0x04},
+     {0x7D, 0x2D, 0x2B, 0x74},
+     GROWTH_MEDIUM_FAST},
+    {"Hitmonlee",
+     &I_hitmonlee,
+     0x2B,
+     0x32,
+     0x78,
+     0x35,
+     0x57,
+     0x23,
+     {0x01, 0x01},
+     {0x18, 0x60, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST},
+    {"Hitmonchan",
+     &I_hitmonchan,
+     0x2C,
+     0x32,
+     0x69,
+     0x4F,
+     0x4C,
+     0x23,
+     {0x01, 0x01},
+     {0x04, 0x61, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST},
+    {"Lickitung",
+     &I_lickitung,
+     0x0B,
+     0x5A,
+     0x37,
+     0x4B,
+     0x1E,
+     0x3C,
+     {0x00, 0x00},
+     {0x23, 0x30, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST},
+    {"Koffing",
+     &I_koffing,
+     0x37,
+     0x28,
+     0x41,
+     0x5F,
+     0x23,
+     0x3C,
+     {0x03, 0x03},
+     {0x21, 0x7B, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST},
+    {"Weezing",
+     &I_weezing,
+     0x8F,
+     0x41,
+     0x5A,
+     0x78,
+     0x3C,
+     0x55,
+     {0x03, 0x03},
+     {0x21, 0x7B, 0x7C, 0x00},
+     GROWTH_MEDIUM_FAST},
+    {"Rhyhorn",
+     &I_rhyhorn,
+     0x12,
+     0x50,
+     0x55,
+     0x5F,
+     0x19,
+     0x1E,
+     {0x04, 0x05},
+     {0x1E, 0x00, 0x00, 0x00},
+     GROWTH_SLOW},
+    {"Rhydon",
+     &I_rhydon,
+     0x01,
+     0x69,
+     0x82,
+     0x78,
+     0x28,
+     0x2D,
+     {0x04, 0x05},
+     {0x1E, 0x17, 0x27, 0x1F},
+     GROWTH_SLOW},
+    {"Chansey",
+     &I_chansey,
+     0x28,
+     0xFA,
+     0x05,
+     0x05,
+     0x32,
+     0x69,
+     {0x00, 0x00},
+     {0x01, 0x03, 0x00, 0x00},
+     GROWTH_FAST},
+    {"Tangela",
+     &I_tangela,
+     0x1E,
+     0x41,
+     0x37,
+     0x73,
+     0x3C,
+     0x64,
+     {0x16, 0x16},
+     {0x84, 0x14, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST},
+    {"Kangaskhan",
+     &I_kangaskhan,
+     0x02,
+     0x69,
+     0x5F,
+     0x50,
+     0x5A,
+     0x28,
+     {0x00, 0x00},
+     {0x04, 0x63, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST},
+    {"Horsea",
+     &I_horsea,
+     0x5C,
+     0x1E,
+     0x28,
+     0x46,
+     0x3C,
+     0x46,
+     {0x15, 0x15},
+     {0x91, 0x00, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST},
+    {"Seadra",
+     &I_seadra,
+     0x5D,
+     0x37,
+     0x41,
+     0x5F,
+     0x55,
+     0x5F,
+     {0x15, 0x15},
+     {0x91, 0x6C, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST},
+    {"Goldeen",
+     &I_goldeen,
+     0x9D,
+     0x2D,
+     0x43,
+     0x3C,
+     0x3F,
+     0x32,
+     {0x15, 0x15},
+     {0x40, 0x27, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST},
+    {"Seaking",
+     &I_seaking,
+     0x9E,
+     0x50,
+     0x5C,
+     0x41,
+     0x44,
+     0x50,
+     {0x15, 0x15},
+     {0x40, 0x27, 0x30, 0x00},
+     GROWTH_MEDIUM_FAST},
+    {"Staryu",
+     &I_staryu,
+     0x1B,
+     0x1E,
+     0x2D,
+     0x37,
+     0x55,
+     0x46,
+     {0x15, 0x15},
+     {0x21, 0x00, 0x00, 0x00},
+     GROWTH_SLOW},
+    {"Starmie",
+     &I_starmie,
+     0x98,
+     0x3C,
+     0x4B,
+     0x55,
+     0x73,
+     0x64,
+     {0x15, 0x18},
+     {0x21, 0x37, 0x6A, 0x00},
+     GROWTH_SLOW},
+    {"Mr.Mime",
+     &I_mr_mime,
+     0x2A,
+     0x28,
+     0x2D,
+     0x41,
+     0x5A,
+     0x64,
+     {0x18, 0x18},
+     {0x5D, 0x70, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST},
+    {"Scyther",
+     &I_scyther,
+     0x1A,
+     0x46,
+     0x6E,
+     0x50,
+     0x69,
+     0x37,
+     {0x07, 0x02},
+     {0x62, 0x00, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST},
+    {"Jynx",
+     &I_jynx,
+     0x48,
+     0x41,
+     0x32,
+     0x23,
+     0x5F,
+     0x5F,
+     {0x19, 0x18},
+     {0x01, 0x8E, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST},
+    {"Electabuzz",
+     &I_electabuzz,
+     0x35,
+     0x41,
+     0x53,
+     0x39,
+     0x69,
+     0x55,
+     {0x17, 0x17},
+     {0x62, 0x2B, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST},
+    {"Magmar",
+     &I_magmar,
+     0x33,
+     0x41,
+     0x5F,
+     0x39,
+     0x5D,
+     0x55,
+     {0x14, 0x14},
+     {0x34, 0x00, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST},
+    {"Pinsir",
+     &I_pinsir,
+     0x1D,
+     0x41,
+     0x7D,
+     0x64,
+     0x55,
+     0x37,
+     {0x07, 0x07},
+     {0x0B, 0x00, 0x00, 0x00},
+     GROWTH_SLOW},
+    {"Tauros",
+     &I_tauros,
+     0x3C,
+     0x4B,
+     0x64,
+     0x5F,
+     0x6E,
+     0x46,
+     {0x00, 0x00},
+     {0x21, 0x00, 0x00, 0x00},
+     GROWTH_SLOW},
+    {"Magikarp",
+     &I_magikarp,
+     0x85,
+     0x14,
+     0x0A,
+     0x37,
+     0x50,
+     0x14,
+     {0x15, 0x15},
+     {0x96, 0x00, 0x00, 0x00},
+     GROWTH_SLOW},
+    {"Gyarados",
+     &I_gyarados,
+     0x16,
+     0x5F,
+     0x7D,
+     0x4F,
+     0x51,
+     0x64,
+     {0x15, 0x02},
+     {0x2C, 0x52, 0x2B, 0x38},
+     GROWTH_SLOW},
+    {"Lapras",
+     &I_lapras,
+     0x13,
+     0x82,
+     0x55,
+     0x50,
+     0x3C,
+     0x5F,
+     {0x15, 0x19},
+     {0x37, 0x2D, 0x00, 0x00},
+     GROWTH_SLOW},
+    {"Ditto",
+     &I_ditto,
+     0x4C,
+     0x30,
+     0x30,
+     0x30,
+     0x30,
+     0x30,
+     {0x00, 0x00},
+     {0x90, 0x00, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST},
+    {"Eevee",
+     &I_eevee,
+     0x66,
+     0x37,
+     0x37,
+     0x32,
+     0x37,
+     0x41,
+     {0x00, 0x00},
+     {0x21, 0x1C, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST},
+    {"Vaporeon",
+     &I_vaporeon,
+     0x69,
+     0x82,
+     0x41,
+     0x3C,
+     0x41,
+     0x6E,
+     {0x15, 0x15},
+     {0x21, 0x1C, 0x62, 0x37},
+     GROWTH_MEDIUM_FAST},
+    {"Jolteon",
+     &I_jolteon,
+     0x68,
+     0x41,
+     0x41,
+     0x3C,
+     0x82,
+     0x6E,
+     {0x17, 0x17},
+     {0x21, 0x1C, 0x62, 0x54},
+     GROWTH_MEDIUM_FAST},
+    {"Flareon",
+     &I_flareon,
+     0x67,
+     0x41,
+     0x82,
+     0x3C,
+     0x41,
+     0x6E,
+     {0x14, 0x14},
+     {0x21, 0x1C, 0x62, 0x34},
+     GROWTH_MEDIUM_FAST},
+    {"Porygon",
+     &I_porygon,
+     0xAA,
+     0x41,
+     0x3C,
+     0x46,
+     0x28,
+     0x4B,
+     {0x00, 0x00},
+     {0x21, 0x9F, 0xA0, 0x00},
+     GROWTH_MEDIUM_FAST},
+    {"Omanyte",
+     &I_omanyte,
+     0x62,
+     0x23,
+     0x28,
+     0x64,
+     0x23,
+     0x5A,
+     {0x05, 0x15},
+     {0x37, 0x6E, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST},
+    {"Omastar",
+     &I_omastar,
+     0x63,
+     0x46,
+     0x3C,
+     0x7D,
+     0x37,
+     0x73,
+     {0x05, 0x15},
+     {0x37, 0x6E, 0x1E, 0x00},
+     GROWTH_MEDIUM_FAST},
+    {"Kabuto",
+     &I_kabuto,
+     0x5A,
+     0x1E,
+     0x50,
+     0x5A,
+     0x37,
+     0x2D,
+     {0x05, 0x15},
+     {0x0A, 0x6A, 0x00, 0x00},
+     GROWTH_MEDIUM_FAST},
+    {"Kabutops",
+     &I_kabutops,
+     0x5B,
+     0x3C,
+     0x73,
+     0x69,
+     0x50,
+     0x46,
+     {0x05, 0x15},
+     {0x0A, 0x6A, 0x47, 0x00},
+     GROWTH_MEDIUM_FAST},
+    {"Aerodactyl",
+     &I_aerodactyl,
+     0xAB,
+     0x50,
+     0x69,
+     0x41,
+     0x82,
+     0x3C,
+     {0x05, 0x02},
+     {0x11, 0x61, 0x00, 0x00},
+     GROWTH_SLOW},
+    {"Snorlax",
+     &I_snorlax,
+     0x84,
+     0xA0,
+     0x6E,
+     0x41,
+     0x1E,
+     0x41,
+     {0x00, 0x00},
+     {0x1D, 0x85, 0x9C, 0x00},
+     GROWTH_SLOW},
+    {"Articuno",
+     &I_articuno,
+     0x4A,
+     0x5A,
+     0x55,
+     0x64,
+     0x55,
+     0x7D,
+     {0x19, 0x02},
+     {0x40, 0x3A, 0x00, 0x00},
+     GROWTH_SLOW},
+    {"Zapdos",
+     &I_zapdos,
+     0x4B,
+     0x5A,
+     0x5A,
+     0x55,
+     0x64,
+     0x7D,
+     {0x17, 0x02},
+     {0x54, 0x41, 0x00, 0x00},
+     GROWTH_SLOW},
+    {"Moltres",
+     &I_moltres,
+     0x49,
+     0x5A,
+     0x64,
+     0x5A,
+     0x5A,
+     0x7D,
+     {0x14, 0x02},
+     {0x40, 0x53, 0x00, 0x00},
+     GROWTH_SLOW},
+    {"Dratini",
+     &I_dratini,
+     0x58,
+     0x29,
+     0x40,
+     0x2D,
+     0x32,
+     0x32,
+     {0x1A, 0x1A},
+     {0x23, 0x2B, 0x00, 0x00},
+     GROWTH_SLOW},
+    {"Dragonair",
+     &I_dragonair,
+     0x59,
+     0x3D,
+     0x54,
+     0x41,
+     0x46,
+     0x46,
+     {0x1A, 0x1A},
+     {0x23, 0x2B, 0x56, 0x00},
+     GROWTH_SLOW},
+    {"Dragonite",
+     &I_dragonite,
+     0x42,
+     0x5B,
+     0x86,
+     0x5F,
+     0x50,
+     0x64,
+     {0x1A, 0x02},
+     {0x23, 0x2B, 0x56, 0x61},
+     GROWTH_SLOW},
+    {"Mewtwo",
+     &I_mewtwo,
+     0x83,
+     0x6A,
+     0x6E,
+     0x5A,
+     0x82,
+     0x9A,
+     {0x18, 0x18},
+     {0x5D, 0x32, 0x81, 0x5E},
+     GROWTH_SLOW},
+    {"Mew",
+     &I_mew,
+     0x15,
+     0x64,
+     0x64,
+     0x64,
+     0x64,
+     0x64,
+     {0x18, 0x18},
+     {0x01, 0x00, 0x00, 0x00},
+     GROWTH_MEDIUM_SLOW},
+    {},
+};
+
+const NamedList move_list[] = {
+    {"No Move", 0x00},
+    {"Absorb", 0x47},
+    {"Acid Armor", 0x97},
+    {"Acid", 0x33},
+    {"Agility", 0x61},
+    {"Amnesia", 0x85},
+    {"Aurora Beam", 0x3E},
+    {"Barrage", 0x8C},
+    {"Barrier", 0x70},
+    {"Bide", 0x75},
+    {"Bind", 0x14},
+    {"Bite", 0x2C},
+    {"Blizzard", 0x3B},
+    {"Body Slam", 0x22},
+    {"Bone Club", 0x7D},
+    {"Boomerang", 0x9B},
+    {"Bubblebeam", 0x3D},
+    {"Bubble", 0x91},
+    {"Clamp", 0x80},
+    {"Comet Punch", 0x04},
+    {"Confuse Ray", 0x6D},
+    {"Confusion", 0x5D},
+    {"Constrict", 0x84},
+    {"Conversion", 0xA0},
+    {"Counter", 0x44},
+    {"Crabhammer", 0x98},
+    {"Cut", 0x0F},
+    {"Defense Curl", 0x6F},
+    {"Dig", 0x5B},
+    {"Disable", 0x32},
+    {"Dizzy Punch", 0x92},
+    {"Doubleslap", 0x03},
+    {"Double Kick", 0x18},
+    {"Double Team", 0x68},
+    {"Double-Edge", 0x26},
+    {"Dragon Rage", 0x52},
+    {"Dream Eater", 0x8A},
+    {"Drill Peck", 0x41},
+    {"Earthquake", 0x59},
+    {"Egg Bomb", 0x79},
+    {"Ember", 0x34},
+    {"Explosion", 0x99},
+    {"Fire Blast", 0x7E},
+    {"Fire Punch", 0x07},
+    {"Fire Spin", 0x53},
+    {"Fissure", 0x5A},
+    {"Flamethrower", 0x35},
+    {"Flash", 0x94},
+    {"Fly", 0x13},
+    {"Focus Energy", 0x74},
+    {"Fury Attack", 0x1F},
+    {"Fury Swipes", 0x9A},
+    {"Glare", 0x89},
+    {"Growl", 0x2D},
+    {"Growth", 0x4A},
+    {"Guillotine", 0x0C},
+    {"Gust", 0x10},
+    {"Harden", 0x6A},
+    {"Haze", 0x72},
+    {"Headbutt", 0x1D},
+    {"Hi Jump Kick", 0x88},
+    {"Horn Attack", 0x1E},
+    {"Horn Drill", 0x20},
+    {"Hydro Pump", 0x38},
+    {"Hyper Beam", 0x3F},
+    {"Hyper Fang", 0x9E},
+    {"Hypnosis", 0x5F},
+    {"Ice Beam", 0x3A},
+    {"Ice Punch", 0x08},
+    {"Jump Kick", 0x1A},
+    {"Karate Chop", 0x02},
+    {"Kinesis", 0x86},
+    {"Leech Life", 0x8D},
+    {"Leech Seed", 0x49},
+    {"Leer", 0x2B},
+    {"Lick", 0x7A},
+    {"Light Screen", 0x71},
+    {"Lovely Kiss", 0x8E},
+    {"Low Kick", 0x43},
+    {"Meditate", 0x60},
+    {"Mega Drain", 0x48},
+    {"Mega Kick", 0x19},
+    {"Mega Punch", 0x05},
+    {"Metronome", 0x76},
+    {"Mimic", 0x66},
+    {"Minimize", 0x6B},
+    {"Mirror Move", 0x77},
+    {"Mist", 0x36},
+    {"Night Shade", 0x65},
+    {"Pay Day", 0x06},
+    {"Peck", 0x40},
+    {"Petal Dance", 0x50},
+    {"Pin Missile", 0x2A},
+    {"Poisonpowder", 0x4D},
+    {"Poison Gas", 0x8B},
+    {"Poison Sting", 0x28},
+    {"Pound", 0x01},
+    {"Psybeam", 0x3C},
+    {"Psychic", 0x5E},
+    {"Psywave", 0x95},
+    {"Quick Attack", 0x62},
+    {"Rage", 0x63},
+    {"Razor Leaf", 0x4B},
+    {"Razor Wind", 0x0D},
+    {"Recover", 0x69},
+    {"Reflect", 0x73},
+    {"Rest", 0x9C},
+    {"Roar", 0x2E},
+    {"Rock Slide", 0x9D},
+    {"Rock Throw", 0x58},
+    {"Rolling Kick", 0x1B},
+    {"Sand Attack", 0x1C},
+    {"Scratch", 0x0A},
+    {"Screech", 0x67},
+    {"Seismic Toss", 0x45},
+    {"Selfdestruct", 0x78},
+    {"Sharpen", 0x9F},
+    {"Sing", 0x2F},
+    {"Skull Bash", 0x82},
+    {"Sky Attack", 0x8F},
+    {"Slam", 0x15},
+    {"Slash", 0xA3},
+    {"Sleep Powder", 0x4F},
+    {"Sludge", 0x7C},
+    {"Smog", 0x7B},
+    {"Smokescreen", 0x6C},
+    {"Softboiled", 0x87},
+    {"Solar Beam", 0x4C},
+    {"Sonicboom", 0x31},
+    {"Spike Cannon", 0x83},
+    {"Splash", 0x96},
+    {"Spore", 0x93},
+    {"Stomp", 0x17},
+    {"Strength", 0x46},
+    {"String Shot", 0x51},
+    {"Struggle", 0xA5},
+    {"Stun Spore", 0x4E},
+    {"Submission", 0x42},
+    {"Substitute", 0xA4},
+    {"Supersonic", 0x30},
+    {"Super Fang", 0xA2},
+    {"Surf", 0x39},
+    {"Swift", 0x81},
+    {"Swords Dance", 0x0E},
+    {"Tackle", 0x21},
+    {"Tail Whip", 0x27},
+    {"Take Down", 0x24},
+    {"Teleport", 0x64},
+    {"Thrash", 0x25},
+    {"Thunderbolt", 0x55},
+    {"Thunderpunch", 0x09},
+    {"Thundershock", 0x54},
+    {"Thunder Wave", 0x56},
+    {"Thunder", 0x57},
+    {"Toxic", 0x5C},
+    {"Transform", 0x90},
+    {"Tri Attack", 0xA1},
+    {"Twineedle", 0x29},
+    {"Vicegrip", 0x0B},
+    {"Vine Whip", 0x16},
+    {"Waterfall", 0x7F},
+    {"Water Gun", 0x37},
+    {"Whirlwind", 0x12},
+    {"Wing Attack", 0x11},
+    {"Withdraw", 0x6E},
+    {"Wrap", 0x23},
+    {},
+};
+
+const NamedList type_list[] = {
+    {"Bug", 0x07},
+    {"Dragon", 0x1A},
+    {"Electric", 0x17},
+    {"Fighting", 0x01},
+    {"Fire", 0x14},
+    {"Flying", 0x02},
+    {"Ghost", 0x08},
+    {"Grass", 0x16},
+    {"Ground", 0x04},
+    {"Ice", 0x19},
+    {"Normal", 0x00},
+    {"Poison", 0x03},
+    {"Psychic", 0x18},
+    {"Rock", 0x05},
+    {"Water", 0x15},
+    {},
+};
+
+int pokemon_named_list_get_num_elements(const NamedList* list) {
+    int i;
+
+    for(i = 0;; i++) {
+        if(list[i].name == NULL) return i;
+    }
+
+    /* XXX: Would be faster to do something like this, but, can't easily do
+     * that using the current pointers. Might be able to clean this up later?
+     */
+    //return sizeof(type_list)/sizeof(type_list[0]);
+}
+
+int pokemon_named_list_get_list_pos_from_index(const NamedList* list, uint8_t index) {
+    int i;
+
+    for(i = 0;; i++) {
+        if(list[i].name == NULL) break;
+        if(index == list[i].index) return i;
+    }
+
+    /* XXX: This will return the first entry in case index is not matched.
+     * Could be surprising at runtime.
+     */
+    return 0;
+}
+
+const char* pokemon_named_list_get_name_from_index(const NamedList* list, uint8_t index) {
+    int i;
+
+    for(i = 0;; i++) {
+        if(list[i].name == NULL) break;
+        if(index == list[i].index) return list[i].name;
+    }
+
+    /* XXX: This will return the first entry in the case index is not matched,
+     * this could be confusing/problematic at runtime.
+     */
+    return list[0].name;
+}
+
+/* If dest is not NULL, a copy of the default name is written to it as well */
+void pokemon_trade_block_set_default_name(char* dest, PokemonFap* pokemon_fap, size_t n) {
+    int i;
+    char buf[11];
+
+    /* Walk through the default name, toupper() each character, encode it, and
+     * then write that to the same position in the trade_block.
+     */
+    /* XXX: The limit of this is hard-coded to a length of 11 at most. This may
+     * be a problem down the road!
+     */
+    for(i = 0; i < 11; i++) {
+        pokemon_fap->trade_block->nickname[0].str[i] = pokemon_char_to_encoded(
+            toupper(pokemon_fap->pokemon_table[pokemon_fap->curr_pokemon].name[i]));
+        buf[i] = toupper(pokemon_fap->pokemon_table[pokemon_fap->curr_pokemon].name[i]);
+    }
+
+    if(dest != NULL) {
+        strncpy(dest, buf, n);
+    }
+}
+
+#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_trade_block_recalculate_stats_from_level(PokemonFap* pokemon_fap) {
+    struct pokemon_structure* pkmn = &pokemon_fap->trade_block->party[0];
+    const PokemonTable* table = &pokemon_fap->pokemon_table[pokemon_fap->curr_pokemon];
+    int curr_stats = pokemon_fap->curr_stats;
+    uint32_t experience;
+    int level = pkmn->level;
+    uint16_t stat;
+    uint8_t hp_iv = 0xf;
+    uint8_t atk_iv = 0xf;
+    uint8_t def_iv = 0xf;
+    uint8_t spd_iv = 0xf;
+    uint8_t special_iv = 0xf;
+
+    /* Calculate exp */
+    switch(table->growth) {
+    case GROWTH_FAST:
+        // https://bulbapedia.bulbagarden.net/wiki/Experience#Fast
+        experience = (4 * level * level * level) / 5;
+        break;
+    case GROWTH_MEDIUM_FAST:
+        // https://bulbapedia.bulbagarden.net/wiki/Experience#Medium_Fast
+        experience = (level * level * level);
+        break;
+    case GROWTH_MEDIUM_SLOW:
+        // https://bulbapedia.bulbagarden.net/wiki/Experience#Medium_Slow
+        experience =
+            (((level * level * level) * 6 / 5) - (15 * level * level) + (100 * level) - 140);
+        break;
+    case GROWTH_SLOW:
+        // https://bulbapedia.bulbagarden.net/wiki/Experience#Slow
+        experience = (5 * level * level * level) / 4;
+        break;
+    default:
+        furi_crash("incorrect growth val");
+        break;
+    }
+
+    pkmn->level_again = level;
+    UINT32_TO_EXP(experience, pkmn->exp);
+
+    /* Generate STATEXP */
+    switch(curr_stats) {
+    case 1:
+    case 4:
+        stat = (0xffff / 100) * level;
+        break;
+    case 2:
+    case 5:
+        stat = 0xffff;
+        break;
+    default:
+        stat = 0;
+        break;
+    }
+
+    stat = __builtin_bswap16(stat);
+
+    pkmn->hp_ev = stat;
+    pkmn->atk_ev = stat;
+    pkmn->def_ev = stat;
+    pkmn->spd_ev = stat;
+    pkmn->special_ev = stat;
+
+    /* Set up IVs */
+    if(curr_stats <= 2) {
+        atk_iv = rand() % 15;
+        def_iv = rand() % 15;
+        spd_iv = rand() % 15;
+        special_iv = rand() % 15;
+        pkmn->iv = ((atk_iv & 0x0f) << 12) | ((def_iv & 0x0f) << 8) | ((spd_iv & 0x0f) << 4) |
+                   ((special_iv & 0x0f));
+        hp_iv = (pkmn->iv & 0xAA) >> 4;
+    }
+
+    /* Calculate HP */
+    // https://bulbapedia.bulbagarden.net/wiki/Stat#Generations_I_and_II
+    stat = floor((((2 * (table->base_hp + hp_iv)) + floor(sqrt(pkmn->hp_ev) / 4)) * level) / 100) +
+           (level + 10);
+    pkmn->hp = __builtin_bswap16(stat);
+    pkmn->max_hp = pkmn->hp;
+
+    /* Calculate ATK, DEF, SPD, SP */
+    // https://bulbapedia.bulbagarden.net/wiki/Stat#Generations_I_and_II
+    stat =
+        floor((((2 * (table->base_atk + atk_iv)) + floor(sqrt(pkmn->atk_ev) / 4)) * level) / 100) +
+        5;
+    pkmn->atk = __builtin_bswap16(stat);
+    stat =
+        floor((((2 * (table->base_def + def_iv)) + floor(sqrt(pkmn->def_ev) / 4)) * level) / 100) +
+        5;
+    pkmn->def = __builtin_bswap16(stat);
+    stat =
+        floor((((2 * (table->base_spd + spd_iv)) + floor(sqrt(pkmn->spd_ev) / 4)) * level) / 100) +
+        5;
+    pkmn->spd = __builtin_bswap16(stat);
+    stat = floor(
+               (((2 * (table->base_special + special_iv)) + floor(sqrt(pkmn->special_ev) / 4)) *
+                level) /
+               100) +
+           5;
+    pkmn->special = __builtin_bswap16(stat);
+}
+
+/* Rebuild the current trade block's variables based on curr_pokemon */
+void pokemon_trade_block_recalculate(PokemonFap* pokemon_fap) {
+    struct pokemon_structure* pkmn = &pokemon_fap->trade_block->party[0];
+    const PokemonTable* table = &pokemon_fap->pokemon_table[pokemon_fap->curr_pokemon];
+    int i;
+
+    /* Set current pokemon to the trade structure */
+    pkmn->index = table->index;
+    pokemon_fap->trade_block->party_members[0] = table->index;
+
+    /* Set current pokemon's moves to the trade structure */
+    for(i = 0; i < 4; i++) {
+        pkmn->move[i] = table->move[i];
+    }
+
+    /* Set current pokemon's types to the trade structure */
+    for(i = 0; i < 2; i++) {
+        pkmn->type[i] = table->type[i];
+    }
+
+    pokemon_trade_block_recalculate_stats_from_level(pokemon_fap);
+    pokemon_trade_block_set_default_name(NULL, pokemon_fap, 0);
+}
+
+/* Allocates a chunk of memory for the trade data block and sets up some
+ * default values.
+ */
+static TradeBlock* trade_block_alloc(void) {
+    TradeBlock* trade;
+
+    trade = malloc(sizeof(TradeBlock));
+
+    /* Clear struct to be all TERM_ bytes as the various name strings need this */
+    memset(trade, TERM_, sizeof(TradeBlock));
+
+    /* The party_members element needs to be 0xff for unused */
+    memset(trade->party_members, 0xFF, sizeof(trade->party_members));
+
+    /* Zero the main party data, TERM_ in there can cause weirdness */
+    memset(trade->party, 0x00, sizeof(trade->party));
+
+    /* Set our Name, the pokemon's default OT name and ID */
+    trade->party_cnt = 1;
+
+    /* Trainer/OT name, not to exceed 7 characters! */
+    pokemon_str_to_encoded_array(trade->trainer_name, "Flipper", sizeof(trade->trainer_name));
+    pokemon_str_to_encoded_array(trade->ot_name[0].str, "Flipper", sizeof(trade->ot_name[0].str));
+
+    /* OT trainer ID# */
+    trade->party[0].ot_id = __builtin_bswap16(42069);
+
+    /* XXX: move pp isn't explicitly set up, should be fine */
+    /* XXX: catch/held isn't explicitly set up, should be okay for only Gen I support now */
+    /* XXX: Status condition isn't explicity let up, would you ever want to? */
+
+    /* Set up initial level */
+    trade->party[0].level = 2;
+
+    return trade;
+}
+
+static void trade_block_free(TradeBlock* trade) {
+    free(trade);
+}
+
+PokemonFap* pokemon_alloc() {
+    PokemonFap* pokemon_fap = (PokemonFap*)malloc(sizeof(PokemonFap));
+
+    // View dispatcher
+    pokemon_fap->view_dispatcher = view_dispatcher_alloc();
+
+    view_dispatcher_enable_queue(pokemon_fap->view_dispatcher);
+    view_dispatcher_set_event_callback_context(pokemon_fap->view_dispatcher, pokemon_fap);
+    view_dispatcher_attach_to_gui(
+        pokemon_fap->view_dispatcher,
+        (Gui*)furi_record_open(RECORD_GUI),
+        ViewDispatcherTypeFullscreen);
+
+    /* Set up pointers to const data tables for reference elsewhere */
+    pokemon_fap->pokemon_table = pokemon_table;
+    pokemon_fap->move_list = move_list;
+    pokemon_fap->type_list = type_list;
+
+    // Set up defaults
+    pokemon_fap->curr_pokemon = 0;
+    pokemon_fap->curr_stats = 0;
+
+    // Set up trade party struct
+    pokemon_fap->trade_block = trade_block_alloc();
+
+    /* Update trade block struct with calculated details from initial values from trade_block_alloc() */
+    pokemon_trade_block_recalculate(pokemon_fap);
+
+    /* Set up gui modules used. It would be nice if these could be allocated and
+     * freed as needed, however, the scene manager still requires pointers that
+     * get set up as a part of the scene. Therefore, individual scene's exit
+     * callbacks cannot free the buffer.
+     */
+    pokemon_fap->text_input = text_input_alloc();
+    pokemon_fap->submenu = submenu_alloc();
+    pokemon_fap->variable_item_list = variable_item_list_alloc();
+
+    // Set up menu scene
+    pokemon_fap->scene_manager = scene_manager_alloc(&pokemon_scene_manager_handlers, pokemon_fap);
+    view_dispatcher_add_view(
+        pokemon_fap->view_dispatcher, AppViewMainMenu, submenu_get_view(pokemon_fap->submenu));
+    scene_manager_next_scene(pokemon_fap->scene_manager, MainMenuScene);
+
+    // Select Pokemon View
+    pokemon_fap->select_view = select_pokemon_alloc(pokemon_fap);
+    view_dispatcher_add_view(
+        pokemon_fap->view_dispatcher, AppViewSelectPokemon, pokemon_fap->select_view);
+
+    // Trade View
+    pokemon_fap->trade_view = trade_alloc(pokemon_fap);
+    view_dispatcher_add_view(pokemon_fap->view_dispatcher, AppViewTrade, pokemon_fap->trade_view);
+
+    return pokemon_fap;
+}
+
+void free_app(PokemonFap* pokemon_fap) {
+    furi_assert(pokemon_fap);
+
+    // Free views
+    view_dispatcher_remove_view(pokemon_fap->view_dispatcher, AppViewSelectPokemon);
+    select_pokemon_free(pokemon_fap);
+
+    view_dispatcher_remove_view(pokemon_fap->view_dispatcher, AppViewTrade);
+    trade_free(pokemon_fap);
+
+    view_dispatcher_remove_view(pokemon_fap->view_dispatcher, AppViewMainMenu);
+
+    view_dispatcher_free(pokemon_fap->view_dispatcher);
+
+    // Free scenes
+    scene_manager_free(pokemon_fap->scene_manager);
+
+    // Free gui modules
+    submenu_free(pokemon_fap->submenu);
+    text_input_free(pokemon_fap->text_input);
+    variable_item_list_free(pokemon_fap->variable_item_list);
+
+    // Close records
+    furi_record_close(RECORD_GUI);
+
+    // Free trade block
+    trade_block_free(pokemon_fap->trade_block);
+
+    // Free rest
+    free(pokemon_fap);
+    pokemon_fap = NULL;
+}
+
+int32_t pokemon_app(void* p) {
+    UNUSED(p);
+    PokemonFap* pokemon_fap = pokemon_alloc();
+
+    furi_hal_light_set(LightRed, 0x00);
+    furi_hal_light_set(LightGreen, 0x00);
+    furi_hal_light_set(LightBlue, 0x00);
+
+    //switch view and run dispatcher
+    view_dispatcher_run(pokemon_fap->view_dispatcher);
+
+    // Free resources
+    free_app(pokemon_fap);
+
+    return 0;
+}

+ 0 - 323
pokemon_app.cpp

@@ -1,323 +0,0 @@
-#include "pokemon_app.h"
-
-struct pokemon_lut pokemon_table[] = {
-    {"Bulbasaur", &I_bulbasaur, 0x99, 0x16, 0x03, 1, 45, 49, 49, 45, 65},
-    {"Ivysaur", &I_ivysaur, 0x09, 0x16, 0x03, 1, 60, 62, 63, 60, 80},
-    {"Venusaur", &I_venusaur, 0x9A, 0x16, 0x03, 1, 80, 82, 83, 80, 100},
-    {"Charmander", &I_charmander, 0xB0, 0x14, 0xFF, 1, 39, 52, 43, 65, 50},
-    {"Charmeleon", &I_charmeleon, 0xB2, 0x14, 0xFF, 1, 58, 64, 58, 80, 65},
-    {"Charizard", &I_charizard, 0xB4, 0x14, 0x02, 1, 78, 84, 78, 100, 85},
-    {"Squirtle", &I_squirtle, 0xB1, 0x15, 0xFF, 1, 44, 48, 65, 43, 50},
-    {"Wartortle", &I_wartortle, 0xB3, 0x15, 0xFF, 1, 59, 63, 80, 58, 65},
-    {"Blastoise", &I_blastoise, 0x1C, 0x15, 0xFF, 1, 79, 83, 100, 78, 85},
-    {"Caterpie", &I_caterpie, 0x7B, 0x07, 0xFF, 2, 45, 30, 35, 45, 20},
-    {"Metapod", &I_metapod, 0x7C, 0x07, 0xFF, 2, 50, 20, 55, 30, 25},
-    {"Butterfree", &I_butterfree, 0x7D, 0x07, 0x02, 2, 60, 45, 50, 70, 80},
-    {"Weedle", &I_weedle, 0x70, 0x07, 0x03, 2, 40, 35, 30, 50, 20},
-    {"Kakuna", &I_kakuna, 0x71, 0x07, 0x03, 2, 45, 25, 50, 35, 25},
-    {"Beedrill", &I_beedrill, 0x72, 0x07, 0x03, 2, 65, 80, 40, 75, 45},
-    {"Pidgey", &I_pidgey, 0x24, 0x00, 0x02, 1, 40, 45, 40, 56, 35},
-    {"Pidgeotto", &I_pidgeotto, 0x96, 0x00, 0x02, 1, 63, 60, 55, 71, 50},
-    {"Pidgeot", &I_pidgeot, 0x97, 0x00, 0x02, 1, 83, 80, 75, 91, 70},
-    {"Rattata", &I_rattata, 0xA5, 0x00, 0xFF, 2, 30, 56, 35, 72, 25},
-    {"Raticate", &I_raticate, 0xA6, 0x00, 0xFF, 2, 55, 81, 60, 97, 50},
-    {"Spearow", &I_spearow, 0x05, 0x00, 0x02, 2, 40, 60, 30, 70, 31},
-    {"Fearow", &I_fearow, 0x23, 0x00, 0x02, 2, 65, 90, 65, 100, 61},
-    {"Ekans", &I_ekans, 0x6C, 0x03, 0xFF, 2, 35, 60, 44, 55, 40},
-    {"Arbok", &I_arbok, 0x2D, 0x03, 0xFF, 2, 60, 85, 69, 80, 65},
-    {"Pikachu", &I_pikachu, 0x54, 0x17, 0xFF, 2, 35, 55, 30, 90, 50},
-    {"Raichu", &I_raichu, 0x55, 0x17, 0xFF, 2, 60, 90, 55, 100, 90},
-    {"Sandshrew", &I_sandshrew, 0x60, 0x04, 0xFF, 2, 50, 75, 85, 40, 30},
-    {"Sandslash", &I_sandslash, 0x61, 0x04, 0xFF, 2, 75, 100, 110, 65, 55},
-    {"Nidoran@", &I_nidoranf, 0x0F, 0x03, 0xFF, 1, 55, 47, 52, 41, 40},
-    {"Nidorina", &I_nidorina, 0xA8, 0x03, 0xFF, 1, 70, 62, 67, 56, 55},
-    {"Nidoqueen", &I_nidoqueen, 0x10, 0x03, 0x04, 1, 90, 82, 87, 76, 75},
-    {"Nidoran!", &I_nidoranm, 0x03, 0x03, 0xFF, 1, 46, 57, 40, 50, 40},
-    {"Nidorino", &I_nidorino, 0xA7, 0x03, 0xFF, 1, 61, 72, 57, 65, 55},
-    {"Nidoking", &I_nidoking, 0x07, 0x03, 0x04, 1, 81, 92, 77, 85, 75},
-    {"Clefairy", &I_clefairy, 0x04, 0x00, 0xFF, 3, 70, 45, 48, 35, 60},
-    {"Clefable", &I_clefable, 0x8E, 0x00, 0xFF, 3, 95, 70, 73, 60, 85},
-    {"Vulpix", &I_vulpix, 0x52, 0x14, 0xFF, 2, 38, 41, 40, 65, 65},
-    {"Ninetales", &I_ninetales, 0x53, 0x14, 0xFF, 2, 73, 76, 75, 100, 100},
-    {"Jigglypuff", &I_jigglypuff, 0x64, 0x00, 0xFF, 3, 115, 45, 20, 20, 25},
-    {"Wigglytuff", &I_wigglytuff, 0x65, 0x00, 0xFF, 3, 140, 70, 45, 45, 50},
-    {"Zubat", &I_zubat, 0x6B, 0x03, 0x02, 2, 40, 45, 35, 55, 40},
-    {"Golbat", &I_golbat, 0x82, 0x03, 0x02, 2, 75, 80, 70, 90, 75},
-    {"Oddish", &I_oddish, 0xB9, 0x16, 0x03, 1, 45, 50, 55, 30, 75},
-    {"Gloom", &I_gloom, 0xBA, 0x16, 0x03, 1, 60, 65, 70, 40, 85},
-    {"Vileplume", &I_vileplume, 0xBB, 0x16, 0x03, 1, 75, 80, 85, 50, 100},
-    {"Paras", &I_paras, 0x6D, 0x07, 0x16, 2, 35, 70, 55, 25, 55},
-    {"Parasect", &I_parasect, 0x2E, 0x07, 0x16, 2, 60, 95, 80, 30, 80},
-    {"Venonat", &I_venonat, 0x41, 0x07, 0x03, 2, 60, 55, 50, 45, 40},
-    {"Venomoth", &I_venomoth, 0x77, 0x07, 0x03, 2, 70, 65, 60, 90, 90},
-    {"Diglett", &I_diglett, 0x3B, 0x04, 0xFF, 2, 10, 55, 25, 95, 45},
-    {"Dugtrio", &I_dugtrio, 0x76, 0x04, 0xFF, 2, 35, 80, 50, 120, 70},
-    {"Meowth", &I_meowth, 0x4D, 0x00, 0xFF, 2, 40, 45, 35, 90, 40},
-    {"Persian", &I_persian, 0x90, 0x00, 0xFF, 2, 65, 70, 60, 115, 65},
-    {"Psyduck", &I_psyduck, 0x2F, 0x15, 0xFF, 2, 50, 52, 48, 55, 50},
-    {"Golduck", &I_golduck, 0x80, 0x15, 0xFF, 2, 80, 82, 78, 85, 80},
-    {"Mankey", &I_mankey, 0x39, 0x01, 0xFF, 2, 40, 80, 35, 70, 35},
-    {"Primeape", &I_primeape, 0x75, 0x01, 0xFF, 2, 65, 105, 60, 95, 60},
-    {"Growlithe", &I_growlithe, 0x21, 0x14, 0xFF, 0, 55, 70, 45, 60, 50},
-    {"Arcanine", &I_arcanine, 0x14, 0x14, 0xFF, 0, 90, 110, 80, 95, 80},
-    {"Poliwag", &I_poliwag, 0x47, 0x15, 0xFF, 1, 40, 50, 40, 90, 40},
-    {"Poliwhirl", &I_poliwhirl, 0x6E, 0x15, 0xFF, 1, 65, 65, 65, 90, 50},
-    {"Poliwrath", &I_poliwrath, 0x6F, 0x15, 0x01, 1, 90, 85, 95, 70, 70},
-    {"Abra", &I_abra, 0x94, 0x18, 0xFF, 1, 25, 20, 15, 90, 105},
-    {"Kadabra", &I_kadabra, 0x26, 0x18, 0xFF, 1, 40, 35, 30, 105, 120},
-    {"Alakazam", &I_alakazam, 0x95, 0x18, 0xFF, 1, 55, 50, 45, 120, 135},
-    {"Machop", &I_machop, 0x6A, 0x01, 0xFF, 1, 70, 80, 50, 35, 35},
-    {"Machoke", &I_machoke, 0x29, 0x01, 0xFF, 1, 80, 100, 70, 45, 50},
-    {"Machamp", &I_machamp, 0x7E, 0x01, 0xFF, 1, 90, 130, 80, 55, 65},
-    {"Bellsprout", &I_bellsprout, 0xBC, 0x16, 0x03, 1, 50, 75, 35, 40, 70},
-    {"Weepinbell", &I_weepinbell, 0xBD, 0x16, 0x03, 1, 65, 90, 50, 55, 85},
-    {"Victreebel", &I_victreebel, 0xBE, 0x16, 0x03, 1, 80, 105, 65, 70, 100},
-    {"Tentacool", &I_tentacool, 0x18, 0x15, 0x03, 0, 40, 40, 35, 70, 100},
-    {"Tentacruel", &I_tentacruel, 0x9B, 0x15, 0x03, 0, 80, 70, 65, 100, 120},
-    {"Geodude", &I_geodude, 0xA9, 0x05, 0x04, 1, 40, 80, 100, 20, 30},
-    {"Graveler", &I_graveler, 0x27, 0x05, 0x04, 1, 55, 95, 115, 35, 45},
-    {"Golem", &I_golem, 0x31, 0x05, 0x04, 1, 80, 110, 130, 45, 55},
-    {"Ponyta", &I_ponyta, 0xA3, 0x14, 0xFF, 2, 50, 85, 55, 90, 65},
-    {"Rapidash", &I_rapidash, 0xA4, 0x14, 0xFF, 2, 65, 100, 70, 105, 80},
-    {"Slowpoke", &I_slowpoke, 0x25, 0x15, 0x18, 2, 90, 65, 65, 15, 40},
-    {"Slowbro", &I_slowbro, 0x08, 0x15, 0x18, 2, 95, 75, 110, 30, 80},
-    {"Magnemite", &I_magnemite, 0xAD, 0x17, 0xFF, 2, 25, 35, 70, 45, 95},
-    {"Magneton", &I_magneton, 0x36, 0x17, 0xFF, 2, 50, 60, 95, 70, 120},
-    {"Farfetch'd", &I_farfetchd, 0x40, 0x00, 0x02, 2, 52, 65, 55, 60, 58},
-    {"Doduo", &I_doduo, 0x46, 0x00, 0x02, 2, 35, 85, 45, 75, 35},
-    {"Dodrio", &I_dodrio, 0x74, 0x00, 0x02, 2, 60, 110, 70, 100, 60},
-    {"Seel", &I_seel, 0x3A, 0x15, 0xFF, 2, 65, 45, 55, 45, 70},
-    {"Dewgong", &I_dewgong, 0x78, 0x15, 0x19, 2, 90, 70, 80, 70, 95},
-    {"Grimer", &I_grimer, 0x0D, 0x03, 0xFF, 2, 80, 80, 50, 25, 40},
-    {"Muk", &I_muk, 0x88, 0x03, 0xFF, 2, 105, 105, 75, 50, 65},
-    {"Shellder", &I_shellder, 0x17, 0x15, 0xFF, 0, 30, 65, 100, 40, 45},
-    {"Cloyster", &I_cloyster, 0x8B, 0x15, 0x19, 0, 50, 95, 180, 70, 85},
-    {"Gastly", &I_gastly, 0x19, 0x08, 0x03, 1, 30, 35, 30, 80, 100},
-    {"Haunter", &I_haunter, 0x93, 0x08, 0x03, 1, 45, 50, 45, 95, 115},
-    {"Gengar", &I_gengar, 0x0E, 0x08, 0x03, 1, 60, 65, 60, 110, 130},
-    {"Onix", &I_onix, 0x22, 0x05, 0x04, 2, 35, 45, 160, 70, 30},
-    {"Drowzee", &I_drowzee, 0x30, 0x18, 0xFF, 2, 60, 48, 45, 42, 90},
-    {"Hypno", &I_hypno, 0x81, 0x18, 0xFF, 2, 85, 73, 70, 67, 115},
-    {"Krabby", &I_krabby, 0x4E, 0x15, 0xFF, 2, 30, 105, 90, 50, 25},
-    {"Kingler", &I_kingler, 0x8A, 0x15, 0xFF, 2, 55, 130, 115, 75, 50},
-    {"Voltorb", &I_voltorb, 0x06, 0x17, 0xFF, 2, 40, 30, 50, 100, 55},
-    {"Electrode", &I_electrode, 0x8D, 0x17, 0xFF, 2, 60, 50, 70, 140, 80},
-    {"Exeggcute", &I_exeggcute, 0x0C, 0x16, 0x18, 0, 60, 40, 80, 40, 60},
-    {"Exeggutor", &I_exeggutor, 0x0A, 0x16, 0x18, 0, 95, 95, 85, 55, 125},
-    {"Cubone", &I_cubone, 0x11, 0x04, 0xFF, 2, 50, 50, 95, 35, 40},
-    {"Marowak", &I_marowak, 0x91, 0x04, 0xFF, 2, 60, 80, 110, 45, 50},
-    {"Hitmonlee", &I_hitmonlee, 0x2B, 0x01, 0xFF, 2, 50, 120, 53, 87, 35},
-    {"Hitmonchan", &I_hitmonchan, 0x2C, 0x01, 0xFF, 2, 50, 105, 79, 76, 35},
-    {"Lickitung", &I_lickitung, 0x0B, 0x00, 0xFF, 2, 90, 55, 75, 30, 60},
-    {"Koffing", &I_koffing, 0x37, 0x03, 0xFF, 2, 40, 65, 95, 35, 60},
-    {"Weezing", &I_weezing, 0x8F, 0x03, 0xFF, 2, 65, 90, 120, 60, 85},
-    {"Rhyhorn", &I_rhyhorn, 0x12, 0x04, 0x05, 0, 80, 85, 95, 25, 30},
-    {"Rhydon", &I_rhydon, 0x01, 0x04, 0x05, 0, 105, 130, 120, 40, 45},
-    {"Chansey", &I_chansey, 0x28, 0x00, 0xFF, 3, 250, 5, 5, 50, 105},
-    {"Tangela", &I_tangela, 0x1E, 0x16, 0xFF, 2, 65, 55, 115, 60, 100},
-    {"Kangaskhan", &I_kangaskhan, 0x02, 0x00, 0xFF, 2, 105, 95, 80, 90, 40},
-    {"Horsea", &I_horsea, 0x5C, 0x15, 0xFF, 2, 30, 40, 70, 60, 70},
-    {"Seadra", &I_seadra, 0x5D, 0x15, 0xFF, 2, 55, 65, 95, 85, 95},
-    {"Goldeen", &I_goldeen, 0x9D, 0x15, 0xFF, 2, 45, 67, 60, 63, 50},
-    {"Seaking", &I_seaking, 0x9E, 0x15, 0xFF, 2, 80, 92, 65, 68, 80},
-    {"Staryu", &I_staryu, 0x1B, 0x15, 0xFF, 0, 30, 45, 55, 85, 70},
-    {"Starmie", &I_starmie, 0x98, 0x15, 0x18, 0, 60, 75, 85, 115, 100},
-    {"Mr. Mime", &I_mr_mime, 0x2A, 0x18, 0xFF, 2, 40, 45, 65, 90, 100},
-    {"Scyther", &I_scyther, 0x1A, 0x07, 0x02, 2, 70, 110, 80, 105, 55},
-    {"Jynx", &I_jynx, 0x48, 0x19, 0x18, 2, 65, 50, 35, 95, 95},
-    {"Electabuzz", &I_electabuzz, 0x35, 0x17, 0xFF, 2, 65, 83, 57, 105, 85},
-    {"Magmar", &I_magmar, 0x33, 0x14, 0xFF, 2, 65, 95, 57, 93, 85},
-    {"Pinsir", &I_pinsir, 0x1D, 0x07, 0xFF, 0, 65, 125, 100, 85, 55},
-    {"Tauros", &I_tauros, 0x3C, 0x00, 0xFF, 0, 75, 100, 95, 110, 70},
-    {"Magikarp", &I_magikarp, 0x85, 0x15, 0xFF, 0, 20, 10, 55, 80, 20},
-    {"Gyarados", &I_gyarados, 0x16, 0x15, 0x02, 0, 95, 125, 79, 81, 100},
-    {"Lapras", &I_lapras, 0x13, 0x15, 0x19, 0, 130, 85, 80, 60, 95},
-    {"Ditto", &I_ditto, 0x4C, 0x00, 0xFF, 2, 48, 48, 48, 48, 48},
-    {"Eevee", &I_eevee, 0x66, 0x00, 0xFF, 2, 55, 55, 50, 55, 65},
-    {"Vaporeon", &I_vaporeon, 0x69, 0x15, 0xFF, 2, 130, 65, 60, 65, 110},
-    {"Jolteon", &I_jolteon, 0x68, 0x17, 0xFF, 2, 65, 65, 60, 130, 110},
-    {"Flareon", &I_flareon, 0x67, 0x14, 0xFF, 2, 65, 130, 60, 65, 110},
-    {"Porygon", &I_porygon, 0xAA, 0x00, 0xFF, 2, 65, 60, 70, 40, 75},
-    {"Omanyte", &I_omanyte, 0x62, 0x05, 0x15, 2, 35, 40, 100, 35, 90},
-    {"Omastar", &I_omastar, 0x63, 0x05, 0x15, 2, 70, 60, 125, 55, 115},
-    {"Kabuto", &I_kabuto, 0x5A, 0x05, 0x15, 2, 30, 80, 90, 55, 45},
-    {"Kabutops", &I_kabutops, 0x5B, 0x05, 0x15, 2, 60, 115, 105, 80, 70},
-    {"Aerodactyl", &I_aerodactyl, 0xAB, 0x05, 0x02, 0, 80, 105, 65, 130, 60},
-    {"Snorlax", &I_snorlax, 0x84, 0x00, 0xFF, 0, 160, 110, 65, 30, 65},
-    {"Articuno", &I_articuno, 0x4A, 0x19, 0x02, 0, 90, 85, 100, 85, 125},
-    {"Zapdos", &I_zapdos, 0x4B, 0x17, 0x02, 0, 90, 90, 85, 100, 125},
-    {"Moltres", &I_moltres, 0x49, 0x14, 0x02, 0, 90, 100, 90, 90, 125},
-    {"Dratini", &I_dratini, 0x58, 0x1A, 0xFF, 0, 41, 64, 45, 50, 50},
-    {"Dragonair", &I_dragonair, 0x59, 0x1A, 0xFF, 0, 61, 84, 65, 70, 70},
-    {"Dragonite", &I_dragonite, 0x42, 0x1A, 0x02, 0, 91, 134, 95, 80, 100},
-    {"Mewtwo", &I_mewtwo, 0x83, 0x18, 0xFF, 0, 106, 110, 90, 130, 154},
-    {"Mew", &I_mew, 0x15, 0x18, 0xFF, 1, 100, 100, 100, 100, 100},
-    {},
-};
-
-struct pokemon_mv move_table[] = {
-    {"No Move", 0x00},      {"Absorb", 0x47},       {"Acid Armor", 0x97},   {"Acid", 0x33},
-    {"Agility", 0x61},      {"Amnesia", 0x85},      {"Aurora Beam", 0x3E},  {"Barrage", 0x8C},
-    {"Barrier", 0x70},      {"Bide", 0x75},         {"Bind", 0x14},         {"Bite", 0x2C},
-    {"Blizzard", 0x3B},     {"Body Slam", 0x22},    {"Bone Club", 0x7D},    {"Boomerang", 0x9B},
-    {"Bubblebeam", 0x3D},   {"Bubble", 0x91},       {"Clamp", 0x80},        {"Comet Punch", 0x04},
-    {"Confuse Ray", 0x6D},  {"Confusion", 0x5D},    {"Constrict", 0x84},    {"Conversion", 0xA0},
-    {"Counter", 0x44},      {"Crabhammer", 0x98},   {"Cut", 0x0F},          {"Defense Curl", 0x6F},
-    {"Dig", 0x5B},          {"Disable", 0x32},      {"Dizzy Punch", 0x92},  {"Doubleslap", 0x03},
-    {"Double Kick", 0x18},  {"Double Team", 0x68},  {"Double-Edge", 0x26},  {"Dragon Rage", 0x52},
-    {"Dream Eater", 0x8A},  {"Drill Peck", 0x41},   {"Earthquake", 0x59},   {"Egg Bomb", 0x79},
-    {"Ember", 0x34},        {"Explosion", 0x99},    {"Fire Blast", 0x7E},   {"Fire Punch", 0x07},
-    {"Fire Spin", 0x53},    {"Fissure", 0x5A},      {"Flamethrower", 0x35}, {"Flash", 0x94},
-    {"Fly", 0x13},          {"Focus Energy", 0x74}, {"Fury Attack", 0x1F},  {"Fury Swipes", 0x9A},
-    {"Glare", 0x89},        {"Growl", 0x2D},        {"Growth", 0x4A},       {"Guillotine", 0x0C},
-    {"Gust", 0x10},         {"Harden", 0x6A},       {"Haze", 0x72},         {"Headbutt", 0x1D},
-    {"Hi Jump Kick", 0x88}, {"Horn Attack", 0x1E},  {"Horn Drill", 0x20},   {"Hydro Pump", 0x38},
-    {"Hyper Beam", 0x3F},   {"Hyper Fang", 0x9E},   {"Hypnosis", 0x5F},     {"Ice Beam", 0x3A},
-    {"Ice Punch", 0x08},    {"Jump Kick", 0x1A},    {"Karate Chop", 0x02},  {"Kinesis", 0x86},
-    {"Leech Life", 0x8D},   {"Leech Seed", 0x49},   {"Leer", 0x2B},         {"Lick", 0x7A},
-    {"Light Screen", 0x71}, {"Lovely Kiss", 0x8E},  {"Low Kick", 0x43},     {"Meditate", 0x60},
-    {"Mega Drain", 0x48},   {"Mega Kick", 0x19},    {"Mega Punch", 0x05},   {"Metronome", 0x76},
-    {"Mimic", 0x66},        {"Minimize", 0x6B},     {"Mirror Move", 0x77},  {"Mist", 0x36},
-    {"Night Shade", 0x65},  {"Pay Day", 0x06},      {"Peck", 0x40},         {"Petal Dance", 0x50},
-    {"Pin Missile", 0x2A},  {"Poisonpowder", 0x4D}, {"Poison Gas", 0x8B},   {"Poison Sting", 0x28},
-    {"Pound", 0x01},        {"Psybeam", 0x3C},      {"Psychic", 0x5E},      {"Psywave", 0x95},
-    {"Quick Attack", 0x62}, {"Rage", 0x63},         {"Razor Leaf", 0x4B},   {"Razor Wind", 0x0D},
-    {"Recover", 0x69},      {"Reflect", 0x73},      {"Rest", 0x9C},         {"Roar", 0x2E},
-    {"Rock Slide", 0x9D},   {"Rock Throw", 0x58},   {"Rolling Kick", 0x1B}, {"Sand Attack", 0x1C},
-    {"Scratch", 0x0A},      {"Screech", 0x67},      {"Seismic Toss", 0x45}, {"Selfdestruct", 0x78},
-    {"Sharpen", 0x9F},      {"Sing", 0x2F},         {"Skull Bash", 0x82},   {"Sky Attack", 0x8F},
-    {"Slam", 0x15},         {"Slash", 0xA3},        {"Sleep Powder", 0x4F}, {"Sludge", 0x7C},
-    {"Smog", 0x7B},         {"Smokescreen", 0x6C},  {"Softboiled", 0x87},   {"Solar Beam", 0x4C},
-    {"Sonicboom", 0x31},    {"Spike Cannon", 0x83}, {"Splash", 0x96},       {"Spore", 0x93},
-    {"Stomp", 0x17},        {"Strength", 0x46},     {"String Shot", 0x51},  {"Struggle", 0xA5},
-    {"Stun Spore", 0x4E},   {"Submission", 0x42},   {"Substitute", 0xA4},   {"Supersonic", 0x30},
-    {"Super Fang", 0xA2},   {"Surf", 0x39},         {"Swift", 0x81},        {"Swords Dance", 0x0E},
-    {"Tackle", 0x21},       {"Tail Whip", 0x27},    {"Take Down", 0x24},    {"Teleport", 0x64},
-    {"Thrash", 0x25},       {"Thunderbolt", 0x55},  {"Thunderpunch", 0x09}, {"Thundershock", 0x54},
-    {"Thunder Wave", 0x56}, {"Thunder", 0x57},      {"Toxic", 0x5C},        {"Transform", 0x90},
-    {"Tri Attack", 0xA1},   {"Twineedle", 0x29},    {"Vicegrip", 0x0B},     {"Vine Whip", 0x16},
-    {"Waterfall", 0x7F},    {"Water Gun", 0x37},    {"Whirlwind", 0x12},    {"Wing Attack", 0x11},
-    {"Withdraw", 0x6E},     {"Wrap", 0x23},
-};
-
-uint32_t pokemon_exit_confirm_view(void* context) {
-    UNUSED(context);
-    return AppViewExitConfirm;
-}
-App* pokemon_alloc() {
-    App* app = (App*)malloc(sizeof(App));
-
-    // Gui
-    app->gui = (Gui*)furi_record_open(RECORD_GUI);
-    // View dispatcher
-    app->view_dispatcher = view_dispatcher_alloc();
-
-    view_dispatcher_enable_queue(app->view_dispatcher);
-    view_dispatcher_set_event_callback_context(app->view_dispatcher, app);
-    view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
-
-    //  Start Index first pokemon
-    app->current_pokemon = 0;
-    // Select Pokemon View
-    app->select_pokemon = select_pokemon_alloc(app);
-    view_set_previous_callback(select_pokemon_get_view(app), pokemon_exit_confirm_view);
-    view_dispatcher_add_view(
-        app->view_dispatcher, AppViewSelectPokemon, select_pokemon_get_view(app));
-
-    //  Start Index first level
-    app->current_level = 3;
-    // Select Level View
-    app->select_level = select_level_alloc(app);
-    view_set_previous_callback(select_level_get_view(app), pokemon_exit_confirm_view);
-    view_dispatcher_add_view(app->view_dispatcher, AppViewSelectLevel, select_level_get_view(app));
-
-    //  Start Index first stat
-    app->current_stats = 0;
-    // Select Level View
-    app->select_stats = select_stats_alloc(app);
-    view_set_previous_callback(select_stats_get_view(app), pokemon_exit_confirm_view);
-    view_dispatcher_add_view(app->view_dispatcher, AppViewSelectStats, select_stats_get_view(app));
-
-    //  Start Index first move
-    app->current_move = 0;
-    // Select Move View
-    app->select_move1 = select_move1_alloc(app);
-    view_set_previous_callback(select_move1_get_view(app), pokemon_exit_confirm_view);
-    view_dispatcher_add_view(app->view_dispatcher, AppViewSelectMove1, select_move1_get_view(app));
-
-    //  Start Index first move
-    app->current_move = 0;
-    // Select Move View
-    app->select_move2 = select_move2_alloc(app);
-    view_set_previous_callback(select_move1_get_view(app), pokemon_exit_confirm_view);
-    view_dispatcher_add_view(app->view_dispatcher, AppViewSelectMove2, select_move2_get_view(app));
-
-    //  Start Index first move
-    app->current_move = 0;
-    // Select Move View
-    app->select_move3 = select_move3_alloc(app);
-    view_set_previous_callback(select_move3_get_view(app), pokemon_exit_confirm_view);
-    view_dispatcher_add_view(app->view_dispatcher, AppViewSelectMove3, select_move3_get_view(app));
-
-    //  Start Index first move
-    app->current_move = 0;
-    // Select Move View
-    app->select_move4 = select_move4_alloc(app);
-    view_set_previous_callback(select_move4_get_view(app), pokemon_exit_confirm_view);
-    view_dispatcher_add_view(app->view_dispatcher, AppViewSelectMove4, select_move4_get_view(app));
-
-    // Trade View
-    app->trade = trade_alloc(app);
-    view_set_previous_callback(trade_get_view(app), pokemon_exit_confirm_view);
-    view_dispatcher_add_view(app->view_dispatcher, AppViewTrade, trade_get_view(app));
-
-    view_dispatcher_switch_to_view(app->view_dispatcher, AppViewSelectPokemon);
-
-    return app;
-}
-
-void free_app(App* app) {
-    furi_assert(app);
-
-    // Free views
-    view_dispatcher_remove_view(app->view_dispatcher, AppViewSelectPokemon);
-    select_pokemon_free(app);
-    view_dispatcher_remove_view(app->view_dispatcher, AppViewSelectLevel);
-    select_level_free(app);
-    view_dispatcher_remove_view(app->view_dispatcher, AppViewSelectStats);
-    select_stats_free(app);
-    view_dispatcher_remove_view(app->view_dispatcher, AppViewSelectMove1);
-    select_move1_free(app);
-    view_dispatcher_remove_view(app->view_dispatcher, AppViewSelectMove2);
-    select_move2_free(app);
-    view_dispatcher_remove_view(app->view_dispatcher, AppViewSelectMove3);
-    select_move3_free(app);
-    view_dispatcher_remove_view(app->view_dispatcher, AppViewSelectMove4);
-    select_move4_free(app);
-    view_dispatcher_remove_view(app->view_dispatcher, AppViewTrade);
-    trade_free(app);
-    // Close records
-    furi_record_close(RECORD_GUI);
-    app->gui = NULL;
-
-    // Free rest
-    free(app);
-}
-
-extern "C" int32_t pokemon_app(void* p) {
-    UNUSED(p);
-    //FURI_LOG_D(TAG, "init scene");
-    App* app = (App*)pokemon_alloc();
-
-    furi_hal_light_set(LightRed, 0x00);
-    furi_hal_light_set(LightGreen, 0x00);
-    furi_hal_light_set(LightBlue, 0x00);
-    //switch view  and run dispatcher
-    view_dispatcher_run(app->view_dispatcher);
-
-    // Free resources
-    free_app(app);
-    furi_record_close(RECORD_GUI);
-
-    return 0;
-}

+ 89 - 72
pokemon_app.h

@@ -2,45 +2,48 @@
 #define POKEMON_APP_H
 
 #pragma once
-#include <furi.h>
-#include <furi_hal_light.h>
-#include <gui/gui.h>
+
+#include <gui/scene_manager.h>
 #include <gui/view.h>
 #include <gui/view_dispatcher.h>
 #include <gui/icon.h>
-#include <pokemon_icons.h>
+#include <gui/modules/submenu.h>
+#include <gui/modules/text_input.h>
+#include <gui/modules/variable_item_list.h>
 
-#include "views/select_pokemon.hpp"
-#include "views/select_level.hpp"
-#include "views/select_stats.hpp"
-#include "views/select_move1.hpp"
-#include "views/select_move2.hpp"
-#include "views/select_move3.hpp"
-#include "views/select_move4.hpp"
-#include "views/trade.hpp"
+#include "pokemon_data.h"
 
 #define TAG "Pokemon"
 
-struct pokemon_lut {
+/* #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 hex;
-    const uint8_t type1;
-    const uint8_t type2;
-    const int xp_group;
-    const int base_hp;
-    const int base_atk;
-    const int base_def;
-    const int base_spd;
-    const int base_special;
+    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;
 };
 
-struct pokemon_mv {
+typedef struct pokemon_data_table PokemonTable;
+
+struct named_list {
     const char* name;
-    const uint8_t hex;
+    const uint8_t index;
 };
 
-typedef struct App App;
+typedef struct named_list NamedList;
+
 typedef enum {
     GAMEBOY_INITIAL,
     GAMEBOY_READY,
@@ -51,61 +54,75 @@ typedef enum {
     GAMEBOY_TRADING
 } render_gameboy_state_t;
 
-struct App {
-    Gui* gui;
+struct pokemon_fap {
     ViewDispatcher* view_dispatcher;
-    SelectPokemon* select_pokemon;
-    SelectLevel* select_level;
-    SelectStats* select_stats;
-    SelectMove1* select_move1;
-    SelectMove2* select_move2;
-    SelectMove3* select_move3;
-    SelectMove4* select_move4;
-    Trade* trade;
-    uint32_t view_id;
-
-    int current_pokemon = 0;
-    int current_level = 3;
-    int current_stats = 0;
-    int current_move = 0;
-    char pokemon_hex_code = ' ';
-    char move1_hex_code = ' ';
-    char move2_hex_code = ' ';
-    char move3_hex_code = ' ';
-    char move4_hex_code = ' ';
+
+    /* View ports for each of the application's steps */
+    View* select_view;
+    View* trade_view;
+
+    /* Scene manager */
+    SceneManager* scene_manager;
+
+    /* gui modules used in the application lifetime */
+    Submenu* submenu;
+    TextInput* text_input;
+    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;
+
+    /* Struct for holding trade data */
+    /* 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
+     * pokemon data rather than the full 6 member party data.
+     */
+    TradeBlock* trade_block;
+
+    /* The currently selected pokemon */
+    int curr_pokemon;
+
+    /* Some state tracking */
+    /* This, combined with some globals in trade.cpp, can probably be better
+     * consolidated at some point.
+     */
+    bool trading;
+    bool connected;
+    render_gameboy_state_t gameboy_status;
+
+    /* 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 enum {
+    AppViewMainMenu,
+    AppViewOpts, // Generic view ID meant for module re-use
     AppViewSelectPokemon,
-    AppViewSelectLevel,
-    AppViewSelectStats,
-    AppViewSelectMove1,
-    AppViewSelectMove2,
-    AppViewSelectMove3,
-    AppViewSelectMove4,
     AppViewTrade,
     AppViewExitConfirm,
 } AppView;
 
-typedef void (*SelectPokemonCallback)(void* context, uint32_t index);
-typedef struct SelectPokemonModel {
-    int current_pokemon = 0;
-    int current_level = 3;
-    int current_stats = 0;
-    int current_move = 0;
-    char pokemon_hex_code = ' ';
-    char move1_hex_code = ' ';
-    char move2_hex_code = ' ';
-    char move3_hex_code = ' ';
-    char move4_hex_code = ' ';
-    bool trading = false;
-    bool connected = false;
-    render_gameboy_state_t gameboy_status = GAMEBOY_INITIAL;
-    SelectPokemonCallback callback;
-    void* callback_context;
-} SelectPokemonModel;
-
-extern struct pokemon_lut pokemon_table[];
-extern struct pokemon_mv move_table[];
+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 */

+ 314 - 0
pokemon_char_encode.c

@@ -0,0 +1,314 @@
+#include <stdint.h>
+#include <stddef.h>
+
+#include "pokemon_char_encode.h"
+
+/* XXX Current text_input module only offers alnum and space input */
+char pokemon_char_to_encoded(int byte) {
+    switch(byte) {
+    case 'A':
+        return A_;
+    case 'B':
+        return B_;
+    case 'C':
+        return C_;
+    case 'D':
+        return D_;
+    case 'E':
+        return E_;
+    case 'F':
+        return F_;
+    case 'G':
+        return G_;
+    case 'H':
+        return H_;
+    case 'I':
+        return I_;
+    case 'J':
+        return J_;
+    case 'K':
+        return K_;
+    case 'L':
+        return L_;
+    case 'M':
+        return M_;
+    case 'N':
+        return N_;
+    case 'O':
+        return O_;
+    case 'P':
+        return P_;
+    case 'Q':
+        return Q_;
+    case 'R':
+        return R_;
+    case 'S':
+        return S_;
+    case 'T':
+        return T_;
+    case 'U':
+        return U_;
+    case 'V':
+        return V_;
+    case 'W':
+        return W_;
+    case 'X':
+        return X_;
+    case 'Y':
+        return Y_;
+    case 'Z':
+        return Z_;
+    case 'a':
+        return a_;
+    case 'b':
+        return b_;
+    case 'c':
+        return c_;
+    case 'd':
+        return d_;
+    case 'e':
+        return e_;
+    case 'f':
+        return f_;
+    case 'g':
+        return g_;
+    case 'h':
+        return h_;
+    case 'i':
+        return i_;
+    case 'j':
+        return j_;
+    case 'k':
+        return k_;
+    case 'l':
+        return l_;
+    case 'm':
+        return m_;
+    case 'n':
+        return n_;
+    case 'o':
+        return o_;
+    case 'p':
+        return p_;
+    case 'q':
+        return q_;
+    case 'r':
+        return r_;
+    case 's':
+        return s_;
+    case 't':
+        return t_;
+    case 'u':
+        return u_;
+    case 'v':
+        return v_;
+    case 'w':
+        return w_;
+    case 'x':
+        return x_;
+    case 'y':
+        return y_;
+    case 'z':
+        return z_;
+    case '0':
+        return _0_;
+    case '1':
+        return _1_;
+    case '2':
+        return _2_;
+    case '3':
+        return _3_;
+    case '4':
+        return _4_;
+    case '5':
+        return _5_;
+    case '6':
+        return _6_;
+    case '7':
+        return _7_;
+    case '8':
+        return _8_;
+    case '9':
+        return _9_;
+
+    /* This was previously implemented with unicode escape codes, however, that
+     * seemed to cause compilation issues. Which is strange because others reported
+     * compilation issues with the actual unicode characters. I'm not sure a good
+     * universal way to resolve this.
+     *
+     * Additionally, the ♂/♀ symbols don't render properly on the flipper. Would
+     * need to create a custom image/icon somehow, otherwise its nonobvious that
+     * the traded pokemon would have this symbol in their name.
+     */
+
+    case '\201':
+        return MALE_;
+    case '\200':
+        return FEMALE_;
+    default:
+        return TERM_;
+    }
+}
+
+int pokemon_encoded_to_char(char byte) {
+    switch(byte) {
+    case A_:
+        return 'A';
+    case B_:
+        return 'B';
+    case C_:
+        return 'C';
+    case D_:
+        return 'D';
+    case E_:
+        return 'E';
+    case F_:
+        return 'F';
+    case G_:
+        return 'G';
+    case H_:
+        return 'H';
+    case I_:
+        return 'I';
+    case J_:
+        return 'J';
+    case K_:
+        return 'K';
+    case L_:
+        return 'L';
+    case M_:
+        return 'M';
+    case N_:
+        return 'N';
+    case O_:
+        return 'O';
+    case P_:
+        return 'P';
+    case Q_:
+        return 'Q';
+    case R_:
+        return 'R';
+    case S_:
+        return 'S';
+    case T_:
+        return 'T';
+    case U_:
+        return 'U';
+    case V_:
+        return 'V';
+    case W_:
+        return 'W';
+    case X_:
+        return 'X';
+    case Y_:
+        return 'Y';
+    case Z_:
+        return 'Z';
+    case a_:
+        return 'a';
+    case b_:
+        return 'b';
+    case c_:
+        return 'c';
+    case d_:
+        return 'd';
+    case e_:
+        return 'e';
+    case f_:
+        return 'f';
+    case g_:
+        return 'g';
+    case h_:
+        return 'h';
+    case i_:
+        return 'i';
+    case j_:
+        return 'j';
+    case k_:
+        return 'k';
+    case l_:
+        return 'l';
+    case m_:
+        return 'm';
+    case n_:
+        return 'n';
+    case o_:
+        return 'o';
+    case p_:
+        return 'p';
+    case q_:
+        return 'q';
+    case r_:
+        return 'r';
+    case s_:
+        return 's';
+    case t_:
+        return 't';
+    case u_:
+        return 'u';
+    case v_:
+        return 'v';
+    case w_:
+        return 'w';
+    case x_:
+        return 'x';
+    case y_:
+        return 'y';
+    case z_:
+        return 'z';
+    case _0_:
+        return '0';
+    case _1_:
+        return '1';
+    case _2_:
+        return '2';
+    case _3_:
+        return '3';
+    case _4_:
+        return '4';
+    case _5_:
+        return '5';
+    case _6_:
+        return '6';
+    case _7_:
+        return '7';
+    case _8_:
+        return '8';
+    case _9_:
+        return '9';
+
+    /* This was previously implemented with unicode escape codes, however, that
+     * seemed to cause compilation issues. Which is strange because others reported
+     * compilation issues with the actual unicode characters. I'm not sure a good
+     * universal way to resolve this.
+     *
+     * Additionally, the ♂/♀ symbols don't render properly on the flipper. Would
+     * need to create a custom image/icon somehow, otherwise its nonobvious that
+     * the traded pokemon would have this symbol in their name.
+     */
+    case MALE_:
+        return '\201';
+    case FEMALE_:
+        return '\200';
+    default:
+        return '\0';
+    }
+}
+
+/* encode n bytes, any currently noninputtable characters are set with TERM_ */
+void pokemon_str_to_encoded_array(uint8_t* dest, char* src, size_t n) {
+    for(; n > 0; n--) {
+        *dest = pokemon_char_to_encoded(*src);
+        dest++;
+        src++;
+    }
+}
+
+/* decode n bytes, any currently noninputtable characters are set with '\0' */
+void pokemon_encoded_array_to_str(char* dest, uint8_t* src, size_t n) {
+    for(; n > 0; n--) {
+        *dest = pokemon_encoded_to_char(*src);
+        dest++;
+        src++;
+    }
+}

+ 110 - 0
pokemon_char_encode.h

@@ -0,0 +1,110 @@
+#ifndef POKEMON_CHAR_ENCODE_H
+#define POKEMON_CHAR_ENCODE_H
+/* NOTE: These map to the Gen 1 character set! */
+/* NOTE: These map to English */
+/* TODO: It may make more sense to put this in a const array as a LUT,
+ * e.g. t['F'], t['l'], t['i'], t['p'], t['e'], t['r'], t['\0']
+ * As this could be an easier translation for each letter to build a string
+ * to set names and things on the fly in the flipper. Need to explore that.
+ * once I get to that point.
+ */
+#define TERM_ 0x50
+#define SPACE_ 0x7f
+#define A_ 0x80
+#define B_ 0x81
+#define C_ 0x82
+#define D_ 0x83
+#define E_ 0x84
+#define F_ 0x85
+#define G_ 0x86
+#define H_ 0x87
+#define I_ 0x88
+#define J_ 0x89
+#define K_ 0x8a
+#define L_ 0x8b
+#define M_ 0x8c
+#define N_ 0x8d
+#define O_ 0x8e
+#define P_ 0x8f
+#define Q_ 0x90
+#define R_ 0x91
+#define S_ 0x92
+#define T_ 0x93
+#define U_ 0x94
+#define V_ 0x95
+#define W_ 0x96
+#define X_ 0x97
+#define Y_ 0x98
+#define Z_ 0x99
+#define O_PAREN_ 0x9a
+#define C_PAREN_ 0x9b
+#define COLON_ 0x9c
+#define SEMI_ 0x9d
+#define O_BRACKET_ 0x9e
+#define C_BRACKET_ 0x9f
+#define a_ 0xa0
+#define b_ 0xa1
+#define c_ 0xa2
+#define d_ 0xa3
+#define e_ 0xa4
+#define f_ 0xa5
+#define g_ 0xa6
+#define h_ 0xa7
+#define i_ 0xa8
+#define j_ 0xa9
+#define k_ 0xaa
+#define l_ 0xab
+#define m_ 0xac
+#define n_ 0xad
+#define o_ 0xae
+#define p_ 0xaf
+#define q_ 0xb0
+#define r_ 0xb1
+#define s_ 0xb2
+#define t_ 0xb3
+#define u_ 0xb4
+#define v_ 0xb5
+#define w_ 0xb6
+#define x_ 0xb7
+#define y_ 0xb8
+#define z_ 0xb9
+#define e_ACCENT_ 0xba
+#define d_TICK_ 0xbb
+#define l_TICK_ 0xbc
+#define s_TICK_ 0xbd
+#define t_TICK_ 0xbe
+#define v_TICK_ 0xbf
+#define S_QUOTE_ 0xe0
+#define PK_ 0xe1
+#define MN_ 0xe2
+#define DASH_ 0xe3
+#define r_TICK_ 0xe4
+#define m_TICK_ 0xe5
+#define QUESTION_ 0xe6
+#define EXCLAIM_ 0xe7
+#define PERIOD_ 0xe8
+#define R_ARR_ 0xec
+#define D_ARR_ 0xee
+#define MALE_ 0xef
+#define FEMALE_ 0xf5
+#define _0_ 0xf6
+#define _1_ 0xf7
+#define _2_ 0xf8
+#define _3_ 0xf9
+#define _4_ 0xfa
+#define _5_ 0xfb
+#define _6_ 0xfc
+#define _7_ 0xfd
+#define _8_ 0xfe
+#define _9_ 0xff
+
+#include <stdint.h>
+#include <stddef.h>
+
+char pokemon_char_to_encoded(int byte);
+int pokemon_encoded_to_char(char byte);
+
+void pokemon_str_to_encoded_array(uint8_t* dest, char* src, size_t n);
+void pokemon_encoded_array_to_str(char* dest, uint8_t* src, size_t n);
+
+#endif // POKEMON_CHAR_ENCODE_H

+ 37 - 271
pokemon_data.h

@@ -3,117 +3,30 @@
 
 #pragma once
 
-/* NOTE: These map to the Gen 1 character set! */
-/* TODO: It may make more sense to put this in a const array as a LUT,
- * e.g. t['F'], t['l'], t['i'], t['p'], t['e'], t['r'], t['\0']
- * As this could be an easier translation for each letter to build a string
- * to set names and things on the fly in the flipper. Need to explore that.
- * once I get to that point.
+/* 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.
  */
-#define TERM_ 0x50
-#define SPACE_ 0x7f
-#define A_ 0x80
-#define B_ 0x81
-#define C_ 0x82
-#define D_ 0x83
-#define E_ 0x84
-#define F_ 0x85
-#define G_ 0x86
-#define H_ 0x87
-#define I_ 0x88
-#define J_ 0x89
-#define K_ 0x8a
-#define L_ 0x8b
-#define M_ 0x8c
-#define N_ 0x8d
-#define O_ 0x8e
-#define P_ 0x8f
-#define Q_ 0x90
-#define R_ 0x91
-#define S_ 0x92
-#define T_ 0x93
-#define U_ 0x94
-#define V_ 0x95
-#define W_ 0x96
-#define X_ 0x97
-#define Y_ 0x98
-#define Z_ 0x99
-#define O_PAREN_ 0x9a
-#define C_PAREN_ 0x9b
-#define COLON_ 0x9c
-#define SEMI_ 0x9d
-#define O_BRACKET_ 0x9e
-#define C_BRACKET_ 0x9f
-#define a_ 0xa0
-#define b_ 0xa1
-#define c_ 0xa2
-#define d_ 0xa3
-#define e_ 0xa4
-#define f_ 0xa5
-#define g_ 0xa6
-#define h_ 0xa7
-#define i_ 0xa8
-#define j_ 0xa9
-#define k_ 0xaa
-#define l_ 0xab
-#define m_ 0xac
-#define n_ 0xad
-#define o_ 0xae
-#define p_ 0xaf
-#define q_ 0xb0
-#define r_ 0xb1
-#define s_ 0xb2
-#define t_ 0xb3
-#define u_ 0xb4
-#define v_ 0xb5
-#define w_ 0xb6
-#define x_ 0xb7
-#define y_ 0xb8
-#define z_ 0xb9
-#define e_ACCENT_ 0xba
-#define d_TICK_ 0xbb
-#define l_TICK_ 0xbc
-#define s_TICK_ 0xbd
-#define t_TICK_ 0xbe
-#define v_TICK_ 0xbf
-#define S_QUOTE_ 0xe0
-#define PK_ 0xe1
-#define MN_ 0xe2
-#define DASH_ 0xe3
-#define r_TICK_ 0xe4
-#define m_TICK_ 0xe5
-#define QUESTION_ 0xe6
-#define EXCLAIM_ 0xe7
-#define PERIOD_ 0xe8
-#define R_ARR_ 0xec
-#define D_ARR_ 0xee
-#define MALE_ 0xef
-#define FEMALE_ 0xf5
-#define _0_ 0xf6
-#define _1_ 0xf7
-#define _2_ 0xf8
-#define _3_ 0xf9
-#define _4_ 0xfa
-#define _5_ 0xfb
-#define _6_ 0xfc
-#define _7_ 0xfd
-#define _8_ 0xfe
-#define _9_ 0xff
 
-/* XXX: 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. Does flipper
- * API have calls to swap endianness?
+/* 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.
  */
 struct __attribute__((__packed__)) pokemon_structure {
-    uint8_t species;
-    uint16_t hp;
+    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;
-    uint8_t type[2];
-    uint8_t catch_held;
+    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 orig_trainer;
-    uint8_t exp[3];
+    uint16_t ot_id;
+    uint8_t exp[3]; // Calculated from level
     uint16_t hp_ev;
     uint16_t atk_ev;
     uint16_t def_ev;
@@ -121,185 +34,38 @@ struct __attribute__((__packed__)) pokemon_structure {
     uint16_t special_ev;
     uint16_t iv;
     uint8_t move_pp[4];
-    uint8_t level_again;
-    uint16_t max_hp;
-    uint16_t atk;
-    uint16_t def;
-    uint16_t spd;
-    uint16_t special;
+    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
 };
 
 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];
 };
 
 struct __attribute__((__packed__)) trade_data_block {
     unsigned char trainer_name[11];
     uint8_t party_cnt;
-    uint8_t party_members[7]; // Unsure if last byte always needs to be 0xff for terminator
+    /* 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 trade_data_block DATA_BLOCK2 =
-    {.trainer_name = {F_, l_, i_, p_, p_, e_, r_, TERM_, TERM_, TERM_, TERM_},
-     .party_cnt = 1,
-     .party_members = {0x15, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
-     .party =
-         {
-             {.species = 0x4a,
-              .hp = 0x2c01,
-              .level = 0x4a,
-              .status_condition = 0x0,
-              .type = {0x14, 0x08},
-              .catch_held = 0x1f,
-              .move = {0x7e, 0x38, 0x09, 0x19},
-              .orig_trainer = 0xd204,
-              .exp = {0x3, 0xd, 0x40},
-              .hp_ev = 0xffff,
-              .atk_ev = 0xffff,
-              .def_ev = 0xffff,
-              .spd_ev = 0xffff,
-              .special_ev = 0xffff,
-              .iv = 0xffff,
-              .move_pp = {0xc0, 0xc0, 0xc0, 0xc0},
-              .level_again = 0x4a,
-              .max_hp = 0x2c01,
-              .atk = 0x9600,
-              .def = 0x9700,
-              .spd = 0x9800,
-              .special = 0x9900},
-             {.species = 0x4a,
-              .hp = 0x2c01,
-              .level = 0x4a,
-              .status_condition = 0x0,
-              .type = {0x14, 0x08},
-              .catch_held = 0x1f,
-              .move = {0x7e, 0x38, 0x09, 0x19},
-              .orig_trainer = 0xd204,
-              .exp = {0x3, 0xd, 0x40},
-              .hp_ev = 0xffff,
-              .atk_ev = 0xffff,
-              .def_ev = 0xffff,
-              .spd_ev = 0xffff,
-              .special_ev = 0xffff,
-              .iv = 0xffff,
-              .move_pp = {0xc0, 0xc0, 0xc0, 0xc0},
-              .level_again = 0x4a,
-              .max_hp = 0x2c01,
-              .atk = 0x9600,
-              .def = 0x9700,
-              .spd = 0x9800,
-              .special = 0x9900},
-             {.species = 0x4a,
-              .hp = 0x2c01,
-              .level = 0x4a,
-              .status_condition = 0x0,
-              .type = {0x14, 0x08},
-              .catch_held = 0x1f,
-              .move = {0x7e, 0x38, 0x09, 0x19},
-              .orig_trainer = 0xd204,
-              .exp = {0x3, 0xd, 0x40},
-              .hp_ev = 0xffff,
-              .atk_ev = 0xffff,
-              .def_ev = 0xffff,
-              .spd_ev = 0xffff,
-              .special_ev = 0xffff,
-              .iv = 0xffff,
-              .move_pp = {0xc0, 0xc0, 0xc0, 0xc0},
-              .level_again = 0x4a,
-              .max_hp = 0x2c01,
-              .atk = 0x9600,
-              .def = 0x9700,
-              .spd = 0x9800,
-              .special = 0x9900},
-             {.species = 0x4a,
-              .hp = 0x2c01,
-              .level = 0x4a,
-              .status_condition = 0x0,
-              .type = {0x14, 0x08},
-              .catch_held = 0x1f,
-              .move = {0x7e, 0x38, 0x09, 0x19},
-              .orig_trainer = 0xd204,
-              .exp = {0x3, 0xd, 0x40},
-              .hp_ev = 0xffff,
-              .atk_ev = 0xffff,
-              .def_ev = 0xffff,
-              .spd_ev = 0xffff,
-              .special_ev = 0xffff,
-              .iv = 0xffff,
-              .move_pp = {0xc0, 0xc0, 0xc0, 0xc0},
-              .level_again = 0x4a,
-              .max_hp = 0x2c01,
-              .atk = 0x9600,
-              .def = 0x9700,
-              .spd = 0x9800,
-              .special = 0x9900},
-             {.species = 0x4a,
-              .hp = 0x2c01,
-              .level = 0x4a,
-              .status_condition = 0x0,
-              .type = {0x14, 0x08},
-              .catch_held = 0x1f,
-              .move = {0x7e, 0x38, 0x09, 0x19},
-              .orig_trainer = 0xd204,
-              .exp = {0x3, 0xd, 0x40},
-              .hp_ev = 0xffff,
-              .atk_ev = 0xffff,
-              .def_ev = 0xffff,
-              .spd_ev = 0xffff,
-              .special_ev = 0xffff,
-              .iv = 0xffff,
-              .move_pp = {0xc0, 0xc0, 0xc0, 0xc0},
-              .level_again = 0x4a,
-              .max_hp = 0x2c01,
-              .atk = 0x9600,
-              .def = 0x9700,
-              .spd = 0x9800,
-              .special = 0x9900},
-             {.species = 0x4a,
-              .hp = 0x2c01,
-              .level = 0x4a,
-              .status_condition = 0x0,
-              .type = {0x14, 0x08},
-              .catch_held = 0x1f,
-              .move = {0x7e, 0x38, 0x09, 0x19},
-              .orig_trainer = 0xd204,
-              .exp = {0x3, 0xd, 0x40},
-              .hp_ev = 0xffff,
-              .atk_ev = 0xffff,
-              .def_ev = 0xffff,
-              .spd_ev = 0xffff,
-              .special_ev = 0xffff,
-              .iv = 0xffff,
-              .move_pp = {0xc0, 0xc0, 0xc0, 0xc0},
-              .level_again = 0x4a,
-              .max_hp = 0x2c01,
-              .atk = 0x9600,
-              .def = 0x9700,
-              .spd = 0x9800,
-              .special = 0x9900},
-         },
-     .ot_name =
-         {
-             {.str = {F_, l_, i_, p_, p_, e_, r_, TERM_, TERM_, TERM_, TERM_}},
-             {.str = {F_, l_, i_, p_, p_, e_, r_, TERM_, TERM_, TERM_, TERM_}},
-             {.str = {F_, l_, i_, p_, p_, e_, r_, TERM_, TERM_, TERM_, TERM_}},
-             {.str = {F_, l_, i_, p_, p_, e_, r_, TERM_, TERM_, TERM_, TERM_}},
-             {.str = {F_, l_, i_, p_, p_, e_, r_, TERM_, TERM_, TERM_, TERM_}},
-             {.str = {F_, l_, i_, p_, p_, e_, r_, TERM_, TERM_, TERM_, TERM_}},
-         },
-     .nickname = {
-         {.str = {F_, l_, i_, p_, p_, e_, r_, TERM_, TERM_, TERM_, TERM_}},
-         {.str = {F_, l_, i_, p_, p_, e_, r_, TERM_, TERM_, TERM_, TERM_}},
-         {.str = {F_, l_, i_, p_, p_, e_, r_, TERM_, TERM_, TERM_, TERM_}},
-         {.str = {F_, l_, i_, p_, p_, e_, r_, TERM_, TERM_, TERM_, TERM_}},
-         {.str = {F_, l_, i_, p_, p_, e_, r_, TERM_, TERM_, TERM_, TERM_}},
-         {.str = {F_, l_, i_, p_, p_, e_, r_, TERM_, TERM_, TERM_, TERM_}},
-     }};
-
-unsigned char INPUT_BLOCK[405];
-unsigned char* DATA_BLOCK = (unsigned char*)&DATA_BLOCK2;
+typedef struct trade_data_block TradeBlock;
 
 #endif /* POKEMON_DATA_H */

+ 59 - 0
scenes/pokemon_level.c

@@ -0,0 +1,59 @@
+#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;
+        /* XXX: Need to recalculate other stats with level updated! */
+    }
+
+    return rc;
+}
+
+static void select_level_input_callback(void* context) {
+    PokemonFap* pokemon_fap = (PokemonFap*)context;
+
+    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);
+}

+ 9 - 0
scenes/pokemon_level.h

@@ -0,0 +1,9 @@
+#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

+ 164 - 0
scenes/pokemon_menu.c

@@ -0,0 +1,164 @@
+#include "../pokemon_app.h"
+#include "../pokemon_char_encode.h"
+
+#include "pokemon_menu.h"
+#include "pokemon_select.h"
+#include "pokemon_nickname.h"
+#include "pokemon_level.h"
+#include "pokemon_move.h"
+#include "pokemon_type.h"
+#include "pokemon_stats.h"
+#include "pokemon_ot_id.h"
+#include "pokemon_ot_name.h"
+#include "pokemon_trade.h"
+
+static void scene_change_from_main_cb(void* context, uint32_t index) {
+    PokemonFap* pokemon_fap = (PokemonFap*)context;
+
+    /* 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, MainMenuScene, index);
+    scene_manager_next_scene(pokemon_fap->scene_manager, index);
+}
+
+bool main_menu_back_event_callback(void* context) {
+    furi_assert(context);
+    PokemonFap* pokemon_fap = context;
+    return scene_manager_handle_back_event(pokemon_fap->scene_manager);
+}
+
+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;
+
+    /* 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);
+
+    submenu_reset(pokemon_fap->submenu);
+
+    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(
+        pokemon_fap->submenu,
+        "Select Types",
+        SelectTypeScene,
+        scene_change_from_main_cb,
+        pokemon_fap);
+    submenu_add_item(
+        pokemon_fap->submenu,
+        stats_text[pokemon_fap->curr_stats],
+        SelectStatsScene,
+        scene_change_from_main_cb,
+        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_set_selected_item(
+        pokemon_fap->submenu,
+        scene_manager_get_scene_state(pokemon_fap->scene_manager, MainMenuScene));
+
+    view_dispatcher_set_navigation_event_callback(
+        pokemon_fap->view_dispatcher, main_menu_back_event_callback);
+    view_dispatcher_switch_to_view(pokemon_fap->view_dispatcher, AppViewMainMenu);
+}
+
+bool null_scene_on_event(void* context, SceneManagerEvent event) {
+    UNUSED(context);
+    UNUSED(event);
+    return false;
+}
+
+void null_scene_on_exit(void* context) {
+    UNUSED(context);
+}
+
+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,
+};
+
+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,
+};
+
+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,
+};
+
+const SceneManagerHandlers pokemon_scene_manager_handlers = {
+    .on_enter_handlers = pokemon_scene_on_enter_handlers,
+    .on_exit_handlers = pokemon_scene_on_exit_handlers,
+    .on_event_handlers = pokemon_scene_on_event_handlers,
+    .scene_num = SceneCount,
+};

+ 26 - 0
scenes/pokemon_menu.h

@@ -0,0 +1,26 @@
+#ifndef POKEMON_MENU_H
+#define POKEMON_MENU_H
+
+#pragma once
+
+#include <gui/scene_manager.h>
+
+typedef enum {
+    MainMenuScene,
+    SelectPokemonScene,
+    SelectNicknameScene,
+    SelectLevelScene,
+    SelectMoveScene,
+    SelectMoveIndexScene,
+    SelectMoveSetScene,
+    SelectTypeScene,
+    SelectStatsScene,
+    SelectOTIDScene,
+    SelectOTNameScene,
+    TradeScene,
+    SceneCount,
+} AppScene;
+
+extern const SceneManagerHandlers pokemon_scene_manager_handlers;
+
+#endif // POKEMON_MENU_H

+ 148 - 0
scenes/pokemon_move.c

@@ -0,0 +1,148 @@
+#include <gui/modules/submenu.h>
+#include <gui/scene_manager.h>
+#include <stdio.h>
+
+#include "../pokemon_app.h"
+#include "pokemon_menu.h"
+
+static void select_move_selected_callback(void* context, uint32_t index) {
+    PokemonFap* pokemon_fap = (PokemonFap*)context;
+    uint32_t move = scene_manager_get_scene_state(pokemon_fap->scene_manager, SelectMoveScene);
+
+    if(index == UINT32_MAX) {
+        pokemon_fap->trade_block->party[0].move[move] =
+            pokemon_fap->pokemon_table[pokemon_fap->curr_pokemon].move[move];
+    } else {
+        pokemon_fap->trade_block->party[0].move[move] = (uint8_t)index;
+    }
+
+    /* Move back to move menu */
+    scene_manager_search_and_switch_to_previous_scene(pokemon_fap->scene_manager, SelectMoveScene);
+}
+
+static void select_move_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, SelectMoveIndexScene, index);
+    scene_manager_next_scene(pokemon_fap->scene_manager, SelectMoveSetScene);
+}
+
+static void select_move_number_callback(void* context, uint32_t index) {
+    PokemonFap* pokemon_fap = (PokemonFap*)context;
+
+    /* Move to move index scene, save which move number we're selecting,
+     * This doubles as the move slot we're going to write to later.
+     */
+    scene_manager_set_scene_state(pokemon_fap->scene_manager, SelectMoveScene, index);
+    scene_manager_next_scene(pokemon_fap->scene_manager, SelectMoveIndexScene);
+}
+
+void select_move_scene_on_enter(void* context) {
+    PokemonFap* pokemon_fap = (PokemonFap*)context;
+    uint8_t* pkmn_move = pokemon_fap->trade_block->party[0].move;
+    char buf[64];
+
+    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);
+
+    /* TODO: Add a "Default all moves" item? */
+
+    submenu_set_selected_item(
+        pokemon_fap->submenu,
+        scene_manager_get_scene_state(pokemon_fap->scene_manager, SelectMoveScene));
+
+    /* Clear cursor position on MoveIndex */
+    scene_manager_set_scene_state(pokemon_fap->scene_manager, SelectMoveIndexScene, 0);
+}
+
+void select_move_index_scene_on_enter(void* context) {
+    PokemonFap* pokemon_fap = (PokemonFap*)context;
+    int i;
+    char letter[2] = {'\0'};
+    char buf[32];
+    int curr_pokemon = pokemon_fap->curr_pokemon;
+    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);
+    /* The move list should always start with No Move, put that at the start
+     * for quick access.
+     */
+    submenu_add_item(
+        pokemon_fap->submenu,
+        move_list[0].name,
+        move_list[0].index,
+        select_move_selected_callback,
+        pokemon_fap);
+
+    /* Option to set move back to default */
+    snprintf(
+        buf,
+        sizeof(buf),
+        "Default [%s]",
+        pokemon_named_list_get_name_from_index(pokemon_fap->move_list, default_move));
+    submenu_add_item(
+        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 */
+    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]);
+            submenu_add_item(
+                pokemon_fap->submenu, letter, letter[0], select_move_index_callback, pokemon_fap);
+        }
+    }
+
+    submenu_set_selected_item(
+        pokemon_fap->submenu,
+        scene_manager_get_scene_state(pokemon_fap->scene_manager, SelectMoveIndexScene));
+}
+
+void select_move_set_scene_on_enter(void* context) {
+    PokemonFap* pokemon_fap = (PokemonFap*)context;
+    int i;
+    char letter =
+        (char)scene_manager_get_scene_state(pokemon_fap->scene_manager, SelectMoveIndexScene);
+
+    /* Populate submenu with all moves that start with `letter` */
+    /* NOTE! Start with index of 1 in the move list since 0 should always be no move! */
+    submenu_reset(pokemon_fap->submenu);
+    for(i = 1;; i++) {
+        if(pokemon_fap->move_list[i].name == NULL) break;
+        if(toupper(pokemon_fap->move_list[i].name[0]) == toupper(letter)) {
+            submenu_add_item(
+                pokemon_fap->submenu,
+                pokemon_fap->move_list[i].name,
+                pokemon_fap->move_list[i].index,
+                select_move_selected_callback,
+                pokemon_fap);
+        }
+    }
+}

+ 12 - 0
scenes/pokemon_move.h

@@ -0,0 +1,12 @@
+#ifndef POKEMON_MOVE_H
+#define POKEMON_MOVE_H
+
+#pragma once
+
+void select_move_scene_on_enter(void* context);
+
+void select_move_index_scene_on_enter(void* context);
+
+void select_move_set_scene_on_enter(void* context);
+
+#endif // POKEMON_MOVE_H

+ 78 - 0
scenes/pokemon_nickname.c

@@ -0,0 +1,78 @@
+#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;
+
+    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;
+    }
+
+    if(rc == false) {
+        furi_string_printf(error, "Some error?");
+    } else {
+        /* 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));
+    }
+
+    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);
+}

+ 9 - 0
scenes/pokemon_nickname.h

@@ -0,0 +1,9 @@
+#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

+ 72 - 0
scenes/pokemon_ot_id.c

@@ -0,0 +1,72 @@
+#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;
+    }
+
+    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);
+}

+ 9 - 0
scenes/pokemon_ot_id.h

@@ -0,0 +1,9 @@
+#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

+ 73 - 0
scenes/pokemon_ot_name.c

@@ -0,0 +1,73 @@
+#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;
+
+    // XXX If no pokemon name, use default. How TF to have text input handle that?
+    // 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(!isalnum((unsigned int)text[i])) {
+            if(text[i] == '\0') break;
+            rc = false;
+            break;
+        }
+    }
+
+    if(rc == false) {
+        furi_string_printf(error, "Some error?");
+    } else {
+        /* 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));
+    }
+
+    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);
+}

+ 9 - 0
scenes/pokemon_ot_name.h

@@ -0,0 +1,9 @@
+#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

+ 22 - 0
scenes/pokemon_select.c

@@ -0,0 +1,22 @@
+#include "../pokemon_app.h"
+
+void select_pokemon_scene_on_enter(void* context) {
+    PokemonFap* pokemon_fap = (PokemonFap*)context;
+    // switch to select pokemon scene
+    // Note for the future, this might make sense to setup and teardown each view
+    // at runtime rather than at the start of the whole application
+    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);
+    }
+}

+ 9 - 0
scenes/pokemon_select.h

@@ -0,0 +1,9 @@
+#ifndef POKEMON_SELECT_H
+#define POKEMON_SELECT_H
+
+#pragma once
+
+void select_pokemon_scene_on_enter(void* context);
+void select_pokemon_scene_on_exit(void* context);
+
+#endif // POKEMON_SELECT_H

+ 41 - 0
scenes/pokemon_stats.c

@@ -0,0 +1,41 @@
+#include <gui/modules/submenu.h>
+
+#include "../pokemon_app.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) {
+    PokemonFap* pokemon_fap = (PokemonFap*)context;
+
+    pokemon_fap->curr_stats = index;
+
+    pokemon_trade_block_recalculate_stats_from_level(pokemon_fap);
+
+    scene_manager_previous_scene(pokemon_fap->scene_manager);
+}
+
+void select_stats_scene_on_enter(void* context) {
+    PokemonFap* pokemon_fap = (PokemonFap*)context;
+
+    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);
+}

+ 9 - 0
scenes/pokemon_stats.h

@@ -0,0 +1,9 @@
+#ifndef POKEMON_STATS_H
+#define POKEMON_STATS_H
+
+#pragma once
+
+extern const char* stats_text[6];
+void select_stats_scene_on_enter(void* context);
+
+#endif // POKEMON_STATS_H

+ 9 - 0
scenes/pokemon_trade.c

@@ -0,0 +1,9 @@
+#include "../pokemon_app.h"
+
+void trade_scene_on_enter(void* context) {
+    PokemonFap* pokemon_fap = (PokemonFap*)context;
+    // switch to select pokemon scene
+    // Note for the future, this might make sense to setup and teardown each view
+    // at runtime rather than at the start?
+    view_dispatcher_switch_to_view(pokemon_fap->view_dispatcher, AppViewTrade);
+}

+ 8 - 0
scenes/pokemon_trade.h

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

+ 69 - 0
scenes/pokemon_type.c

@@ -0,0 +1,69 @@
+#include <gui/modules/variable_item_list.h>
+
+#include "../pokemon_app.h"
+#include "pokemon_menu.h"
+
+/* 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
+ * the two different VariableItems in a way that I don't know how to do
+ * yet with this interface.
+ * For now, selecting a type immediately updates the trade_block struct,
+ * requiring the user to press Back to go back. I would like to implement
+ * an OK press or something to save both. But thats a problem for another
+ * 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;
+}
+
+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;
+}
+
+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);
+}
+
+void select_type_scene_on_enter(void* 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;
+
+    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);
+
+    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));
+
+    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));
+
+    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);
+}

+ 9 - 0
scenes/pokemon_type.h

@@ -0,0 +1,9 @@
+#ifndef POKEMON_TYPE_H
+#define POKEMON_TYPE_H
+
+#pragma once
+
+void select_type_scene_on_enter(void* context);
+void select_type_scene_on_exit(void* context);
+
+#endif // POKEMON_TYPE_H

+ 0 - 170
views/select_level.cpp

@@ -1,170 +0,0 @@
-#include "../pokemon_app.h"
-#include "select_level.hpp"
-
-static void select_level_render_callback(Canvas* canvas, void* context) {
-    canvas_clear(canvas);
-
-    SelectPokemonModel* model = (SelectPokemonModel*)context;
-    const uint8_t current_index = model->current_level;
-
-    char level_num[5];
-
-    snprintf(level_num, sizeof(level_num), "#%03d", current_index);
-    canvas_set_font(canvas, FontPrimary);
-    canvas_draw_str_aligned(
-        canvas, 55, 54 / 2, AlignLeft, AlignTop, "Level:");
-
-    canvas_set_font(canvas, FontSecondary);
-    canvas_draw_str_aligned(canvas, 55, 38, AlignLeft, AlignTop, level_num);
-    canvas_draw_icon(canvas, 0, 0, pokemon_table[model->current_pokemon].icon);
-    canvas_draw_icon(canvas, 128 - 80, 0, &I_Space_80x18);
-    canvas_draw_str_aligned(canvas, (128 - 40), 5, AlignCenter, AlignTop, "Select Level");
-
-    canvas_set_font(canvas, FontPrimary);
-    elements_button_center(canvas, "OK");
-}
-
-static bool select_level_input_callback(InputEvent* event, void* context) {
-    furi_assert(context);
-    SelectLevel* select_level = (SelectLevel*)context;
-    bool consumed = false;
-
-    if(event->type == InputTypePress && event->key == InputKeyOk) {
-        with_view_model_cpp(
-            select_level->view,
-            SelectPokemonModel*,
-            model,
-            {
-                select_level->app->current_level = model->current_level;
-            },
-            false);
-        
-        view_dispatcher_switch_to_view(select_level->app->view_dispatcher, AppViewSelectStats);
-        consumed = true;
-    } else if(event->type == InputTypePress && event->key == InputKeyBack) {
-        view_dispatcher_switch_to_view(select_level->app->view_dispatcher, AppViewSelectPokemon);
-        consumed = true;
-    } else if(event->type == InputTypePress && event->key == InputKeyLeft) {
-        with_view_model_cpp(
-            select_level->view,
-            SelectPokemonModel*,
-            model,
-            {
-                if(model->current_level == 3) {
-                    model->current_level = 100;
-                } else {
-                    model->current_level--;
-                }
-            },
-            true);
-        consumed = true;
-    } else if(event->type == InputTypePress && event->key == InputKeyDown) {
-        with_view_model_cpp(
-            select_level->view,
-            SelectPokemonModel*,
-            model,
-            {
-                if(model->current_level >= 10) {
-                    model->current_level -= 10;
-                } else {
-                    model->current_level = 100;
-                }
-            },
-            true);
-        consumed = true;
-    } else if(event->type == InputTypePress && event->key == InputKeyRight) {
-        with_view_model_cpp(
-            select_level->view,
-            SelectPokemonModel*,
-            model,
-            {
-                if(model->current_level == 100) {
-                    model->current_level = 3;
-                } else {
-                    model->current_level++;
-                }
-            },
-            true);
-        consumed = true;
-    } else if(event->type == InputTypePress && event->key == InputKeyUp) {
-        with_view_model_cpp(
-            select_level->view,
-            SelectPokemonModel*,
-            model,
-            {
-                if(model->current_level <= 90) {
-                    model->current_level += 10;
-                } else {
-                    model->current_level = 3;
-                    ;
-                }
-            },
-            true);
-        consumed = true;
-    }
-
-    return consumed;
-}
-
-void select_level_enter_callback(void* context) {
-    furi_assert(context);
-    SelectLevel* select_level = (SelectLevel*)context;
-    with_view_model_cpp(
-        select_level->view,
-        SelectPokemonModel*,
-        model,
-        {
-            model->current_pokemon = select_level->app->current_pokemon;
-            model->pokemon_hex_code = select_level->app->pokemon_hex_code;
-            model->current_level = select_level->app->current_level;
-        },
-        true);
-}
-
-bool select_level_custom_callback(uint32_t event, void* context) {
-    UNUSED(event);
-    furi_assert(context);
-    SelectLevel* select_level = (SelectLevel*)context;
-    view_dispatcher_send_custom_event(select_level->app->view_dispatcher, 0);
-    return true;
-}
-
-void select_level_exit_callback(void* context) {
-    furi_assert(context);
-    UNUSED(context);
-}
-
-SelectLevel* select_level_alloc(App* app) {
-    SelectLevel* select_level = (SelectLevel*)malloc(sizeof(SelectLevel));
-    select_level->app = app;
-    select_level->view = view_alloc();
-    view_set_context(select_level->view, select_level);
-    view_allocate_model(select_level->view, ViewModelTypeLockFree, sizeof(SelectPokemonModel));
-    with_view_model_cpp(
-        select_level->view,
-        SelectPokemonModel*,
-        model,
-        {
-            model->current_level = app->current_level;
-        },
-        true);
-
-    view_set_draw_callback(select_level->view, select_level_render_callback);
-    view_set_input_callback(select_level->view, select_level_input_callback);
-    view_set_enter_callback(select_level->view, select_level_enter_callback);
-    view_set_custom_callback(select_level->view, select_level_custom_callback);
-
-    view_set_exit_callback(select_level->view, select_level_exit_callback);
-    return select_level;
-}
-
-void select_level_free(App* app) {
-    furi_assert(app->select_level);
-    view_free(app->select_level->view);
-    free(app->select_level);
-}
-
-View* select_level_get_view(App* app) {
-    furi_assert(app->select_level);
-    return app->select_level->view;
-}

+ 0 - 25
views/select_level.hpp

@@ -1,25 +0,0 @@
-#ifndef SELECCT_LEVEL_HPP
-#define SELECCT_LEVEL_HPP
-
-#pragma once
-#include <furi.h>
-#include <gui/view.h>
-#include <gui/view_dispatcher.h>
-
-#include <gui/elements.h>
-#include <string>
-
-typedef struct App App;
-
-typedef struct {
-    View* view;
-    App* app;
-} SelectLevel;
-
-SelectLevel* select_level_alloc(App* app);
-
-void select_level_free(App* app);
-
-View* select_level_get_view(App* app);
-
-#endif /* SELECCT_LEVEL_HPP */

+ 0 - 172
views/select_move1.cpp

@@ -1,172 +0,0 @@
-#include "../pokemon_app.h"
-#include "select_move1.hpp"
-
-static void select_move1_render_callback(Canvas* canvas, void* context) {
-    canvas_clear(canvas);
-
-    SelectPokemonModel* model = (SelectPokemonModel*)context;
-    const uint8_t current_index = model->current_move;
-    char move_num[5];
-
-    snprintf(move_num, sizeof(move_num), "#%03d", current_index + 1);
-    canvas_set_font(canvas, FontPrimary);
-    canvas_draw_str_aligned(
-        canvas, 55, 54 / 2, AlignLeft, AlignTop, move_table[current_index].name);
-
-    canvas_set_font(canvas, FontSecondary);
-    canvas_draw_str_aligned(canvas, 55, 38, AlignLeft, AlignTop, move_num);
-    canvas_draw_icon(canvas, 0, 0, pokemon_table[model->current_pokemon].icon);
-    canvas_draw_icon(canvas, 128 - 80, 0, &I_Space_80x18);
-    canvas_draw_str_aligned(canvas, (128 - 40), 5, AlignCenter, AlignTop, "Select Move #1");
-
-    canvas_set_font(canvas, FontPrimary);
-    elements_button_center(canvas, "OK");
-}
-
-static bool select_move1_input_callback(InputEvent* event, void* context) {
-    furi_assert(context);
-    SelectMove1* select_move1 = (SelectMove1*)context;
-    bool consumed = false;
-
-    if(event->type == InputTypePress && event->key == InputKeyOk) {
-        with_view_model_cpp(
-            select_move1->view,
-            SelectPokemonModel*,
-            model,
-            {
-                select_move1->app->current_move = model->current_move;
-                select_move1->app->move1_hex_code = move_table[model->current_move].hex;
-            },
-            false);
-        view_dispatcher_switch_to_view(select_move1->app->view_dispatcher, AppViewSelectMove2);
-        consumed = true;
-    } else if(event->type == InputTypePress && event->key == InputKeyBack) {
-        view_dispatcher_switch_to_view(select_move1->app->view_dispatcher, AppViewSelectStats);
-        consumed = true;
-    } else if(event->type == InputTypePress && event->key == InputKeyLeft) {
-        with_view_model_cpp(
-            select_move1->view,
-            SelectPokemonModel*,
-            model,
-            {
-                if(model->current_move == 0) {
-                    model->current_move = 165;
-                } else {
-                    model->current_move--;
-                }
-            },
-            true);
-        consumed = true;
-    } else if(event->type == InputTypePress && event->key == InputKeyDown) {
-        with_view_model_cpp(
-            select_move1->view,
-            SelectPokemonModel*,
-            model,
-            {
-                if(model->current_move >= 10) {
-                    model->current_move -= 10;
-                } else {
-                    model->current_move = 165;
-                }
-            },
-            true);
-        consumed = true;
-    } else if(event->type == InputTypePress && event->key == InputKeyRight) {
-        with_view_model_cpp(
-            select_move1->view,
-            SelectPokemonModel*,
-            model,
-            {
-                if(model->current_move == 165) {
-                    model->current_move = 0;
-                } else {
-                    model->current_move++;
-                }
-            },
-            true);
-        consumed = true;
-    } else if(event->type == InputTypePress && event->key == InputKeyUp) {
-        with_view_model_cpp(
-            select_move1->view,
-            SelectPokemonModel*,
-            model,
-            {
-                if(model->current_move <= 155) {
-                    model->current_move += 10;
-                } else {
-                    model->current_move = 0;
-                    ;
-                }
-            },
-            true);
-        consumed = true;
-    }
-
-    return consumed;
-}
-
-void select_move1_enter_callback(void* context) {
-    furi_assert(context);
-    SelectMove1* select_move1 = (SelectMove1*)context;
-    with_view_model_cpp(
-        select_move1->view,
-        SelectPokemonModel*,
-        model,
-        {
-            model->current_pokemon = select_move1->app->current_pokemon;
-            model->pokemon_hex_code = select_move1->app->pokemon_hex_code;
-            model->current_level = select_move1->app->current_level;
-            model->current_stats = select_move1->app->current_stats;
-            model->move1_hex_code = select_move1->app->move1_hex_code;
-        },
-        true);
-}
-
-bool select_move1_custom_callback(uint32_t event, void* context) {
-    UNUSED(event);
-    furi_assert(context);
-    SelectMove1* select_move1 = (SelectMove1*)context;
-    view_dispatcher_send_custom_event(select_move1->app->view_dispatcher, 0);
-    return true;
-}
-
-void select_move1_exit_callback(void* context) {
-    furi_assert(context);
-    UNUSED(context);
-}
-
-SelectMove1* select_move1_alloc(App* app) {
-    SelectMove1* select_move1 = (SelectMove1*)malloc(sizeof(SelectMove1));
-    select_move1->app = app;
-    select_move1->view = view_alloc();
-    view_set_context(select_move1->view, select_move1);
-    view_allocate_model(select_move1->view, ViewModelTypeLockFree, sizeof(SelectPokemonModel));
-    with_view_model_cpp(
-        select_move1->view,
-        SelectPokemonModel*,
-        model,
-        {
-            model->current_move = app->current_move;
-            model->move1_hex_code = app->move1_hex_code;
-        },
-        true);
-
-    view_set_draw_callback(select_move1->view, select_move1_render_callback);
-    view_set_input_callback(select_move1->view, select_move1_input_callback);
-    view_set_enter_callback(select_move1->view, select_move1_enter_callback);
-    view_set_custom_callback(select_move1->view, select_move1_custom_callback);
-
-    view_set_exit_callback(select_move1->view, select_move1_exit_callback);
-    return select_move1;
-}
-
-void select_move1_free(App* app) {
-    furi_assert(app->select_move1);
-    view_free(app->select_move1->view);
-    free(app->select_move1);
-}
-
-View* select_move1_get_view(App* app) {
-    furi_assert(app->select_move1);
-    return app->select_move1->view;
-}

+ 0 - 25
views/select_move1.hpp

@@ -1,25 +0,0 @@
-#ifndef SELECCT_MOVE1_HPP
-#define SELECCT_MOVE1_HPP
-
-#pragma once
-#include <furi.h>
-#include <gui/view.h>
-#include <gui/view_dispatcher.h>
-
-#include <gui/elements.h>
-#include <string>
-
-typedef struct App App;
-
-typedef struct {
-    View* view;
-    App* app;
-} SelectMove1;
-
-SelectMove1* select_move1_alloc(App* app);
-
-void select_move1_free(App* app);
-
-View* select_move1_get_view(App* app);
-
-#endif /* SELECCT_MOVE1_HPP */

+ 0 - 173
views/select_move2.cpp

@@ -1,173 +0,0 @@
-#include "../pokemon_app.h"
-#include "select_move2.hpp"
-
-static void select_move2_render_callback(Canvas* canvas, void* context) {
-    canvas_clear(canvas);
-
-    SelectPokemonModel* model = (SelectPokemonModel*)context;
-    const uint8_t current_index = model->current_move;
-    char move_num[5];
-
-    snprintf(move_num, sizeof(move_num), "#%03d", current_index + 1);
-    canvas_set_font(canvas, FontPrimary);
-    canvas_draw_str_aligned(
-        canvas, 55, 54 / 2, AlignLeft, AlignTop, move_table[current_index].name);
-
-    canvas_set_font(canvas, FontSecondary);
-    canvas_draw_str_aligned(canvas, 55, 38, AlignLeft, AlignTop, move_num);
-    canvas_draw_icon(canvas, 0, 0, pokemon_table[model->current_pokemon].icon);
-    canvas_draw_icon(canvas, 128 - 80, 0, &I_Space_80x18);
-    canvas_draw_str_aligned(canvas, (128 - 40), 5, AlignCenter, AlignTop, "Select Move #2");
-
-    canvas_set_font(canvas, FontPrimary);
-    elements_button_center(canvas, "OK");
-}
-
-static bool select_move2_input_callback(InputEvent* event, void* context) {
-    furi_assert(context);
-    SelectMove2* select_move2 = (SelectMove2*)context;
-    bool consumed = false;
-
-    if(event->type == InputTypePress && event->key == InputKeyOk) {
-        with_view_model_cpp(
-            select_move2->view,
-            SelectPokemonModel*,
-            model,
-            {
-                select_move2->app->current_move = model->current_move;
-                select_move2->app->move2_hex_code = move_table[model->current_move].hex;
-            },
-            false);
-        view_dispatcher_switch_to_view(select_move2->app->view_dispatcher, AppViewSelectMove3);
-        consumed = true;
-    } else if(event->type == InputTypePress && event->key == InputKeyBack) {
-        view_dispatcher_switch_to_view(select_move2->app->view_dispatcher, AppViewSelectMove1);
-        consumed = true;
-    } else if(event->type == InputTypePress && event->key == InputKeyLeft) {
-        with_view_model_cpp(
-            select_move2->view,
-            SelectPokemonModel*,
-            model,
-            {
-                if(model->current_move == 0) {
-                    model->current_move = 165;
-                } else {
-                    model->current_move--;
-                }
-            },
-            true);
-        consumed = true;
-    } else if(event->type == InputTypePress && event->key == InputKeyDown) {
-        with_view_model_cpp(
-            select_move2->view,
-            SelectPokemonModel*,
-            model,
-            {
-                if(model->current_move >= 10) {
-                    model->current_move -= 10;
-                } else {
-                    model->current_move = 165;
-                }
-            },
-            true);
-        consumed = true;
-    } else if(event->type == InputTypePress && event->key == InputKeyRight) {
-        with_view_model_cpp(
-            select_move2->view,
-            SelectPokemonModel*,
-            model,
-            {
-                if(model->current_move == 165) {
-                    model->current_move = 0;
-                } else {
-                    model->current_move++;
-                }
-            },
-            true);
-        consumed = true;
-    } else if(event->type == InputTypePress && event->key == InputKeyUp) {
-        with_view_model_cpp(
-            select_move2->view,
-            SelectPokemonModel*,
-            model,
-            {
-                if(model->current_move <= 155) {
-                    model->current_move += 10;
-                } else {
-                    model->current_move = 0;
-                    ;
-                }
-            },
-            true);
-        consumed = true;
-    }
-
-    return consumed;
-}
-
-void select_move2_enter_callback(void* context) {
-    furi_assert(context);
-    SelectMove2* select_move2 = (SelectMove2*)context;
-    with_view_model_cpp(
-        select_move2->view,
-        SelectPokemonModel*,
-        model,
-        {
-            model->current_pokemon = select_move2->app->current_pokemon;
-            model->pokemon_hex_code = select_move2->app->pokemon_hex_code;
-            model->current_level = select_move2->app->current_level;
-            model->current_stats = select_move2->app->current_stats;
-            model->move1_hex_code = select_move2->app->move1_hex_code;
-            model->move2_hex_code = select_move2->app->move2_hex_code;
-        },
-        true);
-}
-
-bool select_move2_custom_callback(uint32_t event, void* context) {
-    UNUSED(event);
-    furi_assert(context);
-    SelectMove2* select_move2 = (SelectMove2*)context;
-    view_dispatcher_send_custom_event(select_move2->app->view_dispatcher, 0);
-    return true;
-}
-
-void select_move2_exit_callback(void* context) {
-    furi_assert(context);
-    UNUSED(context);
-}
-
-SelectMove2* select_move2_alloc(App* app) {
-    SelectMove2* select_move2 = (SelectMove2*)malloc(sizeof(SelectMove2));
-    select_move2->app = app;
-    select_move2->view = view_alloc();
-    view_set_context(select_move2->view, select_move2);
-    view_allocate_model(select_move2->view, ViewModelTypeLockFree, sizeof(SelectPokemonModel));
-    with_view_model_cpp(
-        select_move2->view,
-        SelectPokemonModel*,
-        model,
-        {
-            model->current_move = app->current_move;
-            model->move2_hex_code = app->move2_hex_code;
-        },
-        true);
-
-    view_set_draw_callback(select_move2->view, select_move2_render_callback);
-    view_set_input_callback(select_move2->view, select_move2_input_callback);
-    view_set_enter_callback(select_move2->view, select_move2_enter_callback);
-    view_set_custom_callback(select_move2->view, select_move2_custom_callback);
-
-    view_set_exit_callback(select_move2->view, select_move2_exit_callback);
-    return select_move2;
-}
-
-void select_move2_free(App* app) {
-    furi_assert(app->select_move2);
-    view_free(app->select_move2->view);
-    free(app->select_move2);
-}
-
-View* select_move2_get_view(App* app) {
-    furi_assert(app->select_move2);
-    return app->select_move2->view;
-}

+ 0 - 25
views/select_move2.hpp

@@ -1,25 +0,0 @@
-#ifndef SELECCT_MOVE2_HPP
-#define SELECCT_MOVE2_HPP
-
-#pragma once
-#include <furi.h>
-#include <gui/view.h>
-#include <gui/view_dispatcher.h>
-
-#include <gui/elements.h>
-#include <string>
-
-typedef struct App App;
-
-typedef struct {
-    View* view;
-    App* app;
-} SelectMove2;
-
-SelectMove2* select_move2_alloc(App* app);
-
-void select_move2_free(App* app);
-
-View* select_move2_get_view(App* app);
-
-#endif /* SELECCT_MOVE2_HPP */

+ 0 - 174
views/select_move3.cpp

@@ -1,174 +0,0 @@
-#include "../pokemon_app.h"
-#include "select_move3.hpp"
-
-static void select_move3_render_callback(Canvas* canvas, void* context) {
-    canvas_clear(canvas);
-
-    SelectPokemonModel* model = (SelectPokemonModel*)context;
-    const uint8_t current_index = model->current_move;
-    char move_num[5];
-
-    snprintf(move_num, sizeof(move_num), "#%03d", current_index + 1);
-    canvas_set_font(canvas, FontPrimary);
-    canvas_draw_str_aligned(
-        canvas, 55, 54 / 2, AlignLeft, AlignTop, move_table[current_index].name);
-
-    canvas_set_font(canvas, FontSecondary);
-    canvas_draw_str_aligned(canvas, 55, 38, AlignLeft, AlignTop, move_num);
-    canvas_draw_icon(canvas, 0, 0, pokemon_table[model->current_pokemon].icon);
-    canvas_draw_icon(canvas, 128 - 80, 0, &I_Space_80x18);
-    canvas_draw_str_aligned(canvas, (128 - 40), 5, AlignCenter, AlignTop, "Select Move #3");
-
-    canvas_set_font(canvas, FontPrimary);
-    elements_button_center(canvas, "OK");
-}
-
-static bool select_move3_input_callback(InputEvent* event, void* context) {
-    furi_assert(context);
-    SelectMove3* select_move3 = (SelectMove3*)context;
-    bool consumed = false;
-
-    if(event->type == InputTypePress && event->key == InputKeyOk) {
-        with_view_model_cpp(
-            select_move3->view,
-            SelectPokemonModel*,
-            model,
-            {
-                select_move3->app->current_move = model->current_move;
-                select_move3->app->move3_hex_code = move_table[model->current_move].hex;
-            },
-            false);
-        view_dispatcher_switch_to_view(select_move3->app->view_dispatcher, AppViewSelectMove4);
-        consumed = true;
-    } else if(event->type == InputTypePress && event->key == InputKeyBack) {
-        view_dispatcher_switch_to_view(select_move3->app->view_dispatcher, AppViewSelectMove2);
-        consumed = true;
-    } else if(event->type == InputTypePress && event->key == InputKeyLeft) {
-        with_view_model_cpp(
-            select_move3->view,
-            SelectPokemonModel*,
-            model,
-            {
-                if(model->current_move == 0) {
-                    model->current_move = 165;
-                } else {
-                    model->current_move--;
-                }
-            },
-            true);
-        consumed = true;
-    } else if(event->type == InputTypePress && event->key == InputKeyDown) {
-        with_view_model_cpp(
-            select_move3->view,
-            SelectPokemonModel*,
-            model,
-            {
-                if(model->current_move >= 10) {
-                    model->current_move -= 10;
-                } else {
-                    model->current_move = 165;
-                }
-            },
-            true);
-        consumed = true;
-    } else if(event->type == InputTypePress && event->key == InputKeyRight) {
-        with_view_model_cpp(
-            select_move3->view,
-            SelectPokemonModel*,
-            model,
-            {
-                if(model->current_move == 165) {
-                    model->current_move = 0;
-                } else {
-                    model->current_move++;
-                }
-            },
-            true);
-        consumed = true;
-    } else if(event->type == InputTypePress && event->key == InputKeyUp) {
-        with_view_model_cpp(
-            select_move3->view,
-            SelectPokemonModel*,
-            model,
-            {
-                if(model->current_move <= 155) {
-                    model->current_move += 10;
-                } else {
-                    model->current_move = 0;
-                    ;
-                }
-            },
-            true);
-        consumed = true;
-    }
-
-    return consumed;
-}
-
-void select_move3_enter_callback(void* context) {
-    furi_assert(context);
-    SelectMove3* select_move3 = (SelectMove3*)context;
-    with_view_model_cpp(
-        select_move3->view,
-        SelectPokemonModel*,
-        model,
-        {
-            model->current_pokemon = select_move3->app->current_pokemon;
-            model->pokemon_hex_code = select_move3->app->pokemon_hex_code;
-            model->current_level = select_move3->app->current_level;
-            model->current_stats = select_move3->app->current_stats;
-            model->move1_hex_code = select_move3->app->move1_hex_code;
-            model->move2_hex_code = select_move3->app->move2_hex_code;
-            model->move3_hex_code = select_move3->app->move3_hex_code;
-        },
-        true);
-}
-
-bool select_move3_custom_callback(uint32_t event, void* context) {
-    UNUSED(event);
-    furi_assert(context);
-    SelectMove3* select_move3 = (SelectMove3*)context;
-    view_dispatcher_send_custom_event(select_move3->app->view_dispatcher, 0);
-    return true;
-}
-
-void select_move3_exit_callback(void* context) {
-    furi_assert(context);
-    UNUSED(context);
-}
-
-SelectMove3* select_move3_alloc(App* app) {
-    SelectMove3* select_move3 = (SelectMove3*)malloc(sizeof(SelectMove3));
-    select_move3->app = app;
-    select_move3->view = view_alloc();
-    view_set_context(select_move3->view, select_move3);
-    view_allocate_model(select_move3->view, ViewModelTypeLockFree, sizeof(SelectPokemonModel));
-    with_view_model_cpp(
-        select_move3->view,
-        SelectPokemonModel*,
-        model,
-        {
-            model->current_move = app->current_move;
-            model->move3_hex_code = app->move3_hex_code;
-        },
-        true);
-
-    view_set_draw_callback(select_move3->view, select_move3_render_callback);
-    view_set_input_callback(select_move3->view, select_move3_input_callback);
-    view_set_enter_callback(select_move3->view, select_move3_enter_callback);
-    view_set_custom_callback(select_move3->view, select_move3_custom_callback);
-
-    view_set_exit_callback(select_move3->view, select_move3_exit_callback);
-    return select_move3;
-}
-
-void select_move3_free(App* app) {
-    furi_assert(app->select_move3);
-    view_free(app->select_move3->view);
-    free(app->select_move3);
-}
-
-View* select_move3_get_view(App* app) {
-    furi_assert(app->select_move3);
-    return app->select_move3->view;
-}

+ 0 - 25
views/select_move3.hpp

@@ -1,25 +0,0 @@
-#ifndef SELECCT_MOVE3_HPP
-#define SELECCT_MOVE3_HPP
-
-#pragma once
-#include <furi.h>
-#include <gui/view.h>
-#include <gui/view_dispatcher.h>
-
-#include <gui/elements.h>
-#include <string>
-
-typedef struct App App;
-
-typedef struct {
-    View* view;
-    App* app;
-} SelectMove3;
-
-SelectMove3* select_move3_alloc(App* app);
-
-void select_move3_free(App* app);
-
-View* select_move3_get_view(App* app);
-
-#endif /* SELECCT_MOVE3_HPP */

+ 0 - 174
views/select_move4.cpp

@@ -1,174 +0,0 @@
-#include "../pokemon_app.h"
-#include "select_move4.hpp"
-
-static void select_move4_render_callback(Canvas* canvas, void* context) {
-    canvas_clear(canvas);
-
-    SelectPokemonModel* model = (SelectPokemonModel*)context;
-    const uint8_t current_index = model->current_move;
-    char move_num[5];
-
-    snprintf(move_num, sizeof(move_num), "#%03d", current_index + 1);
-    canvas_set_font(canvas, FontPrimary);
-    canvas_draw_str_aligned(
-        canvas, 55, 54 / 2, AlignLeft, AlignTop, move_table[current_index].name);
-
-    canvas_set_font(canvas, FontSecondary);
-    canvas_draw_str_aligned(canvas, 55, 38, AlignLeft, AlignTop, move_num);
-    canvas_draw_icon(canvas, 0, 0, pokemon_table[model->current_pokemon].icon);
-    canvas_draw_icon(canvas, 128 - 80, 0, &I_Space_80x18);
-    canvas_draw_str_aligned(canvas, (128 - 40), 5, AlignCenter, AlignTop, "Select Move #4");
-
-    canvas_set_font(canvas, FontPrimary);
-    elements_button_center(canvas, "OK");
-}
-
-static bool select_move4_input_callback(InputEvent* event, void* context) {
-    furi_assert(context);
-    SelectMove4* select_move4 = (SelectMove4*)context;
-    bool consumed = false;
-
-    if(event->type == InputTypePress && event->key == InputKeyOk) {
-        with_view_model_cpp(
-            select_move4->view,
-            SelectPokemonModel*,
-            model,
-            {
-                select_move4->app->current_move = model->current_move;
-                select_move4->app->move4_hex_code = move_table[model->current_move].hex;
-            },
-            false);
-        view_dispatcher_switch_to_view(select_move4->app->view_dispatcher, AppViewTrade);
-        consumed = true;
-    } else if(event->type == InputTypePress && event->key == InputKeyBack) {
-        view_dispatcher_switch_to_view(select_move4->app->view_dispatcher, AppViewSelectMove3);
-        consumed = true;
-    } else if(event->type == InputTypePress && event->key == InputKeyLeft) {
-        with_view_model_cpp(
-            select_move4->view,
-            SelectPokemonModel*,
-            model,
-            {
-                if(model->current_move == 0) {
-                    model->current_move = 165;
-                } else {
-                    model->current_move--;
-                }
-            },
-            true);
-        consumed = true;
-    } else if(event->type == InputTypePress && event->key == InputKeyDown) {
-        with_view_model_cpp(
-            select_move4->view,
-            SelectPokemonModel*,
-            model,
-            {
-                if(model->current_move >= 10) {
-                    model->current_move -= 10;
-                } else {
-                    model->current_move = 165;
-                }
-            },
-            true);
-        consumed = true;
-    } else if(event->type == InputTypePress && event->key == InputKeyRight) {
-        with_view_model_cpp(
-            select_move4->view,
-            SelectPokemonModel*,
-            model,
-            {
-                if(model->current_move == 165) {
-                    model->current_move = 0;
-                } else {
-                    model->current_move++;
-                }
-            },
-            true);
-        consumed = true;
-    } else if(event->type == InputTypePress && event->key == InputKeyUp) {
-        with_view_model_cpp(
-            select_move4->view,
-            SelectPokemonModel*,
-            model,
-            {
-                if(model->current_move <= 155) {
-                    model->current_move += 10;
-                } else {
-                    model->current_move = 0;
-                    ;
-                }
-            },
-            true);
-        consumed = true;
-    }
-
-    return consumed;
-}
-
-void select_move4_enter_callback(void* context) {
-    furi_assert(context);
-    SelectMove4* select_move4 = (SelectMove4*)context;
-    with_view_model_cpp(
-        select_move4->view,
-        SelectPokemonModel*,
-        model,
-        {
-            model->current_pokemon = select_move4->app->current_pokemon;
-            model->pokemon_hex_code = select_move4->app->pokemon_hex_code;
-            model->current_level = select_move4->app->current_level;
-            model->current_stats = select_move4->app->current_stats;
-            model->move1_hex_code = select_move4->app->move1_hex_code;
-            model->move2_hex_code = select_move4->app->move2_hex_code;
-            model->move3_hex_code = select_move4->app->move3_hex_code;
-        },
-        true);
-}
-
-bool select_move4_custom_callback(uint32_t event, void* context) {
-    UNUSED(event);
-    furi_assert(context);
-    SelectMove4* select_move4 = (SelectMove4*)context;
-    view_dispatcher_send_custom_event(select_move4->app->view_dispatcher, 0);
-    return true;
-}
-
-void select_move4_exit_callback(void* context) {
-    furi_assert(context);
-    UNUSED(context);
-}
-
-SelectMove4* select_move4_alloc(App* app) {
-    SelectMove4* select_move4 = (SelectMove4*)malloc(sizeof(SelectMove4));
-    select_move4->app = app;
-    select_move4->view = view_alloc();
-    view_set_context(select_move4->view, select_move4);
-    view_allocate_model(select_move4->view, ViewModelTypeLockFree, sizeof(SelectPokemonModel));
-    with_view_model_cpp(
-        select_move4->view,
-        SelectPokemonModel*,
-        model,
-        {
-            model->current_move = app->current_move;
-            model->move4_hex_code = app->move4_hex_code;
-        },
-        true);
-
-    view_set_draw_callback(select_move4->view, select_move4_render_callback);
-    view_set_input_callback(select_move4->view, select_move4_input_callback);
-    view_set_enter_callback(select_move4->view, select_move4_enter_callback);
-    view_set_custom_callback(select_move4->view, select_move4_custom_callback);
-
-    view_set_exit_callback(select_move4->view, select_move4_exit_callback);
-    return select_move4;
-}
-
-void select_move4_free(App* app) {
-    furi_assert(app->select_move4);
-    view_free(app->select_move4->view);
-    free(app->select_move4);
-}
-
-View* select_move4_get_view(App* app) {
-    furi_assert(app->select_move4);
-    return app->select_move4->view;
-}

+ 0 - 25
views/select_move4.hpp

@@ -1,25 +0,0 @@
-#ifndef SELECCT_MOVE4_HPP
-#define SELECCT_MOVE4_HPP
-
-#pragma once
-#include <furi.h>
-#include <gui/view.h>
-#include <gui/view_dispatcher.h>
-
-#include <gui/elements.h>
-#include <string>
-
-typedef struct App App;
-
-typedef struct {
-    View* view;
-    App* app;
-} SelectMove4;
-
-SelectMove4* select_move4_alloc(App* app);
-
-void select_move4_free(App* app);
-
-View* select_move4_get_view(App* app);
-
-#endif /* SELECCT_MOVE4_HPP */

+ 125 - 0
views/select_pokemon.c

@@ -0,0 +1,125 @@
+#include <gui/elements.h>
+#include <pokemon_icons.h>
+
+#include "../scenes/pokemon_menu.h"
+#include "../pokemon_app.h"
+
+int selected_pokemon;
+
+static void select_pokemon_render_callback(Canvas* canvas, void* model) {
+    PokemonFap* pokemon_fap = *(PokemonFap**)model;
+    const uint8_t current_index = selected_pokemon;
+    char pokedex_num[5];
+
+    snprintf(pokedex_num, sizeof(pokedex_num), "#%03d", current_index + 1);
+    canvas_set_font(canvas, FontPrimary);
+    canvas_draw_str_aligned(
+        canvas, 55, 54 / 2, AlignLeft, AlignTop, pokemon_fap->pokemon_table[current_index].name);
+
+    canvas_set_font(canvas, FontSecondary);
+    canvas_draw_str_aligned(canvas, 55, 38, AlignLeft, AlignTop, pokedex_num);
+    canvas_draw_icon(canvas, 0, 0, pokemon_fap->pokemon_table[current_index].icon);
+    canvas_draw_icon(canvas, 128 - 80, 0, &I_Space_80x18);
+    canvas_draw_str_aligned(canvas, (128 - 40), 5, AlignCenter, AlignTop, "Select Pokemon");
+
+    canvas_set_font(canvas, FontPrimary);
+    elements_button_center(canvas, "OK");
+}
+
+static bool select_pokemon_input_callback(InputEvent* event, void* context) {
+    PokemonFap* pokemon_fap = (PokemonFap*)context;
+    bool consumed = false;
+
+    furi_assert(context);
+
+    /* We only handle InputTypePress at the moment */
+    if(event->type != InputTypePress) return consumed;
+
+    switch(event->key) {
+    /* Advance to next view with the selected pokemon */
+    case InputKeyOk:
+        pokemon_fap->curr_pokemon = selected_pokemon;
+        scene_manager_previous_scene(pokemon_fap->scene_manager);
+        consumed = true;
+        break;
+
+    /* Move back one through the pokedex listing */
+    case InputKeyLeft:
+        if(selected_pokemon == 0)
+            selected_pokemon = 150;
+        else
+            selected_pokemon--;
+        consumed = true;
+        break;
+
+    /* Move back ten through the pokemon listing, wrap to max pokemon on
+         * underflow.
+         */
+    case InputKeyDown:
+        if(selected_pokemon >= 10)
+            selected_pokemon -= 10;
+        else
+            selected_pokemon = 150;
+        consumed = true;
+        break;
+
+    /* Move forward one through the pokedex listing */
+    case InputKeyRight:
+        if(selected_pokemon == 150)
+            selected_pokemon = 0;
+        else
+            selected_pokemon++;
+        consumed = true;
+        break;
+
+    /* Move forward ten through the pokemon listing, wrap to min pokemon on
+         * overflow.
+         */
+    case InputKeyUp:
+        if(selected_pokemon <= 140)
+            selected_pokemon += 10;
+        else
+            selected_pokemon = 0;
+        consumed = true;
+        break;
+
+    default:
+        // Do Nothing
+        break;
+    }
+    with_view_model(
+        pokemon_fap->select_view,
+        PokemonFap* model,
+        {
+            model->curr_pokemon = selected_pokemon;
+        },
+        true);
+    return consumed;
+}
+
+void select_pokemon_enter_callback(void* context) {
+    PokemonFap* pokemon_fap = (PokemonFap*)context;
+    selected_pokemon = pokemon_fap->curr_pokemon;
+}
+
+View* select_pokemon_alloc(PokemonFap* pokemon_fap) {
+    View* view;
+
+    view = view_alloc();
+
+    view_set_context(view, pokemon_fap);
+    view_allocate_model(view, ViewModelTypeLockFree, sizeof(PokemonFap**));
+    with_view_model(
+        view, PokemonFap** model_fap, { *model_fap = pokemon_fap; }, false);
+
+    view_set_draw_callback(view, select_pokemon_render_callback);
+    view_set_input_callback(view, select_pokemon_input_callback);
+    view_set_enter_callback(view, select_pokemon_enter_callback);
+    return view;
+}
+
+void select_pokemon_free(PokemonFap* pokemon_fap) {
+    furi_assert(pokemon_fap);
+    view_free_model(pokemon_fap->select_view);
+    view_free(pokemon_fap->select_view);
+}

+ 0 - 161
views/select_pokemon.cpp

@@ -1,161 +0,0 @@
-#include "../pokemon_app.h"
-#include "select_pokemon.hpp"
-
-static void select_pokemon_render_callback(Canvas* canvas, void* context) {
-    canvas_clear(canvas);
-
-    SelectPokemonModel* model = (SelectPokemonModel*)context;
-    const uint8_t current_index = model->current_pokemon;
-    char pokedex_num[5];
-
-    snprintf(pokedex_num, sizeof(pokedex_num), "#%03d", current_index + 1);
-    canvas_set_font(canvas, FontPrimary);
-    canvas_draw_str_aligned(
-        canvas, 55, 54 / 2, AlignLeft, AlignTop, pokemon_table[current_index].name);
-
-    canvas_set_font(canvas, FontSecondary);
-    canvas_draw_str_aligned(canvas, 55, 38, AlignLeft, AlignTop, pokedex_num);
-    canvas_draw_icon(canvas, 0, 0, pokemon_table[current_index].icon);
-    canvas_draw_icon(canvas, 128 - 80, 0, &I_Space_80x18);
-    canvas_draw_str_aligned(canvas, (128 - 40), 5, AlignCenter, AlignTop, "Select Pokemon");
-
-    canvas_set_font(canvas, FontPrimary);
-    elements_button_center(canvas, "OK");
-}
-
-static bool select_pokemon_input_callback(InputEvent* event, void* context) {
-    furi_assert(context);
-    SelectPokemon* select_pokemon = (SelectPokemon*)context;
-    bool consumed = false;
-
-    if(event->type == InputTypePress && event->key == InputKeyOk) {
-        with_view_model_cpp(
-            select_pokemon->view,
-            SelectPokemonModel*,
-            model,
-            {
-                select_pokemon->app->current_pokemon = model->current_pokemon;
-                select_pokemon->app->pokemon_hex_code = pokemon_table[model->current_pokemon].hex;
-            },
-            false);
-
-        view_dispatcher_switch_to_view(select_pokemon->app->view_dispatcher, AppViewSelectLevel);
-        consumed = true;
-    } else if(event->type == InputTypePress && event->key == InputKeyBack) {
-        view_dispatcher_switch_to_view(select_pokemon->app->view_dispatcher, VIEW_NONE);
-        consumed = true;
-    } else if(event->type == InputTypePress && event->key == InputKeyLeft) {
-        with_view_model_cpp(
-            select_pokemon->view,
-            SelectPokemonModel*,
-            model,
-            {
-                if(model->current_pokemon == 0) {
-                    model->current_pokemon = 150;
-                } else {
-                    model->current_pokemon--;
-                }
-            },
-            true);
-        consumed = true;
-    } else if(event->type == InputTypePress && event->key == InputKeyDown) {
-        with_view_model_cpp(
-            select_pokemon->view,
-            SelectPokemonModel*,
-            model,
-            {
-                if(model->current_pokemon >= 10) {
-                    model->current_pokemon -= 10;
-                } else {
-                    model->current_pokemon = 150;
-                }
-            },
-            true);
-        consumed = true;
-    } else if(event->type == InputTypePress && event->key == InputKeyRight) {
-        with_view_model_cpp(
-            select_pokemon->view,
-            SelectPokemonModel*,
-            model,
-            {
-                if(model->current_pokemon == 150) {
-                    model->current_pokemon = 0;
-                } else {
-                    model->current_pokemon++;
-                }
-            },
-            true);
-        consumed = true;
-    } else if(event->type == InputTypePress && event->key == InputKeyUp) {
-        with_view_model_cpp(
-            select_pokemon->view,
-            SelectPokemonModel*,
-            model,
-            {
-                if(model->current_pokemon <= 140) {
-                    model->current_pokemon += 10;
-                } else {
-                    model->current_pokemon = 0;
-                    ;
-                }
-            },
-            true);
-        consumed = true;
-    }
-
-    return consumed;
-}
-
-void select_pokemon_enter_callback(void* context) {
-    furi_assert(context);
-    UNUSED(context);
-}
-
-bool select_pokemon_custom_callback(uint32_t event, void* context) {
-    UNUSED(event);
-    furi_assert(context);
-    SelectPokemon* select_pokemon = (SelectPokemon*)context;
-    view_dispatcher_send_custom_event(select_pokemon->app->view_dispatcher, 0);
-    return true;
-}
-
-void select_pokemon_exit_callback(void* context) {
-    furi_assert(context);
-    UNUSED(context);
-}
-
-SelectPokemon* select_pokemon_alloc(App* app) {
-    SelectPokemon* select_pokemon = (SelectPokemon*)malloc(sizeof(SelectPokemon));
-    select_pokemon->app = app;
-    select_pokemon->view = view_alloc();
-    view_set_context(select_pokemon->view, select_pokemon);
-    view_allocate_model(select_pokemon->view, ViewModelTypeLockFree, sizeof(SelectPokemonModel));
-    with_view_model_cpp(
-        select_pokemon->view,
-        SelectPokemonModel*,
-        model,
-        {
-            model->current_pokemon = app->current_pokemon;
-            model->pokemon_hex_code = app->pokemon_hex_code;
-        },
-        true);
-
-    view_set_draw_callback(select_pokemon->view, select_pokemon_render_callback);
-    view_set_input_callback(select_pokemon->view, select_pokemon_input_callback);
-    view_set_enter_callback(select_pokemon->view, select_pokemon_enter_callback);
-    view_set_custom_callback(select_pokemon->view, select_pokemon_custom_callback);
-
-    view_set_exit_callback(select_pokemon->view, select_pokemon_exit_callback);
-    return select_pokemon;
-}
-
-void select_pokemon_free(App* app) {
-    furi_assert(app->select_pokemon);
-    view_free(app->select_pokemon->view);
-    free(app->select_pokemon);
-}
-
-View* select_pokemon_get_view(App* app) {
-    furi_assert(app->select_pokemon);
-    return app->select_pokemon->view;
-}

+ 13 - 0
views/select_pokemon.h

@@ -0,0 +1,13 @@
+#ifndef SELECCT_POKEMON_H
+#define SELECCT_POKEMON_H
+
+#pragma once
+
+#include <gui/view.h>
+#include "../pokemon_app.h"
+
+View* select_pokemon_alloc(PokemonFap* pokemon_fap);
+
+void select_pokemon_free(PokemonFap* pokemon_fap);
+
+#endif /* SELECCT_POKEMON_H */

+ 0 - 25
views/select_pokemon.hpp

@@ -1,25 +0,0 @@
-#ifndef SELECCT_POKEMON_HPP
-#define SELECCT_POKEMON_HPP
-
-#pragma once
-#include <furi.h>
-#include <gui/view.h>
-#include <gui/view_dispatcher.h>
-
-#include <gui/elements.h>
-#include <string>
-
-typedef struct App App;
-
-typedef struct {
-    View* view;
-    App* app;
-} SelectPokemon;
-
-SelectPokemon* select_pokemon_alloc(App* app);
-
-void select_pokemon_free(App* app);
-
-View* select_pokemon_get_view(App* app);
-
-#endif /* SELECCT_POKEMON_HPP */

+ 0 - 195
views/select_stats.cpp

@@ -1,195 +0,0 @@
-#include "../pokemon_app.h"
-#include "select_stats.hpp"
-
-static void select_stats_render_callback(Canvas* canvas, void* context) {
-    canvas_clear(canvas);
-
-    SelectPokemonModel* model = (SelectPokemonModel*)context;
-    const uint8_t current_index = model->current_stats;
-
-    canvas_set_font(canvas, FontPrimary);
-    if(current_index == 0) {
-        canvas_draw_str_aligned(
-            canvas, 55, 54 / 2, AlignLeft, AlignTop, "Random IV");
-        canvas_draw_str_aligned(
-            canvas, 55, 38, AlignLeft, AlignTop, "Zero EV");
-    } else if(current_index == 1) {
-        canvas_draw_str_aligned(
-            canvas, 55, 54 / 2, AlignLeft, AlignTop, "Random IV");
-        canvas_draw_str_aligned(
-            canvas, 55, 38, AlignLeft, AlignTop, "Max EV / Lvl");
-    } else if(current_index == 2) {
-        canvas_draw_str_aligned(
-            canvas, 55, 54 / 2, AlignLeft, AlignTop, "Random IV");
-        canvas_draw_str_aligned(
-            canvas, 55, 38, AlignLeft, AlignTop, "Max EV");
-    } else if(current_index == 3) {
-        canvas_draw_str_aligned(
-            canvas, 55, 54 / 2, AlignLeft, AlignTop, "Max IV");
-        canvas_draw_str_aligned(
-            canvas, 55, 38, AlignLeft, AlignTop, "Zero EV");
-    } else if(current_index == 4) {
-        canvas_draw_str_aligned(
-            canvas, 55, 54 / 2, AlignLeft, AlignTop, "Max IV");
-        canvas_draw_str_aligned(
-            canvas, 55, 38, AlignLeft, AlignTop, "Max EV / Lvl");
-    } else if(current_index == 5) {
-        canvas_draw_str_aligned(
-            canvas, 55, 54 / 2, AlignLeft, AlignTop, "Max IV");
-        canvas_draw_str_aligned(
-            canvas, 55, 38, AlignLeft, AlignTop, "Max EV");
-    }
-
-    canvas_set_font(canvas, FontSecondary);
-    canvas_draw_icon(canvas, 0, 0, pokemon_table[model->current_pokemon].icon);
-    canvas_draw_icon(canvas, 128 - 80, 0, &I_Space_80x18);
-    canvas_draw_str_aligned(canvas, (128 - 40), 5, AlignCenter, AlignTop, "Select Stats");
-
-    canvas_set_font(canvas, FontPrimary);
-    elements_button_center(canvas, "OK");
-}
-
-static bool select_stats_input_callback(InputEvent* event, void* context) {
-    furi_assert(context);
-    SelectStats* select_stats = (SelectStats*)context;
-    bool consumed = false;
-
-    if(event->type == InputTypePress && event->key == InputKeyOk) {
-        with_view_model_cpp(
-            select_stats->view,
-            SelectPokemonModel*,
-            model,
-            {
-                select_stats->app->current_stats = model->current_stats;
-            },
-            false);
-        
-        view_dispatcher_switch_to_view(select_stats->app->view_dispatcher, AppViewSelectMove1);
-        consumed = true;
-    } else if(event->type == InputTypePress && event->key == InputKeyBack) {
-        view_dispatcher_switch_to_view(select_stats->app->view_dispatcher, AppViewSelectLevel);
-        consumed = true;
-    } else if(event->type == InputTypePress && event->key == InputKeyLeft) {
-        with_view_model_cpp(
-            select_stats->view,
-            SelectPokemonModel*,
-            model,
-            {
-                if(model->current_stats == 0) {
-                    model->current_stats = 5;
-                } else {
-                    model->current_stats--;
-                }
-            },
-            true);
-        consumed = true;
-    } else if(event->type == InputTypePress && event->key == InputKeyDown) {
-        with_view_model_cpp(
-            select_stats->view,
-            SelectPokemonModel*,
-            model,
-            {
-                if(model->current_stats == 0) {
-                    model->current_stats = 5;
-                } else {
-                    model->current_stats--;
-                }
-            },
-            true);
-        consumed = true;
-    } else if(event->type == InputTypePress && event->key == InputKeyRight) {
-        with_view_model_cpp(
-            select_stats->view,
-            SelectPokemonModel*,
-            model,
-            {
-                if(model->current_stats == 5) {
-                    model->current_stats = 0;
-                } else {
-                    model->current_stats++;
-                }
-            },
-            true);
-        consumed = true;
-    } else if(event->type == InputTypePress && event->key == InputKeyUp) {
-        with_view_model_cpp(
-            select_stats->view,
-            SelectPokemonModel*,
-            model,
-            {
-                if(model->current_stats == 5) {
-                    model->current_stats = 0;
-                } else {
-                    model->current_stats++;
-                }
-            },
-            true);
-        consumed = true;
-    }
-
-    return consumed;
-}
-
-void select_stats_enter_callback(void* context) {
-    furi_assert(context);
-    SelectStats* select_stats = (SelectStats*)context;
-    with_view_model_cpp(
-        select_stats->view,
-        SelectPokemonModel*,
-        model,
-        {
-            model->current_pokemon = select_stats->app->current_pokemon;
-            model->pokemon_hex_code = select_stats->app->pokemon_hex_code;
-            model->current_level = select_stats->app->current_level;
-            model->current_stats = select_stats->app->current_stats;
-        },
-        true);
-}
-
-bool select_stats_custom_callback(uint32_t event, void* context) {
-    UNUSED(event);
-    furi_assert(context);
-    SelectStats* select_stats = (SelectStats*)context;
-    view_dispatcher_send_custom_event(select_stats->app->view_dispatcher, 0);
-    return true;
-}
-
-void select_stats_exit_callback(void* context) {
-    furi_assert(context);
-    UNUSED(context);
-}
-
-SelectStats* select_stats_alloc(App* app) {
-    SelectStats* select_stats = (SelectStats*)malloc(sizeof(SelectStats));
-    select_stats->app = app;
-    select_stats->view = view_alloc();
-    view_set_context(select_stats->view, select_stats);
-    view_allocate_model(select_stats->view, ViewModelTypeLockFree, sizeof(SelectPokemonModel));
-    with_view_model_cpp(
-        select_stats->view,
-        SelectPokemonModel*,
-        model,
-        {
-            model->current_stats = app->current_stats;
-        },
-        true);
-
-    view_set_draw_callback(select_stats->view, select_stats_render_callback);
-    view_set_input_callback(select_stats->view, select_stats_input_callback);
-    view_set_enter_callback(select_stats->view, select_stats_enter_callback);
-    view_set_custom_callback(select_stats->view, select_stats_custom_callback);
-
-    view_set_exit_callback(select_stats->view, select_stats_exit_callback);
-    return select_stats;
-}
-
-void select_stats_free(App* app) {
-    furi_assert(app->select_stats);
-    view_free(app->select_stats->view);
-    free(app->select_stats);
-}
-
-View* select_stats_get_view(App* app) {
-    furi_assert(app->select_stats);
-    return app->select_stats->view;
-}

+ 0 - 25
views/select_stats.hpp

@@ -1,25 +0,0 @@
-#ifndef SELECCT_STATS_HPP
-#define SELECCT_LEVEL_HPP
-
-#pragma once
-#include <furi.h>
-#include <gui/view.h>
-#include <gui/view_dispatcher.h>
-
-#include <gui/elements.h>
-#include <string>
-
-typedef struct App App;
-
-typedef struct {
-    View* view;
-    App* app;
-} SelectStats;
-
-SelectStats* select_stats_alloc(App* app);
-
-void select_stats_free(App* app);
-
-View* select_stats_get_view(App* app);
-
-#endif /* SELECCT_STATS_HPP */

+ 455 - 0
views/trade.c

@@ -0,0 +1,455 @@
+#include <furi_hal_light.h>
+#include <gui/view.h>
+#include <pokemon_icons.h>
+
+#include "../pokemon_app.h"
+
+#define GAME_BOY_CLK gpio_ext_pb2
+#define GAME_BOY_SI gpio_ext_pc3
+#define GAME_BOY_SO gpio_ext_pb3
+
+#define DELAY_MICROSECONDS 15
+#define PKMN_BLANK 0x00
+
+#define ITEM_1_HIGHLIGHTED 0xD0
+#define ITEM_2_HIGHLIGHTED 0xD1
+#define ITEM_3_HIGHLIGHTED 0xD2
+#define ITEM_1_SELECTED 0xD4
+#define ITEM_2_SELECTED 0xD5
+#define ITEM_3_SELECTED 0xD6
+
+#define PKMN_MASTER 0x01
+#define PKMN_SLAVE 0x02
+#define PKMN_CONNECTED 0x60
+#define PKMN_WAIT 0x7F
+
+#define PKMN_ACTION 0x60
+
+#define PKMN_TRADE_CENTRE ITEM_1_SELECTED
+#define PKMN_COLOSSEUM ITEM_2_SELECTED
+#define PKMN_BREAK_LINK ITEM_3_SELECTED
+
+#define TRADE_CENTRE_WAIT 0xFD
+
+typedef unsigned char byte;
+typedef enum { NOT_CONNECTED, CONNECTED, TRADE_CENTRE, COLOSSEUM } connection_state_t;
+typedef enum {
+    INIT,
+    READY_TO_GO,
+    SEEN_FIRST_WAIT,
+    SENDING_RANDOM_DATA,
+    WAITING_TO_SEND_DATA,
+    START_SENDING_DATA,
+    SENDING_DATA,
+    DATA_SENT,
+    SENDING_PATCH_DATA,
+    TRADE_PENDING,
+    TRADE_CONFIRMATION,
+    DONE
+} trade_centre_state_t;
+
+/* TODO: Convert all of these to be maintained in a struct in the Trade context */
+uint8_t out_data = 0;
+uint8_t in_data = 0;
+uint8_t shift = 0;
+uint32_t time = 0;
+volatile int counter = 0;
+volatile bool procesing = true;
+volatile connection_state_t connection_state = NOT_CONNECTED;
+volatile trade_centre_state_t trade_centre_state = INIT;
+unsigned char INPUT_BLOCK[405];
+
+void screen_gameboy_connect(Canvas* const canvas) {
+    canvas_draw_frame(canvas, 0, 0, 128, 64);
+    canvas_draw_icon(canvas, 1, 21, &I_Connect_me_62x31);
+    canvas_draw_icon(canvas, 0, 53, &I_Background_128x11);
+    canvas_draw_icon(canvas, 80, 0, &I_game_boy);
+    canvas_draw_icon(canvas, 8, 2, &I_Space_65x18);
+    canvas_draw_str(canvas, 18, 13, "Connect GB");
+}
+void screen_gameboy_connected(Canvas* const canvas) {
+    canvas_draw_frame(canvas, 0, 0, 128, 64);
+    canvas_draw_icon(canvas, 1, 21, &I_Connected_62x31);
+    canvas_draw_icon(canvas, 0, 53, &I_Background_128x11);
+    canvas_draw_icon(canvas, 80, 0, &I_game_boy);
+    canvas_draw_icon(canvas, 8, 2, &I_Space_65x18);
+    canvas_draw_str(canvas, 18, 13, "Connected!");
+}
+
+int time_in_seconds = 0;
+
+static void trade_draw_callback(Canvas* canvas, void* model) {
+    const char* gameboy_status_text = NULL;
+    PokemonFap* pokemon_fap = *(PokemonFap**)model;
+
+    canvas_clear(canvas);
+    if(!pokemon_fap->trading) {
+        if(!pokemon_fap->connected) {
+            furi_hal_light_set(LightGreen, 0x00);
+            furi_hal_light_set(LightBlue, 0x00);
+            furi_hal_light_set(LightRed, 0xff);
+            screen_gameboy_connect(canvas);
+        } else {
+            furi_hal_light_set(LightGreen, 0xff);
+            furi_hal_light_set(LightBlue, 0x00);
+            furi_hal_light_set(LightRed, 0x00);
+            screen_gameboy_connected(canvas);
+        }
+    } else {
+        switch(pokemon_fap->gameboy_status) {
+        case GAMEBOY_TRADING:
+            furi_hal_light_set(LightGreen, 0x00);
+            furi_hal_light_set(LightRed, 0x00);
+            if(time_in_seconds % 2 == 1) {
+                furi_hal_light_set(LightBlue, 0xff);
+                canvas_draw_icon(canvas, 0, 0, &I_gb_step_1);
+            } else {
+                furi_hal_light_set(LightBlue, 0x00);
+                canvas_draw_icon(canvas, 0, 0, &I_gb_step_2);
+            }
+            break;
+        case GAMEBOY_READY:
+        case GAMEBOY_WAITING:
+        case GAMEBOY_SEND:
+            canvas_draw_icon(
+                canvas, 38, 11, pokemon_fap->pokemon_table[pokemon_fap->curr_pokemon].icon);
+            break;
+        default:
+            // Default state added to eliminated enum warning
+            break;
+        }
+        canvas_draw_icon(canvas, 0, 53, &I_Background_128x11);
+
+        canvas_draw_frame(canvas, 0, 0, 128, 64);
+        canvas_draw_icon(canvas, 24, 0, &I_Space_80x18);
+
+        switch(pokemon_fap->gameboy_status) {
+        case GAMEBOY_READY:
+            gameboy_status_text = "READY";
+            break;
+        case GAMEBOY_WAITING:
+            gameboy_status_text = "WAITING";
+            break;
+        case GAMEBOY_TRADE_READY:
+            gameboy_status_text = "READY";
+            break;
+        case GAMEBOY_SEND:
+            gameboy_status_text = "DEAL...";
+            break;
+        case GAMEBOY_PENDING:
+            gameboy_status_text = "PENDING...";
+            break;
+        case GAMEBOY_TRADING:
+            gameboy_status_text = "TRADING...";
+            break;
+        default:
+            gameboy_status_text = "INITIAL";
+            break;
+        }
+
+        canvas_draw_str(canvas, 48, 12, gameboy_status_text);
+
+        canvas_draw_icon(canvas, 27, 1, &I_red_16x15);
+
+        time_in_seconds = (int)DWT->CYCCNT / (72000000.0f / 4); //  250ms
+    }
+}
+
+uint32_t micros() {
+    return DWT->CYCCNT / 64;
+}
+
+/* Get the response byte from the link partner, updating the connection
+ * state if needed.
+ */
+byte getConnectResponse(byte in) {
+    byte ret;
+
+    switch(in) {
+    case PKMN_CONNECTED:
+        connection_state = CONNECTED;
+        ret = PKMN_CONNECTED;
+        break;
+    case PKMN_MASTER:
+        ret = PKMN_SLAVE;
+        break;
+    case PKMN_BLANK:
+        ret = PKMN_BLANK;
+        break;
+    default:
+        connection_state = NOT_CONNECTED;
+        ret = PKMN_BREAK_LINK;
+        break;
+    }
+
+    return ret;
+}
+
+/* Figure out what the pokemon game is requesting and move to that mode.
+ */
+byte getMenuResponse(byte in) {
+    /* TODO: Find out what this byte means */
+    byte response = 0x00;
+
+    switch(in) {
+    case PKMN_CONNECTED:
+        response = PKMN_CONNECTED;
+        break;
+    case PKMN_TRADE_CENTRE:
+        connection_state = TRADE_CENTRE;
+        break;
+    case PKMN_COLOSSEUM:
+        connection_state = COLOSSEUM;
+        break;
+    case PKMN_BREAK_LINK:
+    case PKMN_MASTER:
+        connection_state = NOT_CONNECTED;
+        response = PKMN_BREAK_LINK;
+        break;
+    default:
+        response = in;
+        break;
+    }
+
+    return response;
+}
+
+byte getTradeCentreResponse(byte in, void* context) {
+    PokemonFap* pokemon_fap = (PokemonFap*)context;
+    uint8_t* trade_block_flat = (uint8_t*)pokemon_fap->trade_block;
+    byte send = in;
+
+    furi_assert(context);
+
+    switch(trade_centre_state) {
+    case INIT:
+        // TODO: What does this value of in mean?
+        if(in == 0x00) {
+            // TODO: What does counter signify here?
+            if(counter == 5) {
+                trade_centre_state = READY_TO_GO;
+                //  CLICK EN LA MESA
+                pokemon_fap->gameboy_status = GAMEBOY_READY;
+            }
+            counter++;
+        }
+        break;
+
+    case READY_TO_GO:
+        if((in & 0xF0) == 0xF0) trade_centre_state = SEEN_FIRST_WAIT;
+        break;
+
+    case SEEN_FIRST_WAIT:
+        if((in & 0xF0) != 0xF0) {
+            counter = 0;
+            trade_centre_state = SENDING_RANDOM_DATA;
+        }
+        break;
+
+    case SENDING_RANDOM_DATA:
+        if((in & 0xF0) == 0xF0) {
+            if(counter == 5) {
+                trade_centre_state = WAITING_TO_SEND_DATA;
+                pokemon_fap->gameboy_status = GAMEBOY_WAITING;
+            }
+            counter++;
+        }
+        break;
+
+    case WAITING_TO_SEND_DATA:
+        if((in & 0xF0) != 0xF0) {
+            counter = 0;
+            INPUT_BLOCK[counter] = in;
+            send = trade_block_flat[counter];
+            counter++;
+            trade_centre_state = SENDING_DATA;
+        }
+        break;
+
+    case SENDING_DATA:
+        INPUT_BLOCK[counter] = in;
+        send = trade_block_flat[counter];
+        counter++;
+        if(counter == 405) //TODO: replace with sizeof struct rather than static number
+            trade_centre_state = SENDING_PATCH_DATA;
+        break;
+
+    case SENDING_PATCH_DATA:
+        if(in == 0xFD) {
+            counter = 0;
+            send = 0xFD;
+        } else {
+            counter++;
+            if(counter == 197) // TODO: What is this magic value?
+                trade_centre_state = TRADE_PENDING;
+        }
+        break;
+
+    case TRADE_PENDING:
+        /* TODO: What are these states */
+        if(in == 0x6F) {
+            trade_centre_state = READY_TO_GO;
+            send = 0x6F;
+            pokemon_fap->gameboy_status = GAMEBOY_TRADE_READY;
+        } else if((in & 0x60) == 0x60) {
+            send = 0x60; // first pokemon
+            pokemon_fap->gameboy_status = GAMEBOY_SEND;
+        } else if(in == 0x00) {
+            send = 0;
+            trade_centre_state = TRADE_CONFIRMATION;
+        }
+        break;
+
+    case TRADE_CONFIRMATION:
+        if(in == 0x61) {
+            trade_centre_state = TRADE_PENDING;
+            pokemon_fap->gameboy_status = GAMEBOY_PENDING;
+        } else if((in & 0x60) == 0x60) {
+            trade_centre_state = DONE;
+        }
+        break;
+
+    case DONE:
+        if(in == 0x00) {
+            send = 0;
+            trade_centre_state = INIT;
+            pokemon_fap->gameboy_status = GAMEBOY_TRADING;
+        }
+        break;
+
+    default:
+        // Do Nothing
+        break;
+    }
+
+    return send;
+}
+
+void transferBit(void* context) {
+    PokemonFap* pokemon_fap = (PokemonFap*)context;
+    furi_assert(context);
+
+    byte raw_data = furi_hal_gpio_read(&GAME_BOY_SI);
+    in_data |= raw_data << (7 - shift);
+    if(++shift > 7) {
+        shift = 0;
+        switch(connection_state) {
+        case NOT_CONNECTED:
+            pokemon_fap->connected = false;
+            out_data = getConnectResponse(in_data);
+            break;
+        case CONNECTED:
+            pokemon_fap->connected = true;
+            out_data = getMenuResponse(in_data);
+            break;
+        case TRADE_CENTRE:
+            out_data = getTradeCentreResponse(in_data, pokemon_fap);
+            break;
+        default:
+            out_data = in_data;
+            break;
+        }
+
+        in_data = 0; // TODO: I don't think this is necessary?
+    }
+
+    while(procesing && !furi_hal_gpio_read(&GAME_BOY_CLK))
+        ;
+
+    furi_hal_gpio_write(&GAME_BOY_SO, out_data & 0x80 ? true : false);
+    furi_delay_us(
+        DELAY_MICROSECONDS); // Wait 20-60us ... 120us max (in slave mode is not necessary)
+    // TODO: The above comment doesn't make sense as DELAY_MICROSECONDS is defined as 15
+
+    if(trade_centre_state == READY_TO_GO) pokemon_fap->trading = true;
+
+    out_data = out_data << 1;
+}
+
+void input_clk_gameboy(void* context) {
+    furi_assert(context);
+
+    if(time > 0) {
+        //  if there is no response from the master in 120 microseconds, the counters are reset
+        if(micros() - time > 120) {
+            //  IDLE & Reset
+            in_data = 0;
+            shift = 0;
+        }
+    }
+
+    transferBit(context);
+    time = micros();
+}
+
+void trade_enter_callback(void* context) {
+    PokemonFap* pokemon_fap = (PokemonFap*)context;
+    furi_assert(context);
+
+    pokemon_fap->trading = false;
+    pokemon_fap->connected = false;
+    pokemon_fap->gameboy_status = GAMEBOY_INITIAL;
+
+    // B3 (Pin6) / SO (2)
+    furi_hal_gpio_write(&GAME_BOY_SO, false);
+    furi_hal_gpio_init(&GAME_BOY_SO, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
+    // B2 (Pin5) / SI (3)
+    furi_hal_gpio_write(&GAME_BOY_SI, false);
+    furi_hal_gpio_init(&GAME_BOY_SI, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh);
+    // // C3 (Pin7) / CLK (5)
+    furi_hal_gpio_init(
+        &GAME_BOY_CLK,
+        GpioModeInterruptRise,
+        GpioPullNo,
+        GpioSpeedVeryHigh); // <-- This line causes the "OK" to stop functioning when exiting the application, so a reboot of the Flipper Zero is required.
+    furi_hal_gpio_remove_int_callback(&GAME_BOY_CLK);
+    furi_hal_gpio_add_int_callback(&GAME_BOY_CLK, input_clk_gameboy, pokemon_fap);
+
+    // furi_hal_gpio_disable_int_callback(&GAME_BOY_CLK);
+    // furi_hal_gpio_remove_int_callback(&GAME_BOY_CLK);
+    // Reset GPIO pins to default state
+    // furi_hal_gpio_init(&GAME_BOY_CLK, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
+}
+
+void disconnect_pin(const GpioPin* pin) {
+    furi_hal_gpio_init(pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
+    furi_hal_gpio_write(pin, true);
+}
+
+void trade_exit_callback(void* context) {
+    furi_assert(context);
+    procesing = false;
+    furi_hal_light_set(LightGreen, 0x00);
+    furi_hal_light_set(LightBlue, 0x00);
+    furi_hal_light_set(LightRed, 0x00);
+}
+
+View* trade_alloc(PokemonFap* pokemon_fap) {
+    View* view;
+
+    view = view_alloc();
+    procesing = true;
+
+    view_set_context(view, pokemon_fap);
+    view_allocate_model(view, ViewModelTypeLockFree, sizeof(PokemonFap**));
+    with_view_model(
+        view, PokemonFap** model_fap, { *model_fap = pokemon_fap; }, false);
+
+    view_set_draw_callback(view, trade_draw_callback);
+    view_set_enter_callback(view, trade_enter_callback);
+    view_set_exit_callback(view, trade_exit_callback);
+
+    return view;
+}
+
+void trade_free(PokemonFap* pokemon_fap) {
+    furi_assert(pokemon_fap);
+    // Free resources
+    procesing = false;
+    furi_hal_gpio_remove_int_callback(&GAME_BOY_CLK);
+
+    disconnect_pin(&GAME_BOY_CLK);
+
+    view_free_model(pokemon_fap->trade_view);
+    view_free(pokemon_fap->trade_view);
+}

+ 0 - 682
views/trade.cpp

@@ -1,682 +0,0 @@
-#include "../pokemon_app.h"
-#include "trade.hpp"
-
-#include "../pokemon_data.h"
-
-uint8_t out_data = 0;
-uint8_t in_data = 0;
-uint8_t shift = 0;
-uint32_t time = 0;
-volatile int counter = 0;
-volatile bool procesing = true;
-volatile connection_state_t connection_state = NOT_CONNECTED;
-volatile trade_centre_state_t trade_centre_state = INIT;
-
-void screen_gameboy_connect(Canvas* const canvas) {
-    canvas_draw_frame(canvas, 0, 0, 128, 64);
-    canvas_draw_icon(canvas, 1, 21, &I_Connect_me_62x31);
-    canvas_draw_icon(canvas, 0, 53, &I_Background_128x11);
-    canvas_draw_icon(canvas, 80, 0, &I_game_boy);
-    canvas_draw_icon(canvas, 8, 2, &I_Space_65x18);
-    canvas_draw_str(canvas, 18, 13, "Connect GB");
-}
-void screen_gameboy_connected(Canvas* const canvas) {
-    canvas_draw_frame(canvas, 0, 0, 128, 64);
-    canvas_draw_icon(canvas, 1, 21, &I_Connected_62x31);
-    canvas_draw_icon(canvas, 0, 53, &I_Background_128x11);
-    canvas_draw_icon(canvas, 80, 0, &I_game_boy);
-    canvas_draw_icon(canvas, 8, 2, &I_Space_65x18);
-    canvas_draw_str(canvas, 18, 13, "Connected!");
-}
-
-int time_in_seconds = 0;
-
-static void trade_draw_callback(Canvas* canvas, void* context) {
-    canvas_clear(canvas);
-    SelectPokemonModel* model = (SelectPokemonModel*)context;
-
-    if(!model->trading) {
-        if(!model->connected) {
-            furi_hal_light_set(LightGreen, 0x00);
-            furi_hal_light_set(LightBlue, 0x00);
-            furi_hal_light_set(LightRed, 0xff);
-            screen_gameboy_connect(canvas);
-        } else if(model->connected) {
-            furi_hal_light_set(LightGreen, 0xff);
-            furi_hal_light_set(LightBlue, 0x00);
-            furi_hal_light_set(LightRed, 0x00);
-            screen_gameboy_connected(canvas);
-        }
-    } else {
-        if(model->gameboy_status == GAMEBOY_TRADING) {
-            furi_hal_light_set(LightGreen, 0x00);
-            furi_hal_light_set(LightRed, 0x00);
-            if(time_in_seconds % 2 == 1) {
-                furi_hal_light_set(LightBlue, 0xff);
-                canvas_draw_icon(canvas, 0, 0, &I_gb_step_1);
-            } else {
-                furi_hal_light_set(LightBlue, 0x00);
-                canvas_draw_icon(canvas, 0, 0, &I_gb_step_2);
-            }
-        } else if(
-            model->gameboy_status == GAMEBOY_READY || model->gameboy_status == GAMEBOY_WAITING ||
-            model->gameboy_status == GAMEBOY_SEND) {
-            canvas_draw_icon(canvas, 38, 11, pokemon_table[model->current_pokemon].icon);
-        }
-        canvas_draw_icon(canvas, 0, 53, &I_Background_128x11);
-
-        canvas_draw_frame(canvas, 0, 0, 128, 64);
-        canvas_draw_icon(canvas, 24, 0, &I_Space_80x18);
-
-        const char* gameboy_status_text = "INITIAL";
-        if(model->gameboy_status == GAMEBOY_READY) {
-            gameboy_status_text = "READY";
-        } else if(model->gameboy_status == GAMEBOY_WAITING) {
-            gameboy_status_text = "WAITING";
-        } else if(model->gameboy_status == GAMEBOY_TRADE_READY) {
-            gameboy_status_text = "READY";
-        } else if(model->gameboy_status == GAMEBOY_SEND) {
-            gameboy_status_text = "DEAL...";
-        } else if(model->gameboy_status == GAMEBOY_PENDING) {
-            gameboy_status_text = "PENDING...";
-        } else if(model->gameboy_status == GAMEBOY_TRADING) {
-            gameboy_status_text = "TRADING...";
-        }
-
-        canvas_draw_str(canvas, 48, 12, gameboy_status_text);
-
-        canvas_draw_icon(canvas, 27, 1, &I_red_16x15);
-
-        time_in_seconds = (int)DWT->CYCCNT / (72000000.0f / 4); //  250ms
-    }
-}
-
-static bool trade_input_callback(InputEvent* event, void* context) {
-    furi_assert(context);
-    Trade* trade = (Trade*)context;
-    bool consumed = false;
-
-    if(event->type == InputTypePress && event->key == InputKeyBack) {
-        view_dispatcher_switch_to_view(trade->app->view_dispatcher, AppViewSelectPokemon);
-        consumed = true;
-    }
-
-    return consumed;
-}
-
-uint32_t micros() {
-    return DWT->CYCCNT / 64;
-}
-byte getConnectResponse(byte in) {
-    if(in == PKMN_CONNECTED) {
-        connection_state = CONNECTED;
-        return PKMN_CONNECTED;
-    }
-
-    if(in == PKMN_MASTER) {
-        return PKMN_SLAVE;
-    }
-
-    if(in == PKMN_BLANK) {
-        return PKMN_BLANK;
-    }
-
-    connection_state = NOT_CONNECTED;
-    return PKMN_BREAK_LINK;
-}
-byte getMenuResponse(byte in) {
-    byte response = 0x00;
-    if(in == PKMN_CONNECTED) {
-        response = PKMN_CONNECTED;
-    } else if(in == PKMN_TRADE_CENTRE) {
-        connection_state = TRADE_CENTRE;
-    } else if(in == PKMN_COLOSSEUM) {
-        connection_state = COLOSSEUM;
-    } else if(in == PKMN_BREAK_LINK || in == PKMN_MASTER) {
-        connection_state = NOT_CONNECTED;
-        response = PKMN_BREAK_LINK;
-    } else {
-        response = in;
-    }
-    return response;
-}
-
-byte getTradeCentreResponse(byte in, void* context) {
-    UNUSED(context);
-    Trade* trade = (Trade*)context;
-    byte send = 0x00;
-    if(trade_centre_state == INIT && in == 0x00) {
-        if(counter++ == 5) {
-            trade_centre_state = READY_TO_GO;
-            //  CLICK EN LA MESA
-            with_view_model_cpp(
-                trade->view,
-                SelectPokemonModel*,
-                model,
-                { model->gameboy_status = GAMEBOY_READY; },
-                false);
-        }
-        send = in;
-    } else if(trade_centre_state == READY_TO_GO && (in & 0xF0) == 0xF0) {
-        trade_centre_state = SEEN_FIRST_WAIT;
-        send = in;
-    } else if(trade_centre_state == SEEN_FIRST_WAIT && (in & 0xF0) != 0xF0) {
-        send = in;
-        counter = 0;
-        trade_centre_state = SENDING_RANDOM_DATA;
-    } else if(trade_centre_state == SENDING_RANDOM_DATA && (in & 0xF0) == 0xF0) {
-        if(counter++ == 5) {
-            trade_centre_state = WAITING_TO_SEND_DATA;
-
-            with_view_model_cpp(
-                trade->view,
-                SelectPokemonModel*,
-                model,
-                { model->gameboy_status = GAMEBOY_WAITING; },
-                false);
-        }
-        send = in;
-    } else if(trade_centre_state == WAITING_TO_SEND_DATA && (in & 0xF0) != 0xF0) {
-        counter = 0;
-        INPUT_BLOCK[counter] = in;
-        send = DATA_BLOCK[counter++];
-        trade_centre_state = SENDING_DATA;
-    } else if(trade_centre_state == SENDING_DATA) {
-        INPUT_BLOCK[counter] = in;
-        send = DATA_BLOCK[counter++];
-        if(counter == 405) {
-            trade_centre_state = SENDING_PATCH_DATA;
-        }
-    } else if(trade_centre_state == SENDING_PATCH_DATA && in == 0xFD) {
-        counter = 0;
-        send = 0xFD;
-    } else if(trade_centre_state == SENDING_PATCH_DATA && in != 0xFD) {
-        send = in;
-        counter++;
-        if(counter == 197) {
-            trade_centre_state = TRADE_PENDING;
-        }
-    } else if(trade_centre_state == TRADE_PENDING && (in & 0x60) == 0x60) {
-        if(in == 0x6f) {
-            trade_centre_state = READY_TO_GO;
-            send = 0x6f;
-            with_view_model_cpp(
-                trade->view,
-                SelectPokemonModel*,
-                model,
-                { model->gameboy_status = GAMEBOY_TRADE_READY; },
-                false);
-
-        } else {
-            send = 0x60; // first pokemon
-            with_view_model_cpp(
-                trade->view,
-                SelectPokemonModel*,
-                model,
-                { model->gameboy_status = GAMEBOY_SEND; },
-                false);
-        }
-    } else if(trade_centre_state == TRADE_PENDING && in == 0x00) {
-        send = 0;
-        trade_centre_state = TRADE_CONFIRMATION;
-
-    } else if(trade_centre_state == TRADE_CONFIRMATION && (in & 0x60) == 0x60) {
-        send = in;
-        if(in == 0x61) {
-            trade_centre_state = TRADE_PENDING;
-            with_view_model_cpp(
-                trade->view,
-                SelectPokemonModel*,
-                model,
-                { model->gameboy_status = GAMEBOY_PENDING; },
-                false);
-        } else {
-            trade_centre_state = DONE;
-        }
-    } else if(trade_centre_state == DONE && in == 0x00) {
-        send = 0;
-        trade_centre_state = INIT;
-        with_view_model_cpp(
-            trade->view,
-            SelectPokemonModel*,
-            model,
-            { model->gameboy_status = GAMEBOY_TRADING; },
-            false);
-    } else {
-        send = in;
-    }
-
-    return send;
-}
-
-void transferBit(void* context) {
-    Trade* trade = (Trade*)context;
-
-    byte raw_data = furi_hal_gpio_read(&GAME_BOY_SI);
-    in_data |= raw_data << (7 - shift);
-    if(++shift > 7) {
-        shift = 0;
-        if(connection_state == NOT_CONNECTED) {
-            with_view_model_cpp(
-                trade->view, SelectPokemonModel*, model, { model->connected = false; }, true);
-            out_data = getConnectResponse(in_data);
-        } else if(connection_state == CONNECTED) {
-            with_view_model_cpp(
-                trade->view, SelectPokemonModel*, model, { model->connected = true; }, true);
-            out_data = getMenuResponse(in_data);
-        } else if(connection_state == TRADE_CENTRE) {
-            out_data = getTradeCentreResponse(in_data, trade);
-
-        } else {
-            out_data = in_data;
-        }
-
-        in_data = 0;
-    }
-    while(procesing && !furi_hal_gpio_read(&GAME_BOY_CLK))
-        ;
-    furi_hal_gpio_write(&GAME_BOY_SO, out_data & 0x80 ? true : false);
-    furi_delay_us(
-        DELAY_MICROSECONDS); // Wait 20-60us ... 120us max (in slave mode is not necessary)
-
-    if(trade_centre_state == READY_TO_GO) {
-        with_view_model_cpp(
-            trade->view, SelectPokemonModel*, model, { model->trading = true; }, true);
-    }
-    out_data = out_data << 1;
-}
-void input_clk_gameboy(void* context) {
-    furi_assert(context);
-    if(time > 0) {
-        //  if there is no response from the master in 120 microseconds, the counters are reset
-        if(micros() - time > 120) {
-            //  IDLE & Reset
-            in_data = 0;
-            shift = 0;
-        }
-    }
-
-    transferBit(context);
-    time = micros();
-}
-unsigned char convertCharToTagHex(char c) {
-    switch(c) {
-    case 'A':
-        return A_;
-    case 'B':
-        return B_;
-    case 'C':
-        return C_;
-    case 'D':
-        return D_;
-    case 'E':
-        return E_;
-    case 'F':
-        return F_;
-    case 'G':
-        return G_;
-    case 'H':
-        return H_;
-    case 'I':
-        return I_;
-    case 'J':
-        return J_;
-    case 'K':
-        return K_;
-    case 'L':
-        return L_;
-    case 'M':
-        return M_;
-    case 'N':
-        return N_;
-    case 'O':
-        return O_;
-    case 'P':
-        return P_;
-    case 'Q':
-        return Q_;
-    case 'R':
-        return R_;
-    case 'S':
-        return S_;
-    case 'T':
-        return T_;
-    case 'U':
-        return U_;
-    case 'V':
-        return V_;
-    case 'W':
-        return W_;
-    case 'X':
-        return X_;
-    case 'Y':
-        return Y_;
-    case 'Z':
-        return Z_;
-    case 'a':
-        return A_;
-    case 'b':
-        return B_;
-    case 'c':
-        return C_;
-    case 'd':
-        return D_;
-    case 'e':
-        return E_;
-    case 'f':
-        return F_;
-    case 'g':
-        return G_;
-    case 'h':
-        return H_;
-    case 'i':
-        return I_;
-    case 'j':
-        return J_;
-    case 'k':
-        return K_;
-    case 'l':
-        return L_;
-    case 'm':
-        return M_;
-    case 'n':
-        return N_;
-    case 'o':
-        return O_;
-    case 'p':
-        return P_;
-    case 'q':
-        return Q_;
-    case 'r':
-        return R_;
-    case 's':
-        return S_;
-    case 't':
-        return T_;
-    case 'u':
-        return U_;
-    case 'v':
-        return V_;
-    case 'w':
-        return W_;
-    case 'x':
-        return X_;
-    case 'y':
-        return Y_;
-    case 'z':
-        return Z_;
-    case ' ':
-        return SPACE_;
-    case '.':
-        return PERIOD_;
-    case '\'':
-        return S_QUOTE_;
-    case '!':
-        return MALE_;
-    case '@':
-        return FEMALE_;
-    default:
-        return 0x00;
-    }
-}
-void trade_enter_callback(void* context) {
-    furi_assert(context);
-    Trade* trade = (Trade*)context;
-    with_view_model_cpp(
-        trade->view,
-        SelectPokemonModel*,
-        model,
-        {
-            model->current_pokemon = trade->app->current_pokemon;
-            model->pokemon_hex_code = trade->app->pokemon_hex_code;
-            model->current_level = trade->app->current_level;
-            model->current_stats = trade->app->current_stats;
-            model->current_move = trade->app->current_move;
-            model->move1_hex_code = trade->app->move1_hex_code;
-            model->move2_hex_code = trade->app->move2_hex_code;
-            model->move3_hex_code = trade->app->move3_hex_code;
-            model->move4_hex_code = trade->app->move4_hex_code;
-            model->trading = false;
-            model->connected = false;
-            model->gameboy_status = GAMEBOY_INITIAL;
-        },
-        true);
-
-    FURI_LOG_D(TAG, "[Trade] Current Pokemon: %d", trade->app->current_pokemon);
-
-    // Set the Pokemon name
-
-    unsigned char nickname[11];
-    for(size_t i = 0; i < 11; ++i) {
-        nickname[i] = 0x50;
-    }
-    for(size_t i = 0; i < strlen(pokemon_table[trade->app->current_pokemon].name); ++i) {
-        nickname[i] = convertCharToTagHex(pokemon_table[trade->app->current_pokemon].name[i]);
-    }
-
-    memcpy(DATA_BLOCK2.nickname[0].str, nickname, sizeof(nickname));
-
-    FURI_LOG_D(TAG, "[Trade] Pokemon Name: %s", pokemon_table[trade->app->current_pokemon].name);
-
-    // Set the Pokemon hex code
-
-    DATA_BLOCK[12] = trade->app->pokemon_hex_code;
-
-    FURI_LOG_D(TAG, "[Trade] Pokemon Hex Code: %x", trade->app->pokemon_hex_code);
-
-    // Set the Pokemon level
-
-    FURI_LOG_D(TAG, "[Trade] Current Level: %d", trade->app->current_level);
-
-    uint8_t level = trade->app->current_level;
-    DATA_BLOCK2.party[0].level = level & 0xFF;
-    DATA_BLOCK2.party[0].level_again = level & 0xFF;
-
-    FURI_LOG_D(TAG, "[Trade] Level Hex Code: %x", DATA_BLOCK2.party[0].level);
-
-    // Set the Pokemon experience
-
-    int32_t exp = 0;
-    if(pokemon_table[trade->app->current_pokemon].xp_group == 0) {
-        exp = 1.25 * level * level * level;
-    } else if(pokemon_table[trade->app->current_pokemon].xp_group == 1) {
-        exp = (1.2 * level * level * level) - (15 * level * level) + (100 * level) - 140;
-    } else if(pokemon_table[trade->app->current_pokemon].xp_group == 2) {
-        exp = level * level * level;
-    } else if(pokemon_table[trade->app->current_pokemon].xp_group == 3) {
-        exp = 0.8 * level * level * level;
-    }
-
-    DATA_BLOCK2.party[0].exp[0] = (exp >> 16) & 0xFF;
-    DATA_BLOCK2.party[0].exp[1] = (exp >> 8) & 0xFF;
-    DATA_BLOCK2.party[0].exp[2] = exp & 0xFF;
-
-    FURI_LOG_D(TAG, "[Trade] XP 1: %x", DATA_BLOCK2.party[0].exp[0]);
-    FURI_LOG_D(TAG, "[Trade] XP 2: %x", DATA_BLOCK2.party[0].exp[1]);
-    FURI_LOG_D(TAG, "[Trade] XP 3: %x", DATA_BLOCK2.party[0].exp[2]);
-
-    // Set the Pokemon stat experience
-
-    uint16_t statexp = 0x0000;
-    if(trade->app->current_stats == 1 || trade->app->current_stats == 4) {
-        statexp = (65535 / 100) * level;
-    } else if(trade->app->current_stats == 2 || trade->app->current_stats == 5) {
-        statexp = 65535;
-    }
-
-    DATA_BLOCK2.party[0].hp_ev = ((statexp >> 8) & 0x00FF) | ((statexp << 8) & 0xFF00);
-    DATA_BLOCK2.party[0].atk_ev = ((statexp >> 8) & 0x00FF) | ((statexp << 8) & 0xFF00);
-    DATA_BLOCK2.party[0].def_ev = ((statexp >> 8) & 0x00FF) | ((statexp << 8) & 0xFF00);
-    DATA_BLOCK2.party[0].spd_ev = ((statexp >> 8) & 0x00FF) | ((statexp << 8) & 0xFF00);
-    DATA_BLOCK2.party[0].special_ev = ((statexp >> 8) & 0x00FF) | ((statexp << 8) & 0xFF00);
-
-    FURI_LOG_D(TAG, "[Trade] Pokemon Stat EXP: %d", statexp);
-    FURI_LOG_D(TAG, "[Trade] Pokemon HP EV: %x", DATA_BLOCK2.party[0].hp_ev);
-    FURI_LOG_D(TAG, "[Trade] Pokemon Attack EV: %x", DATA_BLOCK2.party[0].atk_ev);
-    FURI_LOG_D(TAG, "[Trade] Pokemon Defence EV: %x", DATA_BLOCK2.party[0].def_ev);
-    FURI_LOG_D(TAG, "[Trade] Pokemon Speed EV: %x", DATA_BLOCK2.party[0].spd_ev);
-    FURI_LOG_D(TAG, "[Trade] Pokemon Special EV: %x", DATA_BLOCK2.party[0].special_ev);
-
-    // Set the Pokemon stats
-
-    uint8_t atk_iv = 15;
-    uint8_t def_iv = 15;
-    uint8_t spd_iv = 15;
-    uint8_t special_iv = 15;
-    uint8_t hp_iv = 15;
-    if(trade->app->current_stats <= 2) {
-        atk_iv = rand() % 15;
-        def_iv = rand() % 15;
-        spd_iv = rand() % 15;
-        special_iv = rand() % 15;
-        DATA_BLOCK2.party[0].iv = ((atk_iv & 0b00001111) << 12) | ((def_iv & 0b00001111) << 8) |
-                                  ((spd_iv & 0b00001111) << 4) | ((special_iv & 0b00001111));
-        hp_iv = (DATA_BLOCK2.party[0].iv & 0xAA) >> 4;
-    }
-
-    FURI_LOG_D(TAG, "[Trade] Pokemon IV: %x", DATA_BLOCK2.party[0].iv);
-    FURI_LOG_D(TAG, "[Trade] Pokemon HP IV: %x", hp_iv);
-    FURI_LOG_D(TAG, "[Trade] Pokemon Attack IV: %x", atk_iv);
-    FURI_LOG_D(TAG, "[Trade] Pokemon Defence IV: %x", def_iv);
-    FURI_LOG_D(TAG, "[Trade] Pokemon Speed IV: %x", spd_iv);
-    FURI_LOG_D(TAG, "[Trade] Pokemon Special IV: %x", special_iv);
-
-    uint16_t max_hp = floor(
-                          (((2 * (pokemon_table[trade->app->current_pokemon].base_hp + hp_iv)) +
-                            floor(sqrt(statexp) / 4)) *
-                           level) /
-                          100) +
-                      (level + 10);
-    DATA_BLOCK2.party[0].max_hp = ((max_hp >> 8) & 0x00FF) | ((max_hp << 8) & 0xFF00);
-    DATA_BLOCK2.party[0].hp = ((max_hp >> 8) & 0x00FF) | ((max_hp << 8) & 0xFF00);
-    uint16_t attack = floor(
-                          (((2 * (pokemon_table[trade->app->current_pokemon].base_atk + atk_iv)) +
-                            floor(sqrt(statexp) / 4)) *
-                           level) /
-                          100) +
-                      5;
-    DATA_BLOCK2.party[0].atk = ((attack >> 8) & 0x00FF) | ((attack << 8) & 0xFF00);
-    uint16_t defence = floor(
-                           (((2 * (pokemon_table[trade->app->current_pokemon].base_def + def_iv)) +
-                             floor(sqrt(statexp) / 4)) *
-                            level) /
-                           100) +
-                       5;
-    DATA_BLOCK2.party[0].def = ((defence >> 8) & 0x00FF) | ((defence << 8) & 0xFF00);
-    uint16_t speed = floor(
-                         (((2 * (pokemon_table[trade->app->current_pokemon].base_spd + spd_iv)) +
-                           floor(sqrt(statexp) / 4)) *
-                          level) /
-                         100) +
-                     5;
-    DATA_BLOCK2.party[0].spd = ((speed >> 8) & 0x00FF) | ((speed << 8) & 0xFF00);
-    uint16_t special =
-        floor(
-            (((2 * (pokemon_table[trade->app->current_pokemon].base_special + special_iv)) +
-              floor(sqrt(statexp) / 4)) *
-             level) /
-            100) +
-        5;
-    DATA_BLOCK2.party[0].special = ((special >> 8) & 0x00FF) | ((special << 8) & 0xFF00);
-
-    FURI_LOG_D(TAG, "[Trade] Pokemon Max HP: %x", DATA_BLOCK2.party[0].max_hp);
-    FURI_LOG_D(TAG, "[Trade] Pokemon Attack: %x", DATA_BLOCK2.party[0].atk);
-    FURI_LOG_D(TAG, "[Trade] Pokemon Defence: %x", DATA_BLOCK2.party[0].def);
-    FURI_LOG_D(TAG, "[Trade] Pokemon Speed: %x", DATA_BLOCK2.party[0].spd);
-    FURI_LOG_D(TAG, "[Trade] Pokemon Special: %x", DATA_BLOCK2.party[0].special);
-
-    // Set the Pokemon types
-
-    DATA_BLOCK2.party[0].type[0] = pokemon_table[trade->app->current_pokemon].type1;
-    if(pokemon_table[trade->app->current_pokemon].type2 == 0xFF) {
-        DATA_BLOCK2.party[0].type[1] = pokemon_table[trade->app->current_pokemon].type1;
-    } else {
-        DATA_BLOCK2.party[0].type[1] = pokemon_table[trade->app->current_pokemon].type2;
-    }
-
-    FURI_LOG_D(TAG, "[Trade] Type 1: %x", DATA_BLOCK2.party[0].type[0]);
-    FURI_LOG_D(TAG, "[Trade] Type 2: %x", DATA_BLOCK2.party[0].type[1]);
-
-    // Set the Pokemon moves
-
-    DATA_BLOCK2.party[0].move[0] = trade->app->move1_hex_code;
-    DATA_BLOCK2.party[0].move[1] = trade->app->move2_hex_code;
-    DATA_BLOCK2.party[0].move[2] = trade->app->move3_hex_code;
-    DATA_BLOCK2.party[0].move[3] = trade->app->move4_hex_code;
-
-    FURI_LOG_D(TAG, "[Trade] Move 1 Hex Code: %x", trade->app->move1_hex_code);
-    FURI_LOG_D(TAG, "[Trade] Move 2 Hex Code: %x", trade->app->move2_hex_code);
-    FURI_LOG_D(TAG, "[Trade] Move 3 Hex Code: %x", trade->app->move3_hex_code);
-    FURI_LOG_D(TAG, "[Trade] Move 4 Hex Code: %x", trade->app->move4_hex_code);
-
-    // B3 (Pin6) / SO (2)
-    furi_hal_gpio_write(&GAME_BOY_SO, false);
-    furi_hal_gpio_init(&GAME_BOY_SO, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
-    // B2 (Pin5) / SI (3)
-    furi_hal_gpio_write(&GAME_BOY_SI, false);
-    furi_hal_gpio_init(&GAME_BOY_SI, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh);
-    // // C3 (Pin7) / CLK (5)
-    furi_hal_gpio_init(
-        &GAME_BOY_CLK,
-        GpioModeInterruptRise,
-        GpioPullNo,
-        GpioSpeedVeryHigh); // <-- This line causes the "OK" to stop functioning when exiting the application, so a reboot of the Flipper Zero is required.
-    furi_hal_gpio_remove_int_callback(&GAME_BOY_CLK);
-    furi_hal_gpio_add_int_callback(&GAME_BOY_CLK, input_clk_gameboy, trade);
-
-    // furi_hal_gpio_disable_int_callback(&GAME_BOY_CLK);
-    // furi_hal_gpio_remove_int_callback(&GAME_BOY_CLK);
-    // Reset GPIO pins to default state
-    // furi_hal_gpio_init(&GAME_BOY_CLK, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
-}
-
-bool trade_custom_callback(uint32_t event, void* context) {
-    UNUSED(event);
-    furi_assert(context);
-    Trade* trade = (Trade*)context;
-    view_dispatcher_send_custom_event(trade->app->view_dispatcher, 0);
-    return true;
-}
-void disconnect_pin(const GpioPin* pin) {
-    furi_hal_gpio_init(pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
-    furi_hal_gpio_write(pin, true);
-}
-void trade_exit_callback(void* context) {
-    furi_assert(context);
-    procesing = false;
-    furi_hal_light_set(LightGreen, 0x00);
-    furi_hal_light_set(LightBlue, 0x00);
-    furi_hal_light_set(LightRed, 0x00);
-}
-Trade* trade_alloc(App* app) {
-    Trade* trade = (Trade*)malloc(sizeof(Trade));
-    trade->app = app;
-    trade->view = view_alloc();
-    procesing = true;
-    view_set_context(trade->view, trade);
-    view_allocate_model(trade->view, ViewModelTypeLockFree, sizeof(SelectPokemonModel));
-
-    view_set_draw_callback(trade->view, trade_draw_callback);
-    view_set_input_callback(trade->view, trade_input_callback);
-    view_set_enter_callback(trade->view, trade_enter_callback);
-    view_set_custom_callback(trade->view, trade_custom_callback);
-    view_set_exit_callback(trade->view, trade_exit_callback);
-
-    return trade;
-}
-
-void trade_free(App* app) {
-    furi_assert(app);
-    // Free resources
-    procesing = false;
-    furi_hal_gpio_remove_int_callback(&GAME_BOY_CLK);
-
-    disconnect_pin(&GAME_BOY_CLK);
-    view_free(app->trade->view);
-    free(app->trade);
-}
-
-View* trade_get_view(App* app) {
-    furi_assert(app);
-    return app->trade->view;
-}

+ 13 - 0
views/trade.h

@@ -0,0 +1,13 @@
+#ifndef TRADE_H
+#define TRADE_H
+
+#pragma once
+
+#include <gui/view.h>
+#include "../pokemon_app.h"
+
+View* trade_alloc(PokemonFap* pokemon_fap);
+
+void trade_free(PokemonFap* pokemon_fap);
+
+#endif /* TRADE_H */

+ 0 - 72
views/trade.hpp

@@ -1,72 +0,0 @@
-#ifndef TRADE_HPP
-#define TRADE_HPP
-
-#pragma once
-
-#include <furi.h>
-#include <furi_hal.h>
-#include <furi_hal_light.h>
-#include <gui/view.h>
-#include <gui/view_dispatcher.h>
-
-#define GAME_BOY_CLK gpio_ext_pb2
-#define GAME_BOY_SI gpio_ext_pc3
-#define GAME_BOY_SO gpio_ext_pb3
-
-#define DELAY_MICROSECONDS 15
-#define PKMN_BLANK 0x00
-
-#define ITEM_1_HIGHLIGHTED 0xD0
-#define ITEM_2_HIGHLIGHTED 0xD1
-#define ITEM_3_HIGHLIGHTED 0xD2
-#define ITEM_1_SELECTED 0xD4
-#define ITEM_2_SELECTED 0xD5
-#define ITEM_3_SELECTED 0xD6
-
-#define PKMN_MASTER 0x01
-#define PKMN_SLAVE 0x02
-#define PKMN_CONNECTED 0x60
-#define PKMN_WAIT 0x7F
-
-#define PKMN_ACTION 0x60
-
-#define PKMN_TRADE_CENTRE ITEM_1_SELECTED
-#define PKMN_COLOSSEUM ITEM_2_SELECTED
-#define PKMN_BREAK_LINK ITEM_3_SELECTED
-
-#define TRADE_CENTRE_WAIT 0xFD
-
-typedef unsigned char byte;
-typedef enum { NOT_CONNECTED, CONNECTED, TRADE_CENTRE, COLOSSEUM } connection_state_t;
-typedef enum {
-    INIT,
-    READY_TO_GO,
-    SEEN_FIRST_WAIT,
-    SENDING_RANDOM_DATA,
-    WAITING_TO_SEND_DATA,
-    START_SENDING_DATA,
-    SENDING_DATA,
-    DATA_SENT,
-    SENDING_PATCH_DATA,
-    TRADE_PENDING,
-    TRADE_CONFIRMATION,
-    DONE
-} trade_centre_state_t;
-
-typedef struct App App;
-
-typedef struct {
-    View* view;
-    App* app;
-} Trade;
-
-Trade* trade_alloc(App* app);
-
-void trade_free(App* app);
-
-View* trade_get_view(App* app);
-
-extern unsigned char INPUT_BLOCK[405];
-extern unsigned char* DATA_BLOCK;
-
-#endif /* TRADE_HPP */