#ifndef _TRANSFERPAKDETECTIONWIDGET_H #define _TRANSFERPAKDETECTIONWIDGET_H #include "widget/IWidget.h" #include "core/Sprite.h" #include "core/RDPQGraphics.h" #include "gen1/Gen1Common.h" #include "gen2/Gen2Common.h" class AnimationManager; class TransferPakManager; enum class TransferPakWidgetState { UNKNOWN, DETECTING_PAK, VALIDATING_GB_HEADER, DETECTING_GAME, VALIDATING_GAME_SAVE, GB_HEADER_VALIDATION_FAILED, NO_TRANSFER_PAK_FOUND, NO_GAME_FOUND, GAME_FOUND, VALID_SAVE_FOUND, NO_SAVE_FOUND }; typedef struct TransferPakDetectionWidgetStyle { TextRenderSettings textSettings; } TransferPakDetectionWidgetStyle; /** * @brief This widget is used to handle the transfer pak detection process. * One of the major reasons for this widget is because libdragon's joypad polling is done in the background during interrupts * and therefore may not be ready when we're initializing the scene. Best thing to do is to wait for key input * * So I might as well turn it into something visible. */ class TransferPakDetectionWidget : public IWidget { public: TransferPakDetectionWidget(AnimationManager& animManager, TransferPakManager& pakManager); virtual ~TransferPakDetectionWidget(); bool isFocused() const override; /** * @brief Sets whether the widget is currently focused * */ void setFocused(bool isFocused) override; /** * @brief Returns whether the widget is currently visible */ bool isVisible() const override; /** * @brief Changes the visibility of the widget */ void setVisible(bool visible) override; /** * @brief Returns the current (relative) bounds of the widget */ Rectangle getBounds() const override; /** * @brief Changes the current (relative) bounds of the widget */ void setBounds(const Rectangle& bounds) override; /** * @brief Returns the size (width/height) of the widget */ Dimensions getSize() const override; /** * @brief Handles user input * * For button presses, it is advised to track button release situations instead of * button presses for executing an action. Otherwise the key press might be handled again immediately * in the next scene/widget because the user wouldn't have had the time to actually release the key. */ bool handleUserInput(const joypad_inputs_t& userInput) override; /** * @brief Renders the widget * * @param gfx The graphics instance that must be used to render the widget * @param parentBounds The bounds of the parent widget or scene. You must add the x,y offset of your own bounds * to the parentBounds to get the absolute bounds for rendering. * * Getting the parentBounds as an argument of this function was done because a parent widget may be * animated or change positions independent of the child widget. But when the parent widget moves, the child must as well! */ void render(RDPQGraphics& gfx, const Rectangle& parentBounds) override; TransferPakWidgetState getState() const; /** * This function retrieves the gametypes for gen1 and gen2. The hard split is because of the way libpokemegb was set up */ void retrieveGameType(Gen1GameType& outGen1Type, Gen2GameType& outGen2Type); void setStyle(const TransferPakDetectionWidgetStyle& style); void setStateChangedCallback(void (*callback)(void*, TransferPakWidgetState), void* context); protected: private: void switchState(TransferPakWidgetState previousState, TransferPakWidgetState newState); void renderUnknownState(RDPQGraphics& gfx, const Rectangle& parentBounds); void renderValidatingSaveState(RDPQGraphics& gfx, const Rectangle& parentBounds); void renderErrorState(RDPQGraphics& gfx, const Rectangle& parentBounds); /** * @brief This function checks every controller for an N64 transfer pak, selects it in our * TransferPakManager instance and returns true after it finds the first one. */ bool selectTransferPak(); /** * @brief Checks whether the CRC value of the gameboy header matches. * This check is useful for checking whether a good connection with the cartridge was established. */ bool validateGameboyHeader(); /** * @brief This function detects the Pokémon game type in the N64 transfer pak. * It stores the found value in the gen1Type_ and gen2Type_ member vars. These values can be retrieved with retrieveGameType() * * @return returns true if the connected game pak is a Gen1 or Gen2 pokémon gameboy game. */ bool detectGameType(); void updateCartridgeIcon(); bool validateGameSave(); TransferPakDetectionWidgetStyle style_; AnimationManager& animManager_; TransferPakManager& tpakManager_; Rectangle bounds_; TransferPakWidgetState currentState_; joypad_inputs_t previousInputState_; Gen1GameType gen1Type_; Gen2GameType gen2Type_; void (*stateChangedCallback_)(void*, TransferPakWidgetState); void* stateChangedCallbackContext_; sprite_t* cartridgeIconSprite_; sprite_t* cartridgeLabelSprite_; SpriteRenderSettings cartridgeIconRenderSettings_; SpriteRenderSettings cartridgeLabelRenderSettings_; bool focused_; bool visible_; }; #endif