Initial work on implementing support for cheats

This commit is contained in:
Gericom
2026-02-22 20:28:35 +01:00
parent f73c8b0547
commit dddee0bb94
28 changed files with 966 additions and 17 deletions

View File

@@ -8,6 +8,7 @@ class RomBrowserViewModel;
class FileInfo;
class TaskQueueBase;
class ICoverRepository;
class ICheatRepository;
class IRomBrowserController
{
@@ -17,7 +18,7 @@ public:
virtual void NavigateUp() = 0;
virtual void NavigateToPath(const TCHAR* name) = 0;
virtual void LaunchFile(const FileInfo& fileInfo) = 0;
virtual void ShowGameInfo() = 0;
virtual void ShowGameInfo(const FileInfo& fileInfo) = 0;
virtual void HideGameInfo() = 0;
virtual void ShowDisplaySettings() = 0;
virtual void HideDisplaySettings() = 0;
@@ -33,11 +34,14 @@ public:
virtual TaskQueueBase* GetIoTaskQueue() const = 0;
virtual TaskQueueBase* GetBgTaskQueue() const = 0;
virtual const ICoverRepository& GetCoverRepository() const = 0;
virtual const ICheatRepository& GetCheatRepository() const = 0;
virtual const RomBrowserDisplaySettings& GetRomBrowserDisplaySettings() const = 0;
virtual void SetRomBrowserDisplaySettings(
const RomBrowserDisplaySettings& romBrowserDisplaySettings) = 0;
virtual const FileInfo& GetTriggerFileInfo() const = 0;
};
inline IRomBrowserController::~IRomBrowserController() { }

View File

@@ -6,6 +6,7 @@
#include "FileType/FileType.h"
#include "SdFolderFactory.h"
#include "services/settings/IAppSettingsService.h"
#include "cheats/UsrCheatRepositoryFactory.h"
#include "RomBrowserController.h"
RomBrowserController::RomBrowserController(
@@ -23,14 +24,14 @@ void RomBrowserController::NavigateToPath(const TCHAR* name)
void RomBrowserController::LaunchFile(const FileInfo& fileInfo)
{
_launchFileInfo = FileInfo(fileInfo);
_triggerFileInfo = FileInfo(fileInfo);
_stateMachine.Fire(RomBrowserStateTrigger::Launch);
}
void RomBrowserController::ShowGameInfo()
void RomBrowserController::ShowGameInfo(const FileInfo& fileInfo)
{
// Currently disabled, as the game info panel is not complete
// _stateMachine.Fire(RomBrowserStateTrigger::ShowGameInfo);
_triggerFileInfo = FileInfo(fileInfo);
_stateMachine.Fire(RomBrowserStateTrigger::ShowGameInfo);
}
void RomBrowserController::HideGameInfo()
@@ -140,6 +141,10 @@ void RomBrowserController::HandleNavigateTrigger()
_coverRepository = std::make_unique<CoverRepository>();
_coverRepository->Initialize();
}
if (!_cheatRepository)
{
_cheatRepository = UsrCheatRepositoryFactory().FromUsrCheatDat("/_pico/usrcheat.dat");
}
u64 startTick = gTickCounter.GetValue();
_navigateFileName = nullptr;
@@ -182,7 +187,7 @@ void RomBrowserController::HandleLaunchTrigger()
int idx = strlcat(_navigatePath, "/", sizeof(_navigatePath));
if (_navigatePath[idx - 2] == '/')
_navigatePath[idx - 1] = 0;
strlcat(_navigatePath, _launchFileInfo.GetFileName(), sizeof(_navigatePath));
strlcat(_navigatePath, _triggerFileInfo.GetFileName(), sizeof(_navigatePath));
_appSettingsService->GetAppSettings().lastUsedFilePath = _navigatePath;
_appSettingsService->Save();
@@ -190,7 +195,7 @@ void RomBrowserController::HandleLaunchTrigger()
loadParams->savePath[0] = 0;
loadParams->arguments[0] = 0;
loadParams->argumentsLength = 0;
if (_launchFileInfo.GetFileType()->TrySetLaunchParameters(loadParams, _navigatePath))
if (_triggerFileInfo.GetFileType()->TrySetLaunchParameters(loadParams, _navigatePath))
{
gProcessManager.Goto<PicoLoaderProcess>();
}

View File

