From 6c34d9324de8abf8a43300851dab9b6667c6ec4a Mon Sep 17 00:00:00 2001 From: Gericom Date: Sun, 8 Mar 2026 13:03:26 +0100 Subject: [PATCH] Add cheat documentation, enable input repeat for L and R, show cheat category name --- README.md | 1 + arm9/source/App.cpp | 2 +- arm9/source/cheats/Cheat.h | 39 ++++++++++++++---- arm9/source/cheats/CheatCategory.h | 35 ++++++++++------ arm9/source/cheats/CheatTreeItem.h | 25 ----------- arm9/source/cheats/EmptyCheatRepository.h | 1 + arm9/source/cheats/GameCheats.h | 22 +++++++--- arm9/source/cheats/ICheatCategory.h | 15 +++++++ arm9/source/cheats/ICheatRepository.h | 7 ++++ .../cheats/PicoLoaderCheatDataFactory.h | 4 ++ arm9/source/cheats/UsrCheatDat.h | 1 + arm9/source/cheats/UsrCheatRepository.h | 1 + .../source/cheats/UsrCheatRepositoryFactory.h | 4 ++ .../romBrowser/viewModels/CheatsViewModel.cpp | 8 +++- .../romBrowser/viewModels/CheatsViewModel.h | 35 +++++++++++++++- .../views/cheats/CheatListItemView.h | 1 + .../romBrowser/views/cheats/CheatsAdapter.h | 1 + .../views/cheats/CheatsBottomSheetView.cpp | 35 +++++++++++----- .../views/cheats/CheatsBottomSheetView.h | 3 +- docs/Cheats.md | 15 +++++++ docs/Usage.md | 1 + docs/images/Cheats.png | Bin 0 -> 6143 bytes 22 files changed, 189 insertions(+), 67 deletions(-) delete mode 100644 arm9/source/cheats/CheatTreeItem.h create mode 100644 docs/Cheats.md create mode 100644 docs/images/Cheats.png 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 0000000000000000000000000000000000000000..b81958ec26277a1a1bd37111330c982118fb4195 GIT binary patch literal 6143 zcma(#2{=@3+cOLe2BXA~EKy8k$%Xr5T-S5%h<+(s*`VIaoATz+ZM)0D!OU zU;sOAK6hs^MS=~l23y_q3=VS*@&H_R_4W@|G&j5=sjjG^sG^br7V2hAs`I#t_C%vk z1HdeT10V|I_@mpHMIHMm&N2re2W0=lKQKrI$OaH#L3`HlP6GYm`?BzZ%Y`)#Z2uSh zcdY;bI{=V=2moTuQ9i4x3@rSI+=De7h5wL6WZ?%F8%q+~pP81L2p0CwRVvEL2TwWG z`>~X?AT0nus1$9Wd*znXQl3%_seXj|X3j>^#^8!%YVz~ss{$6Wb!--~2)Dll1Z*V* zP?D)S*J4=@PebLQN0X(Yr~~Xost${Dg0;vXg3V&}Zdmr`mXG_7LPKdxD^(es?8j*| zTPAY1)L(Q?1E~j=BAbleU_iiYf-sagY8V&@J>jCbqYaJp~7mSre#|~9H}s`kLCfS zqrh8=t8Z3t`5|Ju`Re&|+hbeT+>ZY?0_2;(ybx2UC}VJ-h!>HwiMs{M)$Yx|W;Io> z=s~~kate?Vc00X5Lho8yRNgmdo|tg)Zsvpg6FBj=-b?4$3V%LQ0Iyl8s{eAHpevGg zq#wNtaa`!V0i2jVG%Wdv5xoj-z2WSB2Ra!ZzpIzLHw1|D1?hDvEl)P9fQG?jlc-jj zv-|5h@;x@48Xj=Vg%rtSts%mtx&rQ2g@AN3EC@*OIGXr)g^A9PE55ISUu}|LM1&Rk zjWUJCxO%s=8wc%I;y>BnD9==@emxGv7|9{t{#u2p&Ke4IpfgJH2%&4W1P}GR3u^#z z`4|OSZb|p&GV(EMoeH%(P-3B8B$3J0BArhdI-_GeFTlS)eeda)?j2LPlsF4}q9M^U zR5Zv}{3M__*zufQ?sn{@7s4F^b3?~^MrsmVANx5VPH4S3@@{KSE=5-s=VYp%G=;Db zG=aZXz~Hq~GiC0sFkNAT8#`o=mXqYB;pO2i_T6T`k5_F@Hg-~w=)Oid$xE^)gUNoQ zW;dr0!$U)&szi^hB5yI-w!)Ge54-k2w}iZohp8=xx(*d9(5eS%^_r=wkPmpz+R;|$ ztk@MLea+Oqn&YJQLNb%5l&!S&!O674F*Y9p#V-C>0(JD-xAs4m<4!luIOR>M@-s>1 zIRr2Eu*>o|S?oJn^U~P(tpY9aadw{XD9Z$-QbhF0{lBjp8GF;W>=$RAltg{k?vY;; z;{=t5I1b?Qg>l`QefD8~wnW4Hxe#jy51`(dBsc|`Huaobu?{ij&`qCvBP#f@t_;z4 zXa+Z~#w*wH?&Yz<`<_1^eJ!P3wqGpqo=`|Okxa9W9hm6MKykI;Z_6Sr1g#dfn<@y2 z@9(N$h+cZht}qu6xD7C(E$8k;3N(6=Ei-=|lIQ1qwS6lRej8rAf|b8(asK(^H-Syx znXOxqSDDi|Cpy2U8qR`XJ%4y8Cxi*n8!Pc1AwTLKS_fkY(Co7>tDUIx%vh{>vPtqH zb6>g4w6DB=2_y31JWnUadLp&;0g4MSdz#`}*`r5tM2A{FMJDhi*W~#+&fy)%{5IROfvr{dfKHy?gf-w&>AYjoG#fFSnU1 z7x$C7G_|5boc1=FZZS6t(zGu|M@DsDh}ox&HFn$>at#5!otEU^ycXG z0!gKNlS>Z87xt9U(Z$Up5)p0m=-TEzAEN*^x zZyme;tu2Y%5d2kmVc~Sdx~EvPPC!SXVsV7we5q|k8N$oUizQ2VMX8>)T>Il3eU8kW ze7(QBJo#>WEuj%PH8*F!i2GWiI=Q<+m)MVT9?1TEZEdKcN948M+X(c9Xbo%Y$lYHL z8f8k4R+{fVs(?IPtw4>}8Kir4JuT4zuZ`&fB_DBe#Q2H|0w~^>0i7lPV&qDLV`PdW zzdLMtm^24LWZ*)KzurL=VreA_`* z$qf0b(<*}w)q`R1DwU?}F?Z~UtxgH>k=PN%Ov2}YBEQk?NxssveXYunRi#{s76DLz zAYm{qOipws7JA|~u5KQxmuy1#yrCa*i9r8>(=mVl;}wvoq85`1MlV)x(Jt-&APUalY9ilkm0 zs{SJjc2zfdD^;YsPFo?T{6viB6=*K>cXzx; zZQ#c{1WdO?rYa8$X`Vqrbp|`1I1K+#c>qF=Y7YV5DwLMISZ}=bkpuAjx!l^Kcx*U2 zV?yvZJOBig>|CEA3<1DFLI3l^|A#01FUBnRMr_n_dq8JTmjLL8Hc`LU&%wr8%y7N@ zE;uMSRh468Xop|+_^mZVJ8MHHW$LrMYN0+MhcAl0ve70)j~{ZPx28>r9_y2xM9pq+ zK;0dBHZW9k1a;gizklm?*rYjHODeZI@0w8MrNcEvVySrFq$xZgtrd2}P$l?tr312u z2Sd$+vi9F&vkt>Z!fBMp5}|r6ZJjP@9{sFFVeMZOZXUo7H{}L+Z#xhN9_%(wAyf9fxeo z3ksrZjkgZ7{g9g>39!7mug^_kIRj}O8sKVJ8ULt%1A9$-p-M8BfFJHX!eF@v)o1{XOL9?a3 ziE7mUdkr)<=7R;f+i66~cHYrU1&HH6kKktqEs`xOmdKABM{9n34`mhME=C#8a?X-6 zI^!&Lw5I$6N?Vbs*w4Fsff>k(K1wD@rC_DHBR7ESVd#u8TsuwiZDgpPDN%ROp;KdP zJJu<5!2i0F5UUvCImN2MQWIicHK#*YoC@do z6k5K*j9sChe*wGiihpl@y7)1(K)Sf7|Hi_38f9jbt2o#~FvRf=$7MRdNyFm-ioJYy z(`vew*oQUYATgADmdw1_qvhE^g~i8h8YtuWm!}u&{TFiUzFP)@%;5l5k!IpwJD>m^ zf$x-k8*2PUl`EHmM5gwXW7fZ|ErBA$q~f86K~fRVJdUwKW=n*zFbwS! z+YxpQ9;!wC@`-UD{9Z6riFo=X=o4;Tpl z9@8a&@!c82cn3|fL{qXex1wQ*md30ko6Klxs!aJiP_0=b?~`%t?X)RG)P%#$XTF4R zmg1ZvhN~~te>PDJAD9d$xWd|1ipL%G>{>QdFba^id5~>+65kiG!0tFnK|L9?u{MeA zIB-{*(ZH_ywgFZreKB7e49M0Kr?-PB2X4*%|8waBr|)8Azi(~`)=Cg? zBItW0!GptL{z1p5nlqSPEkB@kx8QZ>#fzlBVTWjU<4s|`C(xEXPbX-V z^~cr$W>%LR#)$@j45sdAjiHmEA+YJNP~X;6z5BA|YL2S?%mZ@+`luSK1%eE{ec!8D z3^)}*x7`UxG5S}Q_3k&Q`5M^j_PjEkvkplE&p^{G9qjTun2KMyuj$eidjPpA+9PI;Uf!Hh2sc@mp+?4ZW6x_+f{kNjVFH)b z2TE?Wl*D&^vlzzU@#TEfGqWAqKXsn6dRjt`q!(xjbn%Qo zn5nJu$*lsFdF0mDL{)nv9PfBc@F{0NJ0=Kh%AR9fua4;5@K_3!jVE|mpQ{$(Wn5ZG zYK8TMF8ObT_}tLsS;B7IoqDbyU!DbJJAatQcilOnaODsz)kcdI)$6O_x$lud)XDBy zmqC#;$Ky*#w4xP1#RHY`mMc{4`A5!vcCNqpY@2sGu*N{04))IlJ(XFX(YXQC44FK> zcgSa~{=w;0u#kPo22gH;qOiWz@afFdrBEHx>k@KnT40vPaGNlM*!xKtQp=}qFJJn~ z-m5AYnu|#Pc}v_kS8X1*U3x~|_v0_^s&F@;0U zzN`Ey)H3PJBfVmnkM0TXZ*#D$uQ;IO_ikr^cr;WD1rI4?4~Ja(3y|qx4&yYQmrI#* zDNsbZzqZF@NZVgM+OZNWaNWt@m>a=~*w<};0!UOVz6J7iI`yxE66{MCa)@G(g z`siF%-q-Kg_0fg%TK%mI>FE7f;X^#w>v7Rxd$SkqX^VJ)5vgid$-N3FxIH0Xra z_#V;C7ai9wI2I@UZ>{Q|1|>B>`1$ulOYxDz0!#%iMuHo7O}lQzv5?iq&fGT*y=|kf zCIzO>to*$b{Nh?FHz14gOh$y)9_60$EZY)QD@d_`} zr*6E!!Ah&$dgTSOU(Mz%=)|)#XDXN ziXx55V=Omyp)nWAdCDAU-FDPfWSWX`G7Y8=B&Aq!$6&{|C@V}!a9L5L1LI89pdC?# zx?Hz)4ZegM_ZObU<u(aBiAPPZFycjMF?bdDZHy6y8aV`Lp)eZP@UO^=6Y85=my)cw zTh@^nNJE$488BO2fXv^#agr$pqZ%Y;dQr7`U$9SZd|iCq`lBpm3xN}&D2#}*q8+C@ zUXNo9sm183QqZbEVy=LJRweVB!a+el(v+v4P5$t8doKWrF_B7t*2V>jk-U_|a02Il7DgylYG5$EShzCx`OC##ykcOV z%tN;Gsq8n48LpS^W=$i^tp|+Zs=0`iVuJAtU3j-03{(k?zI^&ey&`x#Ttj&c^XnV~(x_#>sN_tVPWzJ6V{20h!V zH8(~$J%wp$IqW?4ZOSnn25rw|yhpa$f{lc9p_h%q8XQMMn^ss-`S&7R6}*r%%j~+g zLfF$lu|R4u!K$^yyU=BVnSRkMLw70Ug-#TN^*3}5X_kR#ibB+&$PH*CZ^652Xf`@M zp`lT(YH)ohy(>ora!0{L3;sxmUUGgqQ*jsJW<#lP_MEhk-|1!dQfuszcDi{! literal 0 HcmV?d00001