libpokemegb/include/gen1/Gen1GameReader.h
Philippe Symons aedb58832c
V0.3 (#7)
* Feature/add support for localized versions (#3)

* Add support for gen I localizations

There currently is no way to automatically detect the localization yet though.

* Add gen1_determineGameLanguage() and use it automatically in Gen1GameReader if not specified in the constructor

* Add support for the gen II localizations

Note: for Korean games, specifically, I did not implement the Korean character set
(nor do I plan to)

The reason is that it is entirely different and more complex to implement than the other
character sets. I'm not personally invested nor interested enough to go through the hassle.

This affects the pokémon names that will be displayed when using libpokemegb. Instead as
a fallback, it will return "poke-<number>" for these roms.

Feel free to contribute proper Korean character set (and pokémon names) support!

* Streamline the Gen I localization code to be structured similarly to the gen II's

* Fix bug

When Gen1GameReader is triggering the language detection automatically, the
Gen1IconDecoder instance is not updated with the detected language

* Update README.md

* Fix markdown special char escape problem in README.md

* Fix name decoding for localizations

* Fix detection of German Pokémon Yellow

* Fix bug: Japanese saves were not detected correctly for gen I

* Convert every character map entry into UTF-8.

libdragon requires every string to be in UTF-8 encoding

* Fix bug with Japanese saves. Wrong offset was used for the party storage in several functions

* Fix OT names and nicknames for Japanese cartridges

* Fix another issue with Gen I nicknames. Gen II is also still entirely broken in this regard

* Also fix Japanese pokemon name retrieval in gen I

* Fix OT and nicknames for Japanese gen I games

* Add support for Japanese and Korean Gen II save offsets

Japenese and Korean Gen II saves have a different save structure (data at different offsets)

* Disable save corruption check for Japanese/Korean games.

I don't have the right save offsets nor can find them. (I'm not adept at decompilation myself and pokegold/pokecrystal don't have the japanese offsets. Nor does PkHex)

* Refactor the gen1 related code to store the different SRAM offsets for gen 1 saves similarly as we do for gen 2

* Remove unnecessary casts

* Avoid trying to decode Korean trainername and rival name.

We don't support the character set, neither in libpokemegb and PokeMe64.

The Korean character set is a lot more complex than the other ones. But it would also require the inclusion of a lot of additional character glyphs in PokeMe64
in order to show the characters. Right now I don't feel like doing that for a localization that won't be used by many in combination with PokeMe64.

So instead of showing the trainer name and rival name, I will just replace it with "player" and "rival". It's not an amazing solution, but it's the best I'm willing to do for now.

* Add support for the Korean character sets.

This was needed after all, because we don't want injected pokémon on a Korean cartridge to end up having a nickname like "poke-<indexnumber>".
So that implies the need for being able to decode and encode pokémon names in Korean.

So yeah, I bit the bullet and added support for it. This was really annoying to do, but it has been done!

* Feature/add japanese distribution pokemon (#4)

* Add Gen I Japanese Distribution Event Pokémon

* Remove external definitions of each individual distribution pokémon from the headers and make them static in the source file

* Add isJapanese field to Gen2DistributionPokemon struct and adapt every existing definition

* Fix mistake in last commit

* Make sure the OT name is replaced when dealing with Japanese distribution pokémon on a different language cart and vice versa

* Add Japanese Mystery Egg #3 gen II  event distribution Pokémon

* Add remaining Japanese Event Distribution Pokémon

* Fix another issue with Gen I nicknames. Gen II is also still entirely broken in this regard

* Fix OT and nicknames for Japanese gen I games

* Refactor the gen1 related code to store the different SRAM offsets for gen 1 saves similarly as we do for gen 2

* Bugfix/fix distribution event pokemon inconsistencies (#5)

* Add Gen I Japanese Distribution Event Pokémon

* Remove external definitions of each individual distribution pokémon from the headers and make them static in the source file

* Add isJapanese field to Gen2DistributionPokemon struct and adapt every existing definition

* Fix mistake in last commit

* Make sure the OT name is replaced when dealing with Japanese distribution pokémon on a different language cart and vice versa

* Add Japanese Mystery Egg #3 gen II  event distribution Pokémon

* Add remaining Japanese Event Distribution Pokémon

* Fix another issue with Gen I nicknames. Gen II is also still entirely broken in this regard

* Fix OT and nicknames for Japanese gen I games

* Refactor the gen1 related code to store the different SRAM offsets for gen 1 saves similarly as we do for gen 2

* Fix some inconsistencies with Gen II distribution event pokémon.

* Make use of the Move enum instead of magic numbers for the pokémon moves (#6)

* Add Gen1GameReader::getCurrentMap() function to retrieve the current map the player is on.

I may be able to use this when adding a move deleter for gen1 games later, to make sure the player is in the pokemon center.
(to make sure the player doesn't get trapped because he/she would delete an HM move)

* Fix broken Japanese PC Box support + add support for Pokémon Green (JPN) + fix currentMap retrieval on japanese saves

So, turns out that PC Box support was entirely broken for the Japanese games with PokeMe64.

Apparently, the japanese games only have 8 boxes instead of 12, but each of them has a capacity of 30 pokémon instead
of 20!

So that changes things for the save offsets.

Also: the currentMap offset was wrong for Japanese games. So I had to fix that too!

* Updated README.md

* Fix the same issue with PC Boxes for Japanese Gen II games.

The international releases have 14 pc boxes with 20 pokemon each. But the japanese gen II games have 9 pc boxes with 30 entries each.

* Rework rom offsets for pokémon blue to avoid duplicate rom offset definitions.

The only one different from the red/green offsets is the japanese one anyway.

* Replace "PokeMe64" OT with "PM64" when injecting japanese mons into international games.

Turns out the official character limit was 7

* Fill the unused bytes of a nickname with the terminator byte.

This is needed because according to Bulbapedia, a nickname is only considered not a nickname if it matches with the
uppercase species name with all unused bytes set to the 0x50 terminator.

* Add gen1_isAPokeCenter() function
2025-02-17 12:38:21 +01:00

204 lines
7.8 KiB
C++

#ifndef GEN1GAMEREADER_H
#define GEN1GAMEREADER_H
#include "gen1/Gen1SpriteDecoder.h"
#include "gen1/Gen1IconDecoder.h"
#include "gen1/Gen1PlayerPokemonStorage.h"
#include "gen1/Gen1DistributionPokemon.h"
#include "gen1/Gen1Localization.h"
#include "gen1/Gen1Maps.h"
class IRomReader;
class ISaveManager;
class Gen1GameReader
{
public:
Gen1GameReader(IRomReader& romReader, ISaveManager& saveManager, Gen1GameType gameType, Gen1LocalizationLanguage language = Gen1LocalizationLanguage::MAX);
/**
* @brief Retrieves the current game cartridges'/roms' language
*/
Gen1LocalizationLanguage getGameLanguage() const;
/**
* @brief get the name of a pokémon based on an index number
* Note: you don't own the returned pointer. The data will get overwritten on the next call to this function,
* so make sure to strdup() it if you need to store it for later
*
* @return const char*
*/
const char* getPokemonName(uint8_t index) const;
/**
* @brief Gets the pokedex number of a pokemon at the given internal index
*/
uint8_t getPokemonNumber(uint8_t index) const;
/**
* @brief This function returns the Gen1PokemonIconType for a pokemon at the given index
*/
Gen1PokemonIconType getPokemonIconType(uint8_t index) const;
/**
* @brief With this function, you can check if the index is valid and not referring to missingno
*/
bool isValidIndex(uint8_t index) const;
/**
* @brief This function reads the pokemon stats at the given index
*
* @return whether or not this operation was successful
*/
bool readPokemonStatsForIndex(uint8_t index, Gen1PokeStats& stats) const;
/**
* @brief Get the index of the color palette based on the pokedex number of a certain pokemon
*/
uint8_t getColorPaletteIndexByPokemonNumber(uint8_t pokeNumber);
/**
* @brief This function reads the color palette with the given ID into outColorPalette
* outColorPalette should be a pointer to an uint16_t array with 4 elements
*
* Based on: https://bulbapedia.bulbagarden.net/wiki/List_of_color_palettes_by_index_number_(Generation_I)
*
* @param outColorPalette
*/
void readColorPalette(uint8_t paletteId, uint16_t* outColorPalette);
/**
* @brief Get the trainer name from the save file
* Note: the resulting const char* does not need to be free'd.
* However, it needs to be either used rightaway or strdup()'d, because the data will get overwritten on the next call to this function.
*/
const char* getTrainerName() const;
/**
* @brief Get the rival name from the save file
* Note: the resulting const char* does not need to be free'd.
* However, it needs to be either used rightaway or strdup()'d, because the data will get overwritten on the next call to this function.
*/
const char* getRivalName() const;
/**
* @brief Get the player ID from the save file
*/
uint16_t getTrainerID() const;
/**
* Retrieves the current map the player is in.
*/
Gen1Maps getCurrentMap() const;
/**
* @brief Returns a Gen1Party instance, which can be used to retrieve the information about the pokemon currently in the trainers' party
*/
Gen1Party getParty();
/**
* @brief Returns a Gen1Box instance, which can be used to retrieve information about the pokemon currently in the Trainers' PC box at the specified index
*/
Gen1Box getBox(uint8_t boxIndex);
/**
* @brief Returns the 0-based index of the currently selected PC box for pokemon
*/
uint8_t getCurrentBoxIndex();
/**
* @brief This function returns whether the pokemon with the given pokedexNumber has been SEEN/OWNED.
*/
bool getPokedexFlag(PokedexFlag dexFlag, uint8_t pokedexNumber) const;
/**
* @brief This function sets the pokedex flag for the given pokedexNumber
*/
void setPokedexFlag(PokedexFlag dexFlag, uint8_t pokedexNumber) const;
/**
* @brief This function returns the counter value of the given dex flag
*/
uint8_t getPokedexCounter(PokedexFlag dexFlag) const;
/**
* @brief This function decodes a sprite at the given bank and pointer.
* Returns a pointer to a buffer containing the decoded gameboy sprite in gameboy sprite format.
* The size of the returned buffer is SPRITE_BITPLANE_BUFFER_SIZE_IN_BYTES * 2
*
* WARNING: this function returns a pointer to an internal buffer of the Gen1SpriteDecoder. It will get overwritten on the next call to this function.
* If you want to keep the content around for longer, make a copy of this data.
*/
uint8_t* decodeSprite(uint8_t bankIndex, uint16_t pointer);
/**
* @brief This function decodes the given pokemon icon and returns an internal buffer
* Note that this returns a 16x16 buffer in gameboy format with tiles in vertical order.
* You need to feed it to an instance of SpriteRenderer to convert it to a useful format
*
* WARNING: this function returns a buffer to an internal buffer of Gen1IconDecoder. That means it will get overwritten on the next call to this function.
* If you want to keep the content around for longer, make a copy of this data
*/
uint8_t* decodePokemonIcon(Gen1PokemonIconType iconType, bool firstFrame = true);
/**
* @brief Adds a pokemon to the save. Tries to add it to the party first. If there's no more room there, it tries to add it to the
* first ingame PC box with open slots
*
* @param originalTrainerID optional parameter. If not specified (=null), the original trainer id string will be set to the players'
* @param nickname optional parameter. If not specified (= null), the pokemon species name will be used instead.
*
* @return
* 0xFF - Could not add pokemon (probably no space left)
* 0xFE - Added to players' party
* 0x0 - 0xB - Added to box at index <value>
*/
uint8_t addPokemon(Gen1TrainerPokemon& poke, const char* originalTrainerID = 0, const char* nickname = 0);
/**
* @brief Adds a distribution pokemon to the save. Tries to add it to the party first. If there's no more room there, it tries to add it to the
* first ingame PC box with open slots
*
* @param nickname optional parameter. If not specified (= null), the pokemon species name will be used instead.
*
* @return
* 0xFF - Could not add pokemon (probably no space left)
* 0xFE - Added to players' party
* 0x0 - 0xB - Added to box at index <value>
*/
uint8_t addDistributionPokemon(const Gen1DistributionPokemon& distributionPoke, const char* nickname = 0);
/**
* @brief This function checks whether the main checksum of bank 1 is valid
*/
bool isMainChecksumValid();
/**
* @brief Updates the main data checksum in bank 1
*/
void updateMainChecksum();
/**
* @brief Checks whether the whole bank checksum of the given memory bank is valid.
* As you may or may not know, bank 2 and bank 3 contain the PC Pokemon boxes of the player
* These memory banks have 2 types of checksums: a whole memory bank checksum (which is this one)
* And a checksum per box. (which will be dealt with in the Gen1Box class)
*
* So yeah, this function is about the whole memory bank checksums in memory bank 2 and 3
*/
bool isWholeBoxBankValid(uint8_t bankIndex);
/**
* @brief This function updates the checksum value of the whole box bank (bank 2 or 3)
*/
void updateWholeBoxBankChecksum(uint8_t bankIndex);
protected:
private:
IRomReader& romReader_;
ISaveManager& saveManager_;
Gen1SpriteDecoder spriteDecoder_;
Gen1IconDecoder iconDecoder_;
Gen1GameType gameType_;
Gen1LocalizationLanguage localization_;
};
#endif