@@ -9,6 +9,7 @@
#include "CoverRepository.h"
#include "FileType/ExtensionFileTypeProvider.h"
#include "services/settings/IAppSettingsService.h"
#include "cheats/ICheatRepository.h"
class RomBrowserController : public IRomBrowserController
{
@@ -23,7 +24,7 @@ public:
void NavigateToPath(const TCHAR* name) override;
void LaunchFile(const FileInfo& fileInfo) override;
void ShowGameInfo() override;
void ShowGameInfo(const FileInfo& fileInfo) override;
void HideGameInfo() override;
void ShowDisplaySettings() override;
void HideDisplaySettings() override;
@@ -39,6 +40,7 @@ public:
TaskQueueBase* GetIoTaskQueue() const override { return _ioTaskQueue; }
TaskQueueBase* GetBgTaskQueue() const override { return _bgTaskQueue; }
const ICoverRepository& GetCoverRepository() const override { return *_coverRepository; }
const ICheatRepository& GetCheatRepository() const override { return *_cheatRepository; }
void SetRomBrowserDisplaySettings(const RomBrowserDisplaySettings& romBrowserDisplaySettings) override;
@@ -47,6 +49,8 @@ public:
return _appSettingsService->GetAppSettings().romBrowserDisplaySettings;
}
virtual const FileInfo& GetTriggerFileInfo() const override { return _triggerFileInfo; }
private:
IAppSettingsService* _appSettingsService;
TaskQueueBase* _ioTaskQueue;
@@ -58,11 +62,12 @@ private:
RomBrowserStateMachine _stateMachine;
TCHAR _navigatePath[256];
TCHAR* _navigateFileName;
FileInfo _launchFileInfo;
FileInfo _triggerFileInfo;
QueueTask<void> _navigateTask;
bool _saveSettingsPending = false;
std::unique_ptr<CoverRepository> _coverRepository;
ExtensionFileTypeProvider _fileTypeProvider;
std::unique_ptr<ICheatRepository> _cheatRepository;
void HandleTrigger();
void HandleNavigateTrigger();

View File

@@ -0,0 +1,66 @@
#include "common.h"
#include "cheats/ICheatRepository.h"
#include "fat/File.h"
#include "CheatsViewModel.h"
#define CRCPOLY 0xEDB88320
static u32 crc32(const void* buffer, u32 length)
{
u32 crc = ~0u;
const u8* p = (u8*)buffer;
while (length--)
{
crc ^= *p++;
for (int i = 0; i < 8; i++)
{
crc = (crc >> 1) ^ ((crc & 1) ? CRCPOLY : 0);
}
}
return crc;
}
CheatsViewModel::CheatsViewModel(const FileInfo& romFileInfo, IRomBrowserController* romBrowserController)
: _romFileInfo(romFileInfo), _romBrowserController(romBrowserController)
{
_loadCheatsTask = _romBrowserController->GetIoTaskQueue()->Enqueue([this] (const vu8& cancelRequested)
{
LOG_DEBUG("%s\n", _romFileInfo.GetFileName());
auto file = std::make_unique<File>();
file->Open(_romFileInfo.GetFastFileRef(), FA_READ);
auto headerBuffer = std::make_unique_for_overwrite<u8[]>(512);
if (!file->ReadExact(headerBuffer.get(), 512))
{
LOG_ERROR("Could not read rom header\n");
return TaskResult<void>::Failed();
}
file->Close();
if (cancelRequested)
{
return TaskResult<void>::Canceled();
}
u32 gameCode = *(u32*)(headerBuffer.get() + 0xC);
u32 crc = crc32(headerBuffer.get(), 512);
headerBuffer.reset();
if (cancelRequested)
{
return TaskResult<void>::Canceled();
}
_cheats = _romBrowserController->GetCheatRepository().GetCheatsForGame(gameCode, crc);
if (_cheats)
{
_state = State::DisplayCheats;
}
else
{
_state = State::NoCheats;
}
return TaskResult<void>::Completed();
});
}

View File

@@ -0,0 +1,35 @@
#pragma once
#include <memory>
#include "core/task/TaskQueue.h"
#include "cheats/GameCheats.h"
#include "romBrowser/FileInfo.h"
#include "romBrowser/IRomBrowserController.h"
/// @brief View model for the cheats screen.
class CheatsViewModel
{
public:
enum class State
{
Loading,
NoCheats,
DisplayCheats
};
CheatsViewModel(const FileInfo& romFileInfo, IRomBrowserController* romBrowserController);
void Close()
{
_romBrowserController->HideGameInfo();
}
State GetState() const { return _state; }
const GameCheats* GetCheats() const { return _cheats.get(); }
private:
FileInfo _romFileInfo;
IRomBrowserController* _romBrowserController;
QueueTask<void> _loadCheatsTask;
std::unique_ptr<GameCheats> _cheats;
State _state = State::Loading;
};

