Add cheat documentation, enable input repeat for L and R, show cheat category name

This commit is contained in:
Gericom
2026-03-08 13:03:26 +01:00
parent 43b1bf7afa
commit 6c34d9324d
22 changed files with 189 additions and 67 deletions

View File

@@ -15,6 +15,7 @@ This repository contains Pico Launcher, which is a front-end for [Pico Loader](h
- [Covers](docs/Covers.md) - [Covers](docs/Covers.md)
- [Material Design 3 and custom themes](docs/Themes.md) - [Material Design 3 and custom themes](docs/Themes.md)
- Support for background music (see [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). General usage documentation can be found here: [Usage](docs/Usage.md).

View File

@@ -45,7 +45,7 @@ App::App(IAppSettingsService& appSettingsService, IBgmService& bgmService)
, _bgmService(bgmService) , _bgmService(bgmService)
, _inputProvider(&_inputSource) , _inputProvider(&_inputSource)
, _inputRepeater(&_inputProvider, , _inputRepeater(&_inputProvider,
InputKey::DpadLeft | InputKey::DpadRight | InputKey::DpadUp | InputKey::DpadDown, InputKey::DpadLeft | InputKey::DpadRight | InputKey::DpadUp | InputKey::DpadDown | InputKey::L | InputKey::R,
25, 8) 25, 8)
, _romBrowserController(&appSettingsService, &_ioTaskQueue, &_bgTaskQueue) , _romBrowserController(&appSettingsService, &_ioTaskQueue, &_bgTaskQueue)
, _displaySettingsBottomSheetViewModel(&_romBrowserController) , _displaySettingsBottomSheetViewModel(&_romBrowserController)

View File

@@ -1,27 +1,48 @@
#pragma once #pragma once
#include "CheatTreeItem.h"
class Cheat : public CheatTreeItem /// @brief Class representing a single cheat.
class Cheat
{ {
public: public:
Cheat() Cheat() { }
: _cheatData(nullptr), _cheatDataLength(0) { }
Cheat(const char* name, const char* description, u32* flagsPointer, const void* cheatData, u32 cheatDataLength) 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) { } , _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 const void* GetCheatData(u32& cheatDataLength) const
{ {
cheatDataLength = _cheatDataLength; cheatDataLength = _cheatDataLength;
return _cheatData; 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 bool GetIsCheatActive() const
{ {
return ((*_flagsPointer >> 24) & 1) == 1; 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 void SetIsCheatActive(bool isCheatActive) const
{ {
u32 flags = *_flagsPointer; u32 flags = *_flagsPointer;
@@ -34,7 +55,9 @@ public:
} }
private: private:
u32* _flagsPointer; const char* _name = nullptr;
const void* _cheatData; const char* _description = nullptr;
u32 _cheatDataLength; u32* _flagsPointer = nullptr;
const void* _cheatData = nullptr;
u32 _cheatDataLength = 0;
}; };

View File

@@ -1,19 +1,16 @@
#pragma once #pragma once
#include <memory> #include <memory>
#include "CheatTreeItem.h"
#include "Cheat.h"
#include "ICheatCategory.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: public:
CheatCategory() CheatCategory() { }
: _isMaxOneCheatActive(false), _subCategories(nullptr), _numberOfSubCategories(0)
, _cheats(nullptr), _numberOfCheats(0) { }
CheatCategory(const char* name, const char* description, bool isMaxOneCheatActive, CheatCategory(const char* name, const char* description, bool isMaxOneCheatActive,
CheatCategory* subCategories, u32 numberOfSubCategories, Cheat* cheats, u32 numberOfCheats) CheatCategory* subCategories, u32 numberOfSubCategories, Cheat* cheats, u32 numberOfCheats)
: CheatTreeItem(name, description), _isMaxOneCheatActive(isMaxOneCheatActive) : _name(name), _description(description), _isMaxOneCheatActive(isMaxOneCheatActive)
, _subCategories(subCategories), _numberOfSubCategories(numberOfSubCategories) , _subCategories(subCategories), _numberOfSubCategories(numberOfSubCategories)
, _cheats(cheats), _numberOfCheats(numberOfCheats) { } , _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 bool GetIsMaxOneCheatActive() const override
{ {
return _isMaxOneCheatActive; return _isMaxOneCheatActive;
@@ -74,9 +83,11 @@ public:
} }
private: private:
bool _isMaxOneCheatActive; const char* _name = nullptr;
CheatCategory* _subCategories; const char* _description = nullptr;
u32 _numberOfSubCategories; bool _isMaxOneCheatActive = false;
Cheat* _cheats; CheatCategory* _subCategories = nullptr;
u32 _numberOfCheats; u32 _numberOfSubCategories = 0;
Cheat* _cheats = nullptr;
u32 _numberOfCheats = 0;
}; };

View File

@@ -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) { }
};

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include "ICheatRepository.h" #include "ICheatRepository.h"
/// @brief Class implementing an empty cheat repository.
class EmptyCheatRepository : public ICheatRepository class EmptyCheatRepository : public ICheatRepository
{ {
public: public:

View File

@@ -3,6 +3,7 @@
#include "CheatCategory.h" #include "CheatCategory.h"
#include "ICheatCategory.h" #include "ICheatCategory.h"
/// @brief Class holding the cheats for a game.
class GameCheats : public ICheatCategory class GameCheats : public ICheatCategory
{ {
public: 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 const char* GetGameName() const
{ {
return _gameName; return _gameName;
} }
bool GetIsMaxOneCheatActive() const override
{
return false;
}
const CheatCategory* GetCategories(u32& numberOfCategories) const override const CheatCategory* GetCategories(u32& numberOfCategories) const override
{ {
numberOfCategories = _numberOfCategories; numberOfCategories = _numberOfCategories;
@@ -46,6 +44,9 @@ public:
return _cheats; 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 u8* GetCheatData(u32& cheatDataLength) const
{ {
cheatDataLength = _cheatDataLength; cheatDataLength = _cheatDataLength;
@@ -66,4 +67,15 @@ private:
u32 _numberOfCategories; u32 _numberOfCategories;
Cheat* _cheats; Cheat* _cheats;
u32 _numberOfCheats; u32 _numberOfCheats;
// From ICheatCategory
const char* GetName() const override
{
return "";
}
bool GetIsMaxOneCheatActive() const override
{
return false;
}
}; };

View File

@@ -3,13 +3,28 @@
class CheatCategory; class CheatCategory;
class Cheat; class Cheat;
/// @brief Interface for a collection of cheats.
class ICheatCategory class ICheatCategory
{ {
public: public:
virtual ~ICheatCategory() = default; 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; 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; 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; virtual const Cheat* GetCheats(u32& numberOfCheats) const;
protected: protected:

View File

@@ -2,12 +2,19 @@
#include "GameCheats.h" #include "GameCheats.h"
#include "fat/FastFileRef.h" #include "fat/FastFileRef.h"
/// @brief Interface for a repository providing access to cheats.
class ICheatRepository class ICheatRepository
{ {
public: public:
virtual ~ICheatRepository() { } 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<GameCheats> GetCheatsForGame(const FastFileRef& romFile) const = 0; virtual std::unique_ptr<GameCheats> 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<GameCheats>& cheats) const = 0; virtual void UpdateEnabledCheatsForGame(const std::unique_ptr<GameCheats>& cheats) const = 0;
protected: protected:

View File

@@ -3,9 +3,13 @@
#include "GameCheats.h" #include "GameCheats.h"
#include "picoLoader7.h" #include "picoLoader7.h"
/// @brief Factory for creating Pico Loader compatible cheat data.
class PicoLoaderCheatDataFactory class PicoLoaderCheatDataFactory
{ {
public: 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>& gameCheats) const; pload_cheats_t* CreateCheatData(const std::unique_ptr<GameCheats>& gameCheats) const;
private: private:

View File

@@ -1,5 +1,6 @@
#pragma once #pragma once
/// @brief Struct representing an index entry of usrcheat.dat.
struct usr_cheat_index_entry_t struct usr_cheat_index_entry_t
{ {
u32 gameCode; u32 gameCode;

View File

@@ -4,6 +4,7 @@
#include "ICheatRepository.h" #include "ICheatRepository.h"
#include "UsrCheatDat.h" #include "UsrCheatDat.h"
/// @brief Class implementing a cheat repository for usrcheat.dat.
class UsrCheatRepository : public ICheatRepository class UsrCheatRepository : public ICheatRepository
{ {
public: public:

View File

@@ -2,8 +2,12 @@
#include <memory> #include <memory>
#include "UsrCheatRepository.h" #include "UsrCheatRepository.h"
/// @brief Factory for constructing a \see UsrCheatRepository for a usrcheat.dat file.
class UsrCheatRepositoryFactory class UsrCheatRepositoryFactory
{ {
public: 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<UsrCheatRepository> FromUsrCheatDat(const TCHAR* usrCheatDatPath); std::unique_ptr<UsrCheatRepository> FromUsrCheatDat(const TCHAR* usrCheatDatPath);
}; };

View File

@@ -24,7 +24,7 @@ CheatsViewModel::CheatsViewModel(const FileInfo& romFileInfo, IRomBrowserControl
}); });
} }
void CheatsViewModel::ItemActivated() void CheatsViewModel::ActivateSelectedItem()
{ {
auto cheatCategory = GetCurrentCheatCategory(); auto cheatCategory = GetCurrentCheatCategory();
u32 numberOfCategories = 0; u32 numberOfCategories = 0;
@@ -58,21 +58,25 @@ void CheatsViewModel::ItemActivated()
} }
} }
void CheatsViewModel::Back() bool CheatsViewModel::NavigateUp()
{ {
if (_categoryStackLevel == 0) if (_categoryStackLevel == 0)
{ {
Close(); Close();
return false;
} }
else else
{ {
_selectedItem = _categoryStack[_categoryStackLevel].index; _selectedItem = _categoryStack[_categoryStackLevel].index;
_categoryStack[_categoryStackLevel--] = { nullptr, 0 }; _categoryStack[_categoryStackLevel--] = { nullptr, 0 };
return true;
} }
} }
void CheatsViewModel::Close() void CheatsViewModel::Close()
{ {
_categoryStack.fill({ nullptr, 0 });
if (_changed) if (_changed)
{ {
// Save which cheats are enabled/disabled // Save which cheats are enabled/disabled

View File

@@ -10,26 +10,57 @@
class CheatsViewModel class CheatsViewModel
{ {
public: public:
/// @brief Enum representing the state of the cheats panel.
enum class State enum class State
{ {
/// @brief Cheats are being loaded.
Loading, Loading,
/// @brief No cheats were found.
NoCheats, NoCheats,
/// @brief Cheats are being displayed.
DisplayCheats DisplayCheats
}; };
CheatsViewModel(const FileInfo& romFileInfo, IRomBrowserController* romBrowserController); CheatsViewModel(const FileInfo& romFileInfo, IRomBrowserController* romBrowserController);
void ItemActivated(); /// @brief Activates the selected cheat or category.
void Back(); 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(); void Close();
/// @brief Disables all cheats.
void DisableAllCheats(); void DisableAllCheats();
/// @brief Gets the current state of the cheats panel.
/// @return The current state of the cheats panel.
State GetState() const { return _state; } State GetState() const { return _state; }
/// @brief Gets the current cheat category.
/// @return The current cheat category.
const ICheatCategory* GetCurrentCheatCategory() const { return _categoryStack[_categoryStackLevel].cheatCategory; } 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; } 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; } 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: private:
struct CategoryStackEntry struct CategoryStackEntry
{ {

View File

@@ -7,6 +7,7 @@
class MaterialColorScheme; class MaterialColorScheme;
class IFontRepository; class IFontRepository;
/// @brief List item view for the cheats panel, representing a single cheat or cheat category.
class CheatListItemView : public ViewContainer class CheatListItemView : public ViewContainer
{ {
public: public:

View File

@@ -4,6 +4,7 @@
#include "cheats/Cheat.h" #include "cheats/Cheat.h"
#include "CheatListItemView.h" #include "CheatListItemView.h"
/// @brief Recycler adapter for cheats.
class CheatsAdapter : public RecyclerAdapter class CheatsAdapter : public RecyclerAdapter
{ {
public: public:

View File

@@ -16,6 +16,9 @@
#define TITLE_LABEL_X 20 #define TITLE_LABEL_X 20
#define TITLE_LABEL_Y 16 #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_X 20
#define NO_CHEATS_FOUND_LABEL_Y 36 #define NO_CHEATS_FOUND_LABEL_Y 36
@@ -32,7 +35,7 @@ CheatsBottomSheetView::CheatsBottomSheetView(std::unique_ptr<CheatsViewModel> vi
FocusManager* focusManager) FocusManager* focusManager)
: _viewModel(std::move(viewModel)) : _viewModel(std::move(viewModel))
, _titleLabel(64, 16, 25, fontRepository->GetFont(FontType::Medium11)) , _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)) , _descriptionLabel(224, 16, 256, fontRepository->GetFont(FontType::Medium7_5))
, _cheatListRecycler(std::make_unique<RecyclerView>( , _cheatListRecycler(std::make_unique<RecyclerView>(
LIST_X, LIST_Y, LIST_WIDTH, LIST_HEIGHT, RecyclerView::Mode::VerticalList)) LIST_X, LIST_Y, LIST_WIDTH, LIST_HEIGHT, RecyclerView::Mode::VerticalList))
@@ -41,11 +44,12 @@ CheatsBottomSheetView::CheatsBottomSheetView(std::unique_ptr<CheatsViewModel> vi
, _focusManager(focusManager) , _focusManager(focusManager)
{ {
_titleLabel.SetText(u"Cheats"); _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.SetEllipsisStyle(LabelView::EllipsisStyle::Marquee);
_descriptionLabel.SetText(""); _descriptionLabel.SetText("");
AddChildTail(&_titleLabel); AddChildTail(&_titleLabel);
AddChildTail(&_noCheatsFoundLabel); AddChildTail(&_secondaryLabel);
AddChildTail(&_descriptionLabel); AddChildTail(&_descriptionLabel);
AddChildTail(_cheatListRecycler.get()); AddChildTail(_cheatListRecycler.get());
} }
@@ -80,7 +84,14 @@ void CheatsBottomSheetView::InitVram(const VramContext& vramContext)
void CheatsBottomSheetView::Update() void CheatsBottomSheetView::Update()
{ {
_titleLabel.SetPosition(TITLE_LABEL_X, _position.y + TITLE_LABEL_Y); _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); _descriptionLabel.SetPosition(DESCRIPTION_LABEL_X, _position.y + DESCRIPTION_LABEL_Y);
_cheatListRecycler->SetPosition(LIST_X, _position.y + LIST_Y); _cheatListRecycler->SetPosition(LIST_X, _position.y + LIST_Y);
if (_viewModel->GetState() == CheatsViewModel::State::DisplayCheats) if (_viewModel->GetState() == CheatsViewModel::State::DisplayCheats)
@@ -168,11 +179,12 @@ void CheatsBottomSheetView::Draw(GraphicsContext& graphicsContext)
_titleLabel.SetForegroundColor(_materialColorScheme->onSurface); _titleLabel.SetForegroundColor(_materialColorScheme->onSurface);
_titleLabel.Draw(graphicsContext); _titleLabel.Draw(graphicsContext);
if (_viewModel->GetState() == CheatsViewModel::State::NoCheats) if (_viewModel->GetState() == CheatsViewModel::State::NoCheats ||
_viewModel->ShouldShowCategoryName())
{ {
_noCheatsFoundLabel.SetBackgroundColor(backColor); _secondaryLabel.SetBackgroundColor(backColor);
_noCheatsFoundLabel.SetForegroundColor(_materialColorScheme->onSurface); _secondaryLabel.SetForegroundColor(_materialColorScheme->onSurfaceVariant);
_noCheatsFoundLabel.Draw(graphicsContext); _secondaryLabel.Draw(graphicsContext);
} }
_descriptionLabel.SetBackgroundColor(backColor); _descriptionLabel.SetBackgroundColor(backColor);
@@ -190,9 +202,10 @@ bool CheatsBottomSheetView::HandleInput(const InputProvider& inputProvider, Focu
if (focusManager.IsFocusInside(_cheatListRecycler.get())) if (focusManager.IsFocusInside(_cheatListRecycler.get()))
{ {
auto oldCategory = _viewModel->GetCurrentCheatCategory(); auto oldCategory = _viewModel->GetCurrentCheatCategory();
_viewModel->ItemActivated(); _viewModel->ActivateSelectedItem();
if (oldCategory != _viewModel->GetCurrentCheatCategory()) if (oldCategory != _viewModel->GetCurrentCheatCategory())
{ {
_secondaryLabel.SetText(_viewModel->GetCurrentCheatCategory()->GetName());
UpdateCheatList(); UpdateCheatList();
} }
@@ -202,8 +215,8 @@ bool CheatsBottomSheetView::HandleInput(const InputProvider& inputProvider, Focu
else if (inputProvider.Triggered(InputKey::B)) else if (inputProvider.Triggered(InputKey::B))
{ {
auto oldCategory = _viewModel->GetCurrentCheatCategory(); auto oldCategory = _viewModel->GetCurrentCheatCategory();
_viewModel->Back(); if (_viewModel->NavigateUp() &&
if (oldCategory != _viewModel->GetCurrentCheatCategory()) oldCategory != _viewModel->GetCurrentCheatCategory())
{ {
UpdateCheatList(); UpdateCheatList();
} }

View File

@@ -11,6 +11,7 @@ class MaterialColorScheme;
class IFontRepository; class IFontRepository;
class IVramManager; class IVramManager;
/// @brief Bottom sheet for browsing and enabling/disabling cheats.
class CheatsBottomSheetView : public BottomSheetView class CheatsBottomSheetView : public BottomSheetView
{ {
public: public:
@@ -40,7 +41,7 @@ public:
private: private:
std::unique_ptr<CheatsViewModel> _viewModel; std::unique_ptr<CheatsViewModel> _viewModel;
Label2DView _titleLabel; Label2DView _titleLabel;
Label2DView _noCheatsFoundLabel; Label2DView _secondaryLabel;
Label2DView _descriptionLabel; Label2DView _descriptionLabel;
std::unique_ptr<RecyclerView> _cheatListRecycler; std::unique_ptr<RecyclerView> _cheatListRecycler;
CheatsAdapter* _cheatsAdapter = nullptr; CheatsAdapter* _cheatsAdapter = nullptr;

15
docs/Cheats.md Normal file
View File

@@ -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.

View File

@@ -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. - A: Open a folder, or to launch a homebrew or game.
- B: Go to the parent folder or close a menu. - B: Go to the parent folder or close a menu.
- L and R: Scroll quickly when there are many items in a folder. - 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. The back arrow on the top left of the bottom screen can also be used to go up to the parent folder.

BIN
docs/images/Cheats.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB