diff --git a/README.md b/README.md index 6a7c1a5..0c8213e 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ This repository contains Pico Launcher, which is a front-end for [Pico Loader](h - [Covers](docs/Covers.md) - [Material Design 3 and custom themes](docs/Themes.md) - Support for background music (see [Themes](docs/Themes.md)) +- Support for cheats (See [Cheats](docs/Cheats.md)) General usage documentation can be found here: [Usage](docs/Usage.md). diff --git a/arm9/source/App.cpp b/arm9/source/App.cpp index e495712..1102b80 100644 --- a/arm9/source/App.cpp +++ b/arm9/source/App.cpp @@ -45,7 +45,7 @@ App::App(IAppSettingsService& appSettingsService, IBgmService& bgmService) , _bgmService(bgmService) , _inputProvider(&_inputSource) , _inputRepeater(&_inputProvider, - InputKey::DpadLeft | InputKey::DpadRight | InputKey::DpadUp | InputKey::DpadDown, + InputKey::DpadLeft | InputKey::DpadRight | InputKey::DpadUp | InputKey::DpadDown | InputKey::L | InputKey::R, 25, 8) , _romBrowserController(&appSettingsService, &_ioTaskQueue, &_bgTaskQueue) , _displaySettingsBottomSheetViewModel(&_romBrowserController) diff --git a/arm9/source/cheats/Cheat.h b/arm9/source/cheats/Cheat.h index e4b7ead..7ca5983 100644 --- a/arm9/source/cheats/Cheat.h +++ b/arm9/source/cheats/Cheat.h @@ -1,27 +1,48 @@ #pragma once -#include "CheatTreeItem.h" -class Cheat : public CheatTreeItem +/// @brief Class representing a single cheat. +class Cheat { public: - Cheat() - : _cheatData(nullptr), _cheatDataLength(0) { } + Cheat() { } Cheat(const char* name, const char* description, u32* flagsPointer, const void* cheatData, u32 cheatDataLength) - : CheatTreeItem(name, description), _flagsPointer(flagsPointer) + : _name(name), _description(description), _flagsPointer(flagsPointer) , _cheatData(cheatData), _cheatDataLength(cheatDataLength) { } + /// @brief Gets the name of this cheat. + /// @return A pointer to the name of this cheat. + const char* GetName() const + { + return _name; + } + + /// @brief Gets the description of this cheat. + /// @return A pointer to the description of this cheat. + const char* GetDescription() const + { + return _description; + } + + /// @brief Gets a pointer to the data of this cheat. + /// @param cheatDataLength The length of the cheat data is returned in this reference. + /// @return A pointer to the cheat data. + /// This pointer is only valid for the lifetime of the \see GameCheats instance this cheat belongs to. const void* GetCheatData(u32& cheatDataLength) const { cheatDataLength = _cheatDataLength; return _cheatData; } + /// @brief Gets whether this cheat is active (enabled) or not. + /// @return \c true when this cheat is active, or \c false when not active. bool GetIsCheatActive() const { return ((*_flagsPointer >> 24) & 1) == 1; } + /// @brief Sets whether this cheat is active (enabled) or not. + /// @param isCheatActive \c true to enable this cheat, or \c false to disable this cheat. void SetIsCheatActive(bool isCheatActive) const { u32 flags = *_flagsPointer; @@ -34,7 +55,9 @@ public: } private: - u32* _flagsPointer; - const void* _cheatData; - u32 _cheatDataLength; + const char* _name = nullptr; + const char* _description = nullptr; + u32* _flagsPointer = nullptr; + const void* _cheatData = nullptr; + u32 _cheatDataLength = 0; }; diff --git a/arm9/source/cheats/CheatCategory.h b/arm9/source/cheats/CheatCategory.h index 470b325..e8d858d 100644 --- a/arm9/source/cheats/CheatCategory.h +++ b/arm9/source/cheats/CheatCategory.h @@ -1,19 +1,16 @@ #pragma once #include -#include "CheatTreeItem.h" -#include "Cheat.h" #include "ICheatCategory.h" -class CheatCategory : public CheatTreeItem, public ICheatCategory +/// @brief Class representing a cheat category, containing sub-categories and cheats. +class CheatCategory : public ICheatCategory { public: - CheatCategory() - : _isMaxOneCheatActive(false), _subCategories(nullptr), _numberOfSubCategories(0) - , _cheats(nullptr), _numberOfCheats(0) { } + CheatCategory() { } CheatCategory(const char* name, const char* description, bool isMaxOneCheatActive, CheatCategory* subCategories, u32 numberOfSubCategories, Cheat* cheats, u32 numberOfCheats) - : CheatTreeItem(name, description), _isMaxOneCheatActive(isMaxOneCheatActive) + : _name(name), _description(description), _isMaxOneCheatActive(isMaxOneCheatActive) , _subCategories(subCategories), _numberOfSubCategories(numberOfSubCategories) , _cheats(cheats), _numberOfCheats(numberOfCheats) { } @@ -56,6 +53,18 @@ public: } } + const char* GetName() const override + { + return _name; + } + + /// @brief Gets the description of this cheat category. + /// @return A pointer to the description of this cheat category. + const char* GetDescription() const + { + return _description; + } + bool GetIsMaxOneCheatActive() const override { return _isMaxOneCheatActive; @@ -74,9 +83,11 @@ public: } private: - bool _isMaxOneCheatActive; - CheatCategory* _subCategories; - u32 _numberOfSubCategories; - Cheat* _cheats; - u32 _numberOfCheats; + const char* _name = nullptr; + const char* _description = nullptr; + bool _isMaxOneCheatActive = false; + CheatCategory* _subCategories = nullptr; + u32 _numberOfSubCategories = 0; + Cheat* _cheats = nullptr; + u32 _numberOfCheats = 0; }; diff --git a/arm9/source/cheats/CheatTreeItem.h b/arm9/source/cheats/CheatTreeItem.h deleted file mode 100644 index 4037da5..0000000 --- a/arm9/source/cheats/CheatTreeItem.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -class CheatTreeItem -{ -public: - const char* GetName() const - { - return _name; - } - - const char* GetDescription() const - { - return _description; - } - -protected: - const char* _name; - const char* _description; - - CheatTreeItem() - : _name(nullptr), _description(nullptr) { } - - CheatTreeItem(const char* name, const char* description) - : _name(name), _description(description) { } -}; diff --git a/arm9/source/cheats/EmptyCheatRepository.h b/arm9/source/cheats/EmptyCheatRepository.h index c09f9d6..4600414 100644 --- a/arm9/source/cheats/EmptyCheatRepository.h +++ b/arm9/source/cheats/EmptyCheatRepository.h @@ -1,6 +1,7 @@ #pragma once #include "ICheatRepository.h" +/// @brief Class implementing an empty cheat repository. class EmptyCheatRepository : public ICheatRepository { public: diff --git a/arm9/source/cheats/GameCheats.h b/arm9/source/cheats/GameCheats.h index 7fa52e2..a8ad835 100644 --- a/arm9/source/cheats/GameCheats.h +++ b/arm9/source/cheats/GameCheats.h @@ -3,6 +3,7 @@ #include "CheatCategory.h" #include "ICheatCategory.h" +/// @brief Class holding the cheats for a game. class GameCheats : public ICheatCategory { public: @@ -24,16 +25,13 @@ public: } } + /// @brief Gets the name of the game. + /// @return A pointer to the name of the game. const char* GetGameName() const { return _gameName; } - bool GetIsMaxOneCheatActive() const override - { - return false; - } - const CheatCategory* GetCategories(u32& numberOfCategories) const override { numberOfCategories = _numberOfCategories; @@ -46,6 +44,9 @@ public: return _cheats; } + /// @brief Gets a pointer to the cheat data. + /// @param cheatDataLength The length of the cheat data is returned in this reference. + /// @return A pointer to the cheat data. This pointer is only valid for the lifetime of this \see GameCheats instance. u8* GetCheatData(u32& cheatDataLength) const { cheatDataLength = _cheatDataLength; @@ -66,4 +67,15 @@ private: u32 _numberOfCategories; Cheat* _cheats; u32 _numberOfCheats; + + // From ICheatCategory + const char* GetName() const override + { + return ""; + } + + bool GetIsMaxOneCheatActive() const override + { + return false; + } }; diff --git a/arm9/source/cheats/ICheatCategory.h b/arm9/source/cheats/ICheatCategory.h index d90e7d1..e555cac 100644 --- a/arm9/source/cheats/ICheatCategory.h +++ b/arm9/source/cheats/ICheatCategory.h @@ -3,13 +3,28 @@ class CheatCategory; class Cheat; +/// @brief Interface for a collection of cheats. class ICheatCategory { public: virtual ~ICheatCategory() = default; + /// @brief Gets the name of this category. + /// @return The name of this category. + virtual const char* GetName() const = 0; + + /// @brief Indicates if only one cheat in this category is allowed to be on or not. + /// @return \c true when only one cheat is allowed to be active in this category, or \c false otherwise. virtual bool GetIsMaxOneCheatActive() const = 0; + + /// @brief Gets the sub-categories of this category. + /// @param numberOfCategories The number of categories is returned through this reference. + /// @return A pointer to an array of categories. This may be \c nullptr when \p numberOfCategories is 0. virtual const CheatCategory* GetCategories(u32& numberOfCategories) const; + + /// @brief Gets the cheats in this category. + /// @param numberOfCheats The number of cheats is returned through this reference. + /// @return A pointer to an array of cheats. This may be \c nullptr when \p numberOfCheats is 0. virtual const Cheat* GetCheats(u32& numberOfCheats) const; protected: diff --git a/arm9/source/cheats/ICheatRepository.h b/arm9/source/cheats/ICheatRepository.h index 49e3eee..9f64781 100644 --- a/arm9/source/cheats/ICheatRepository.h +++ b/arm9/source/cheats/ICheatRepository.h @@ -2,12 +2,19 @@ #include "GameCheats.h" #include "fat/FastFileRef.h" +/// @brief Interface for a repository providing access to cheats. class ICheatRepository { public: virtual ~ICheatRepository() { } + /// @brief Gets the available cheats for the given \p romFile. + /// @param romFile Reference to the rom file. + /// @return A unique pointer to the found cheats, or an empty unique pointer when no cheats were found. virtual std::unique_ptr GetCheatsForGame(const FastFileRef& romFile) const = 0; + + /// @brief Writes the enable/disabled status of the given \p cheats. + /// @param cheats The cheats to update. virtual void UpdateEnabledCheatsForGame(const std::unique_ptr& cheats) const = 0; protected: diff --git a/arm9/source/cheats/PicoLoaderCheatDataFactory.h b/arm9/source/cheats/PicoLoaderCheatDataFactory.h index 771d773..6198dab 100644 --- a/arm9/source/cheats/PicoLoaderCheatDataFactory.h +++ b/arm9/source/cheats/PicoLoaderCheatDataFactory.h @@ -3,9 +3,13 @@ #include "GameCheats.h" #include "picoLoader7.h" +/// @brief Factory for creating Pico Loader compatible cheat data. class PicoLoaderCheatDataFactory { public: + /// @brief Converts the given \p gameCheats to Pico Loader format. Only the enabled cheats will be included. + /// @param gameCheats The cheats to convert. + /// @return Pointer to the created cheat data. pload_cheats_t* CreateCheatData(const std::unique_ptr& gameCheats) const; private: diff --git a/arm9/source/cheats/UsrCheatDat.h b/arm9/source/cheats/UsrCheatDat.h index a5d1080..873fab5 100644 --- a/arm9/source/cheats/UsrCheatDat.h +++ b/arm9/source/cheats/UsrCheatDat.h @@ -1,5 +1,6 @@ #pragma once +/// @brief Struct representing an index entry of usrcheat.dat. struct usr_cheat_index_entry_t { u32 gameCode; diff --git a/arm9/source/cheats/UsrCheatRepository.h b/arm9/source/cheats/UsrCheatRepository.h index 47ed51e..1d20c65 100644 --- a/arm9/source/cheats/UsrCheatRepository.h +++ b/arm9/source/cheats/UsrCheatRepository.h @@ -4,6 +4,7 @@ #include "ICheatRepository.h" #include "UsrCheatDat.h" +/// @brief Class implementing a cheat repository for usrcheat.dat. class UsrCheatRepository : public ICheatRepository { public: diff --git a/arm9/source/cheats/UsrCheatRepositoryFactory.h b/arm9/source/cheats/UsrCheatRepositoryFactory.h index dda8839..c3398c9 100644 --- a/arm9/source/cheats/UsrCheatRepositoryFactory.h +++ b/arm9/source/cheats/UsrCheatRepositoryFactory.h @@ -2,8 +2,12 @@ #include #include "UsrCheatRepository.h" +/// @brief Factory for constructing a \see UsrCheatRepository for a usrcheat.dat file. class UsrCheatRepositoryFactory { public: + /// @brief Constructs a \see UsrCheatRepository for the given \p usrCheatDatPath. + /// @param usrCheatDatPath The path to the usrcheat.dat file. + /// @return A unique pointer to the constructed repository, or an empty unique pointer when construction failed. std::unique_ptr FromUsrCheatDat(const TCHAR* usrCheatDatPath); }; diff --git a/arm9/source/romBrowser/viewModels/CheatsViewModel.cpp b/arm9/source/romBrowser/viewModels/CheatsViewModel.cpp index b5a3796..7112953 100644 --- a/arm9/source/romBrowser/viewModels/CheatsViewModel.cpp +++ b/arm9/source/romBrowser/viewModels/CheatsViewModel.cpp @@ -24,7 +24,7 @@ CheatsViewModel::CheatsViewModel(const FileInfo& romFileInfo, IRomBrowserControl }); } -void CheatsViewModel::ItemActivated() +void CheatsViewModel::ActivateSelectedItem() { auto cheatCategory = GetCurrentCheatCategory(); u32 numberOfCategories = 0; @@ -58,21 +58,25 @@ void CheatsViewModel::ItemActivated() } } -void CheatsViewModel::Back() +bool CheatsViewModel::NavigateUp() { if (_categoryStackLevel == 0) { Close(); + return false; } else { _selectedItem = _categoryStack[_categoryStackLevel].index; _categoryStack[_categoryStackLevel--] = { nullptr, 0 }; + return true; } } void CheatsViewModel::Close() { + _categoryStack.fill({ nullptr, 0 }); + if (_changed) { // Save which cheats are enabled/disabled diff --git a/arm9/source/romBrowser/viewModels/CheatsViewModel.h b/arm9/source/romBrowser/viewModels/CheatsViewModel.h index 3583d69..392bbd1 100644 --- a/arm9/source/romBrowser/viewModels/CheatsViewModel.h +++ b/arm9/source/romBrowser/viewModels/CheatsViewModel.h @@ -10,26 +10,57 @@ class CheatsViewModel { public: + /// @brief Enum representing the state of the cheats panel. enum class State { + /// @brief Cheats are being loaded. Loading, + + /// @brief No cheats were found. NoCheats, + + /// @brief Cheats are being displayed. DisplayCheats }; CheatsViewModel(const FileInfo& romFileInfo, IRomBrowserController* romBrowserController); - void ItemActivated(); - void Back(); + /// @brief Activates the selected cheat or category. + void ActivateSelectedItem(); + + /// @brief Navigates up in the cheat hierachy, or closes the cheats panel when at the root. + /// @return \c true when navigation happened in the cheats tree, or \c false when the cheats panel was closed. + bool NavigateUp(); + + /// @brief Closes the cheats panel. void Close(); + + /// @brief Disables all cheats. void DisableAllCheats(); + /// @brief Gets the current state of the cheats panel. + /// @return The current state of the cheats panel. State GetState() const { return _state; } + + /// @brief Gets the current cheat category. + /// @return The current cheat category. const ICheatCategory* GetCurrentCheatCategory() const { return _categoryStack[_categoryStackLevel].cheatCategory; } + /// @brief Gets the index of the selected item. + /// @return The index of the selected item. constexpr int GetSelectedItem() const { return _selectedItem; } + + /// @brief Sets the index of the selected item. + /// @param selectedItem The index of the selected item to set. void SetSelectedItem(int selectedItem) { _selectedItem = selectedItem; } + /// @brief Returns whether the category name should be displayed. + /// @return \c true when the category name should be displayed, or \c false otherwise. + bool ShouldShowCategoryName() const + { + return _categoryStackLevel > 0; + } + private: struct CategoryStackEntry { diff --git a/arm9/source/romBrowser/views/cheats/CheatListItemView.h b/arm9/source/romBrowser/views/cheats/CheatListItemView.h index 8776715..16ccd8b 100644 --- a/arm9/source/romBrowser/views/cheats/CheatListItemView.h +++ b/arm9/source/romBrowser/views/cheats/CheatListItemView.h @@ -7,6 +7,7 @@ class MaterialColorScheme; class IFontRepository; +/// @brief List item view for the cheats panel, representing a single cheat or cheat category. class CheatListItemView : public ViewContainer { public: diff --git a/arm9/source/romBrowser/views/cheats/CheatsAdapter.h b/arm9/source/romBrowser/views/cheats/CheatsAdapter.h index 77e27fc..9ce7783 100644 --- a/arm9/source/romBrowser/views/cheats/CheatsAdapter.h +++ b/arm9/source/romBrowser/views/cheats/CheatsAdapter.h @@ -4,6 +4,7 @@ #include "cheats/Cheat.h" #include "CheatListItemView.h" +/// @brief Recycler adapter for cheats. class CheatsAdapter : public RecyclerAdapter { public: diff --git a/arm9/source/romBrowser/views/cheats/CheatsBottomSheetView.cpp b/arm9/source/romBrowser/views/cheats/CheatsBottomSheetView.cpp index e8c8bbe..ff72932 100644 --- a/arm9/source/romBrowser/views/cheats/CheatsBottomSheetView.cpp +++ b/arm9/source/romBrowser/views/cheats/CheatsBottomSheetView.cpp @@ -16,6 +16,9 @@ #define TITLE_LABEL_X 20 #define TITLE_LABEL_Y 16 +#define CATEGORY_NAME_LABEL_X (TITLE_LABEL_X + 43) +#define CATEGORY_NAME_LABEL_Y (TITLE_LABEL_Y + 1) + #define NO_CHEATS_FOUND_LABEL_X 20 #define NO_CHEATS_FOUND_LABEL_Y 36 @@ -32,7 +35,7 @@ CheatsBottomSheetView::CheatsBottomSheetView(std::unique_ptr vi FocusManager* focusManager) : _viewModel(std::move(viewModel)) , _titleLabel(64, 16, 25, fontRepository->GetFont(FontType::Medium11)) - , _noCheatsFoundLabel(96, 16, 25, fontRepository->GetFont(FontType::Regular10)) + , _secondaryLabel(177, 16, 64, fontRepository->GetFont(FontType::Regular10)) , _descriptionLabel(224, 16, 256, fontRepository->GetFont(FontType::Medium7_5)) , _cheatListRecycler(std::make_unique( LIST_X, LIST_Y, LIST_WIDTH, LIST_HEIGHT, RecyclerView::Mode::VerticalList)) @@ -41,11 +44,12 @@ CheatsBottomSheetView::CheatsBottomSheetView(std::unique_ptr vi , _focusManager(focusManager) { _titleLabel.SetText(u"Cheats"); - _noCheatsFoundLabel.SetText(u"No cheats found."); + _secondaryLabel.SetText(u"No cheats found."); + _secondaryLabel.SetEllipsisStyle(LabelView::EllipsisStyle::Ellipsis); _descriptionLabel.SetEllipsisStyle(LabelView::EllipsisStyle::Marquee); _descriptionLabel.SetText(""); AddChildTail(&_titleLabel); - AddChildTail(&_noCheatsFoundLabel); + AddChildTail(&_secondaryLabel); AddChildTail(&_descriptionLabel); AddChildTail(_cheatListRecycler.get()); } @@ -80,7 +84,14 @@ void CheatsBottomSheetView::InitVram(const VramContext& vramContext) void CheatsBottomSheetView::Update() { _titleLabel.SetPosition(TITLE_LABEL_X, _position.y + TITLE_LABEL_Y); - _noCheatsFoundLabel.SetPosition(NO_CHEATS_FOUND_LABEL_X, _position.y + NO_CHEATS_FOUND_LABEL_Y); + if (_viewModel->GetState() == CheatsViewModel::State::DisplayCheats) + { + _secondaryLabel.SetPosition(CATEGORY_NAME_LABEL_X, _position.y + CATEGORY_NAME_LABEL_Y); + } + else + { + _secondaryLabel.SetPosition(NO_CHEATS_FOUND_LABEL_X, _position.y + NO_CHEATS_FOUND_LABEL_Y); + } _descriptionLabel.SetPosition(DESCRIPTION_LABEL_X, _position.y + DESCRIPTION_LABEL_Y); _cheatListRecycler->SetPosition(LIST_X, _position.y + LIST_Y); if (_viewModel->GetState() == CheatsViewModel::State::DisplayCheats) @@ -168,11 +179,12 @@ void CheatsBottomSheetView::Draw(GraphicsContext& graphicsContext) _titleLabel.SetForegroundColor(_materialColorScheme->onSurface); _titleLabel.Draw(graphicsContext); - if (_viewModel->GetState() == CheatsViewModel::State::NoCheats) + if (_viewModel->GetState() == CheatsViewModel::State::NoCheats || + _viewModel->ShouldShowCategoryName()) { - _noCheatsFoundLabel.SetBackgroundColor(backColor); - _noCheatsFoundLabel.SetForegroundColor(_materialColorScheme->onSurface); - _noCheatsFoundLabel.Draw(graphicsContext); + _secondaryLabel.SetBackgroundColor(backColor); + _secondaryLabel.SetForegroundColor(_materialColorScheme->onSurfaceVariant); + _secondaryLabel.Draw(graphicsContext); } _descriptionLabel.SetBackgroundColor(backColor); @@ -190,9 +202,10 @@ bool CheatsBottomSheetView::HandleInput(const InputProvider& inputProvider, Focu if (focusManager.IsFocusInside(_cheatListRecycler.get())) { auto oldCategory = _viewModel->GetCurrentCheatCategory(); - _viewModel->ItemActivated(); + _viewModel->ActivateSelectedItem(); if (oldCategory != _viewModel->GetCurrentCheatCategory()) { + _secondaryLabel.SetText(_viewModel->GetCurrentCheatCategory()->GetName()); UpdateCheatList(); } @@ -202,8 +215,8 @@ bool CheatsBottomSheetView::HandleInput(const InputProvider& inputProvider, Focu else if (inputProvider.Triggered(InputKey::B)) { auto oldCategory = _viewModel->GetCurrentCheatCategory(); - _viewModel->Back(); - if (oldCategory != _viewModel->GetCurrentCheatCategory()) + if (_viewModel->NavigateUp() && + oldCategory != _viewModel->GetCurrentCheatCategory()) { UpdateCheatList(); } diff --git a/arm9/source/romBrowser/views/cheats/CheatsBottomSheetView.h b/arm9/source/romBrowser/views/cheats/CheatsBottomSheetView.h index b0c70e5..9be1168 100644 --- a/arm9/source/romBrowser/views/cheats/CheatsBottomSheetView.h +++ b/arm9/source/romBrowser/views/cheats/CheatsBottomSheetView.h @@ -11,6 +11,7 @@ class MaterialColorScheme; class IFontRepository; class IVramManager; +/// @brief Bottom sheet for browsing and enabling/disabling cheats. class CheatsBottomSheetView : public BottomSheetView { public: @@ -40,7 +41,7 @@ public: private: std::unique_ptr _viewModel; Label2DView _titleLabel; - Label2DView _noCheatsFoundLabel; + Label2DView _secondaryLabel; Label2DView _descriptionLabel; std::unique_ptr _cheatListRecycler; CheatsAdapter* _cheatsAdapter = nullptr; diff --git a/docs/Cheats.md b/docs/Cheats.md new file mode 100644 index 0000000..d13a3ec --- /dev/null +++ b/docs/Cheats.md @@ -0,0 +1,15 @@ +# Cheats +Pico Launcher supports cheats from a `usrcheat.dat` file placed at `/_pico/usrcheat.dat`. The file stores the cheat codes, as well as which cheats are enabled. When a game is started which has cheats enabled, the enabled cheats are passed to Pico Loader. + +## Usage +To display the available cheats highlight a game in the rom browser and press Y to access the cheats panel. + +![Cheats panel](images/Cheats.png) + +### Controls +- DPAD up/down: Scroll through the list of cheats. +- L and R: Scroll quickly when there are many cheats. +- A: Toggle a cheat on/off, or go into a cheat category. +- B: Go up in the cheat hierarchy, or close the cheats panel when at the root. +- Y: Close the cheats panel. +- X: Disable all cheats. diff --git a/docs/Usage.md b/docs/Usage.md index acc6e56..bd3fb14 100644 --- a/docs/Usage.md +++ b/docs/Usage.md @@ -12,6 +12,7 @@ From here you can browse your SD card to launch homebrew and games. - A: Open a folder, or to launch a homebrew or game. - B: Go to the parent folder or close a menu. - L and R: Scroll quickly when there are many items in a folder. +- Y: Open the cheats panel (see [Cheats](Cheats.md)). The back arrow on the top left of the bottom screen can also be used to go up to the parent folder. diff --git a/docs/images/Cheats.png b/docs/images/Cheats.png new file mode 100644 index 0000000..b81958e Binary files /dev/null and b/docs/images/Cheats.png differ