View File

@@ -1,5 +1,6 @@
#include "common.h"
#include <algorithm>
#include "romBrowser/FileType/Nds/NdsFileType.h"
#include "RomBrowserViewModel.h"
RomBrowserViewModel::RomBrowserViewModel(IRomBrowserController* romBrowserController, const char* initialSelectedFileName)
@@ -34,7 +35,8 @@ RomBrowserViewModel::RomBrowserViewModel(IRomBrowserController* romBrowserContro
auto sortedFilteredFiles = sdFolder.FilterAndSort(filterSortParams, filteredCount);
u64 endTick = gTickCounter.GetValue();
LOG_DEBUG("Filter + sort took: %d us\n", (u32)TickCounter::TicksToMicroSeconds(endTick - startTick));
_fileInfoManager = std::make_unique<FileInfoManager>(std::move(sortedFilteredFiles), filteredCount, _romBrowserController->GetCoverRepository());
_fileInfoManager = std::make_unique<FileInfoManager>(std::move(sortedFilteredFiles),
filteredCount, _romBrowserController->GetCoverRepository());
_selectedItem = _fileInfoManager->GetItemIndex(initialSelectedFileName);
}
@@ -59,7 +61,8 @@ void RomBrowserViewModel::NavigateUp()
void RomBrowserViewModel::ShowGameInfo()
{
const auto& item = _fileInfoManager->GetItem(_selectedItem);
if (item.GetFileType()->GetClassification() == FileTypeClassification::Folder)
return;
_romBrowserController->ShowGameInfo();
if (item.GetFileType() == &NdsFileType::sInstance)
{
_romBrowserController->ShowGameInfo(item);
}
}

View File

@@ -0,0 +1,47 @@
#include "common.h"
#include "themes/material/MaterialColorScheme.h"
#include "themes/IFontRepository.h"
#include "gui/palette/GradientPalette.h"
#include "gui/GraphicsContext.h"
#include "gui/OamBuilder.h"
#include "CheatListItemView.h"
#define ICON_X 20
#define ICON_Y 3
#define NAME_LABEL_X 40
#define NAME_LABEL_Y 4
CheatListItemView::CheatListItemView(const MaterialColorScheme* materialColorScheme, const IFontRepository* fontRepository)
: _nameLabel(200, 16, 64, fontRepository->GetFont(FontType::Regular10))
, _materialColorScheme(materialColorScheme)
{
_nameLabel.SetEllipsis(true);
AddChildTail(&_nameLabel);
}
void CheatListItemView::Update()
{
_nameLabel.SetPosition(NAME_LABEL_X, _position.y + NAME_LABEL_Y);
ViewContainer::Update();
}
void CheatListItemView::Draw(GraphicsContext& graphicsContext)
{
auto backColor = _materialColorScheme->GetColor(md::sys::color::surfaceContainerLow);
_nameLabel.SetBackgroundColor(backColor);
_nameLabel.SetForegroundColor(_materialColorScheme->onSurface);
ViewContainer::Draw(graphicsContext);
if (graphicsContext.IsVisible(Rectangle(_position.x + ICON_X, _position.y + ICON_Y, 16, 16)))
{
u32 iconPaletteRow = graphicsContext.GetPaletteManager().AllocRow(
GradientPalette(backColor, _materialColorScheme->primary),
_position.y + ICON_Y, _position.y + ICON_Y + 16);
auto iconOam = graphicsContext.GetOamManager().AllocOams(1);
OamBuilder::OamWithSize<16, 16>(_position.x + ICON_X, _position.y + ICON_Y, _iconVramOffset >> 7)
.WithPalette16(iconPaletteRow)
.WithPriority(graphicsContext.GetPriority())
.Build(iconOam[0]);
}
}

View File

@@ -0,0 +1,35 @@
#pragma once
#include "gui/views/ViewContainer.h"
#include "gui/views/Label2DView.h"
class MaterialColorScheme;
class IFontRepository;
class CheatListItemView : public ViewContainer
{
public:
CheatListItemView(const MaterialColorScheme* materialColorScheme, const IFontRepository* fontRepository);
void Update() override;
void Draw(GraphicsContext& graphicsContext) override;
Rectangle GetBounds() const override
{
return Rectangle(_position.x, _position.y, 256, 24);
}
void SetName(const char* name)
{
_nameLabel.SetText(name);
}
void SetIcon(u32 iconVramOffset)
{
_iconVramOffset = iconVramOffset;
}
private:
Label2DView _nameLabel;
u32 _iconVramOffset = 0;
const MaterialColorScheme* _materialColorScheme;
};

View File

@@ -0,0 +1,74 @@
#pragma once
#include "gui/views/RecyclerAdapter.h"
#include "cheats/CheatCategory.h"
#include "cheats/Cheat.h"
#include "CheatListItemView.h"
class CheatsAdapter : public RecyclerAdapter
{
public:
CheatsAdapter(const CheatCategory* categories, u32 numberOfCategories, const Cheat* cheats, u32 numberOfCheats,
const MaterialColorScheme* materialColorScheme, const IFontRepository* fontRepository,
u32 folderIconVramOffset, u32 checkboxUncheckedIconVramOffset, u32 checkboxCheckedIconVramOffset)
: _categories(categories), _numberOfCategories(numberOfCategories), _cheats(cheats), _numberOfCheats(numberOfCheats)
, _materialColorScheme(materialColorScheme), _fontRepository(fontRepository)
, _folderIconVramOffset(folderIconVramOffset)
, _checkboxUncheckedIconVramOffset(checkboxUncheckedIconVramOffset)
, _checkboxCheckedIconVramOffset(checkboxCheckedIconVramOffset) { }
u32 GetItemCount() const override
{
return _numberOfCategories + _numberOfCheats;
}
void GetViewSize(int& width, int& height) const override
{
width = 256;
height = 24;
}
View* CreateView() const override
{
LOG_DEBUG("CheatsAdapter::CreateView\n");
return new CheatListItemView(_materialColorScheme, _fontRepository);
}
void DestroyView(View* view) const override
{
LOG_DEBUG("CheatsAdapter::DestroyView\n");
delete (CheatListItemView*)view;
}
void BindView(View* view, int index) const override
{
LOG_DEBUG("CheatsAdapter::BindView\n");
auto listItemView = static_cast<CheatListItemView*>(view);
if ((u32)index < _numberOfCategories)
{
listItemView->SetName(_categories[index].GetName());
listItemView->SetIcon(_folderIconVramOffset);
}
else
{
index -= _numberOfCategories;
listItemView->SetName(_cheats[index].GetName());
listItemView->SetIcon(_cheats[index].GetIsCheatActive() ? _checkboxCheckedIconVramOffset : _checkboxUncheckedIconVramOffset);
}
}
void ReleaseView(View* view, int index) const override
{
LOG_DEBUG("CheatsAdapter::ReleaseView\n");
}
private:
const CheatCategory* _categories;
u32 _numberOfCategories;
const Cheat* _cheats;
u32 _numberOfCheats;
const MaterialColorScheme* _materialColorScheme;
const IFontRepository* _fontRepository;
u32 _folderIconVramOffset;
u32 _checkboxUncheckedIconVramOffset;
u32 _checkboxCheckedIconVramOffset;
};

View File

@@ -0,0 +1,103 @@
#include "common.h"
#include "gui/GraphicsContext.h"
#include "themes/material/MaterialColorScheme.h"
#include "themes/IFontRepository.h"
#include "gui/input/InputProvider.h"
#include "gui/VramContext.h"
#include "folderIcon.h"
#include "checkboxChecked.h"
#include "checkboxUnchecked.h"
#include "CheatsBottomSheetView.h"
#define TITLE_LABEL_X 20
#define TITLE_LABEL_Y 16
CheatsBottomSheetView::CheatsBottomSheetView(std::unique_ptr<CheatsViewModel> viewModel,
const MaterialColorScheme* materialColorScheme, const IFontRepository* fontRepository,
FocusManager* focusManager)
: _viewModel(std::move(viewModel))
, _titleLabel(128, 16, 25, fontRepository->GetFont(FontType::Medium11))
, _cheatListRecycler(0, 36, 256, 124, RecyclerView::Mode::VerticalList)
, _materialColorScheme(materialColorScheme)
, _fontRepository(fontRepository)
, _focusManager(focusManager)
{
_titleLabel.SetText(u"Cheats");
AddChildTail(&_titleLabel);
AddChildTail(&_cheatListRecycler);
}
void CheatsBottomSheetView::InitVram(const VramContext& vramContext)
{
BottomSheetView::InitVram(vramContext);
const auto objVramManager = vramContext.GetObjVramManager();
if (objVramManager)
{
_folderIconVramOffset = objVramManager->Alloc(folderIconTilesLen);
dma_ntrCopy32(3, folderIconTiles,
objVramManager->GetVramAddress(_folderIconVramOffset), folderIconTilesLen);
_checkboxUncheckedIconVramOffset = objVramManager->Alloc(checkboxUncheckedTilesLen);
dma_ntrCopy32(3, checkboxUncheckedTiles,
objVramManager->GetVramAddress(_checkboxUncheckedIconVramOffset), checkboxUncheckedTilesLen);
_checkboxCheckedIconVramOffset = objVramManager->Alloc(checkboxCheckedTilesLen);
dma_ntrCopy32(3, checkboxCheckedTiles,
objVramManager->GetVramAddress(_checkboxCheckedIconVramOffset), checkboxCheckedTilesLen);
}
_objVramManager = vramContext.GetObjVramManager();
}
void CheatsBottomSheetView::Update()
{
_titleLabel.SetPosition(TITLE_LABEL_X, _position.y + TITLE_LABEL_Y);
_cheatListRecycler.SetPosition(0, _position.y + 36);
if (_viewModel->GetState() == CheatsViewModel::State::DisplayCheats)
{
if (_cheatsAdapter == nullptr)
{
LOG_DEBUG("Setting adapter\n");
auto gameCheats = _viewModel->GetCheats();
u32 numberOfCategories = 0;
auto categories = gameCheats->GetCheatCategories(numberOfCategories);
u32 numberOfCheats = 0;
auto cheats = gameCheats->GetCheats(numberOfCheats);
_cheatsAdapter = new CheatsAdapter(categories, numberOfCategories, cheats, numberOfCheats,
_materialColorScheme, _fontRepository, _folderIconVramOffset,
_checkboxUncheckedIconVramOffset, _checkboxCheckedIconVramOffset);
_cheatListRecycler.SetAdapter(_cheatsAdapter);
_cheatListRecycler.InitVram(VramContext(nullptr, _objVramManager, nullptr, nullptr));
_cheatListRecycler.Focus(*_focusManager);
LOG_DEBUG("Setting adapter done\n");
}
}
BottomSheetView::Update();
}
void CheatsBottomSheetView::Draw(GraphicsContext& graphicsContext)
{
graphicsContext.SetClipArea(GetBounds());
u32 oldPrio = graphicsContext.SetPriority(1);
{
_titleLabel.SetBackgroundColor(_materialColorScheme->GetColor(md::sys::color::surfaceContainerLow));
_titleLabel.SetForegroundColor(_materialColorScheme->onSurface);
_titleLabel.Draw(graphicsContext);
graphicsContext.SetClipArea(_cheatListRecycler.GetBounds());
_cheatListRecycler.Draw(graphicsContext);
// BottomSheetView::Draw(graphicsContext);
}
graphicsContext.SetPriority(oldPrio);
graphicsContext.ResetClipArea();
}
bool CheatsBottomSheetView::HandleInput(const InputProvider& inputProvider, FocusManager& focusManager)
{
if (inputProvider.Triggered(InputKey::B))
{
_viewModel->Close();
return true;
}
return false;
}

View File

@@ -0,0 +1,42 @@
#pragma once
#include <memory>
#include "romBrowser/views/BottomSheetView.h"
#include "gui/views/Label2DView.h"
#include "gui/views/RecyclerView.h"
#include "romBrowser/viewModels/CheatsViewModel.h"
#include "CheatsAdapter.h"
class MaterialColorScheme;
class IFontRepository;
class IVramManager;
class CheatsBottomSheetView : public BottomSheetView
{
public:
CheatsBottomSheetView(std::unique_ptr<CheatsViewModel> viewModel,
const MaterialColorScheme* materialColorScheme, const IFontRepository* fontRepository,
FocusManager* focusManager);
void InitVram(const VramContext& vramContext) override;
void Update() override;
void Draw(GraphicsContext& graphicsContext) override;
bool HandleInput(const InputProvider& inputProvider, FocusManager& focusManager) override;
void Focus(FocusManager& focusManager) override
{
_cheatListRecycler.Focus(focusManager);
}
private:
std::unique_ptr<CheatsViewModel> _viewModel;
Label2DView _titleLabel;
RecyclerView _cheatListRecycler;
CheatsAdapter* _cheatsAdapter = nullptr;
const MaterialColorScheme* _materialColorScheme;
const IFontRepository* _fontRepository;
IVramManager* _objVramManager;
FocusManager* _focusManager;
u32 _folderIconVramOffset = 0;
u32 _checkboxUncheckedIconVramOffset = 0;
u32 _checkboxCheckedIconVramOffset = 0;
};