Further work on support for cheats

Cheats can now be enabled/disabled and games can be launched with cheats
This commit is contained in:
Gericom
2026-02-28 17:00:02 +01:00
parent dddee0bb94
commit f54a379ff2
27 changed files with 660 additions and 171 deletions

View File

@@ -0,0 +1,7 @@
# tile format
-gt
# graphics bit depth is 4 (16 color)
-gB4
-p!

BIN
arm9/gfx/cheatSelector.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 155 B

View File

@@ -7,22 +7,34 @@ public:
Cheat() Cheat()
: _cheatData(nullptr), _cheatDataLength(0) { } : _cheatData(nullptr), _cheatDataLength(0) { }
Cheat(const char* name, const char* description, bool isCheatActive, const void* cheatData, u32 cheatDataLength) Cheat(const char* name, const char* description, u32* flagsPointer, const void* cheatData, u32 cheatDataLength)
: CheatTreeItem(name, description), _isCheatActive(isCheatActive) : CheatTreeItem(name, description), _flagsPointer(flagsPointer)
, _cheatData(cheatData), _cheatDataLength(cheatDataLength) { } , _cheatData(cheatData), _cheatDataLength(cheatDataLength) { }
const void* GetCheatData(u32& cheatDataLength) const
{
cheatDataLength = _cheatDataLength;
return _cheatData;
}
bool GetIsCheatActive() const bool GetIsCheatActive() const
{ {
return _isCheatActive; return ((*_flagsPointer >> 24) & 1) == 1;
} }
void SetIsCheatActive(bool isCheatActive) void SetIsCheatActive(bool isCheatActive) const
{ {
_isCheatActive = isCheatActive; u32 flags = *_flagsPointer;
flags &= ~(1 << 24);
if (isCheatActive)
{
flags |= 1 << 24;
}
*_flagsPointer = flags;
} }
private: private:
bool _isCheatActive; u32* _flagsPointer;
const void* _cheatData; const void* _cheatData;
u32 _cheatDataLength; u32 _cheatDataLength;
}; };

View File

@@ -2,8 +2,9 @@
#include <memory> #include <memory>
#include "CheatTreeItem.h" #include "CheatTreeItem.h"
#include "Cheat.h" #include "Cheat.h"
#include "ICheatCategory.h"
class CheatCategory : public CheatTreeItem class CheatCategory : public CheatTreeItem, public ICheatCategory
{ {
public: public:
CheatCategory() CheatCategory()
@@ -55,18 +56,18 @@ public:
} }
} }
bool GetIsMaxOneCheatActive() const bool GetIsMaxOneCheatActive() const override
{ {
return _isMaxOneCheatActive; return _isMaxOneCheatActive;
} }
const CheatCategory* GetSubCategories(u32& numberOfSubCategories) const const CheatCategory* GetCategories(u32& numberOfCategories) const override
{ {
numberOfSubCategories = _numberOfSubCategories; numberOfCategories = _numberOfSubCategories;
return _subCategories; return _subCategories;
} }
const Cheat* GetCheats(u32& numberOfCheats) const const Cheat* GetCheats(u32& numberOfCheats) const override
{ {
numberOfCheats = _numberOfCheats; numberOfCheats = _numberOfCheats;
return _cheats; return _cheats;

View File

@@ -1,13 +1,14 @@
#pragma once #pragma once
#include "Cheat.h" #include "Cheat.h"
#include "CheatCategory.h" #include "CheatCategory.h"
#include "ICheatCategory.h"
class GameCheats class GameCheats : public ICheatCategory
{ {
public: public:
GameCheats(std::unique_ptr<u8[]> cheatData, const char* gameName, GameCheats(std::unique_ptr<u8[]> cheatData, u32 cheatDataLength, u32 fileOffset, const char* gameName,
CheatCategory* categories, u32 numberOfCategories, Cheat* cheats, u32 numberOfCheats) CheatCategory* categories, u32 numberOfCategories, Cheat* cheats, u32 numberOfCheats)
: _cheatData(std::move(cheatData)), _gameName(gameName) : _cheatData(std::move(cheatData)), _cheatDataLength(cheatDataLength), _fileOffset(fileOffset), _gameName(gameName)
, _categories(categories), _numberOfCategories(numberOfCategories) , _categories(categories), _numberOfCategories(numberOfCategories)
, _cheats(cheats), _numberOfCheats(numberOfCheats) { } , _cheats(cheats), _numberOfCheats(numberOfCheats) { }
@@ -28,20 +29,38 @@ public:
return _gameName; return _gameName;
} }
const CheatCategory* GetCheatCategories(u32& numberOfCategories) const bool GetIsMaxOneCheatActive() const override
{
return false;
}
const CheatCategory* GetCategories(u32& numberOfCategories) const override
{ {
numberOfCategories = _numberOfCategories; numberOfCategories = _numberOfCategories;
return _categories; return _categories;
} }
const Cheat* GetCheats(u32& numberOfCheats) const const Cheat* GetCheats(u32& numberOfCheats) const override
{ {
numberOfCheats = _numberOfCheats; numberOfCheats = _numberOfCheats;
return _cheats; return _cheats;
} }
u8* GetCheatData(u32& cheatDataLength) const
{
cheatDataLength = _cheatDataLength;
return _cheatData.get();
}
u32 GetFileOffset() const
{
return _fileOffset;
}
private: private:
std::unique_ptr<u8[]> _cheatData; std::unique_ptr<u8[]> _cheatData;
u32 _cheatDataLength;
u32 _fileOffset;
const char* _gameName; const char* _gameName;
CheatCategory* _categories; CheatCategory* _categories;
u32 _numberOfCategories; u32 _numberOfCategories;

View File

@@ -0,0 +1,17 @@
#pragma once
class CheatCategory;
class Cheat;
class ICheatCategory
{
public:
virtual ~ICheatCategory() = default;
virtual bool GetIsMaxOneCheatActive() const = 0;
virtual const CheatCategory* GetCategories(u32& numberOfCategories) const;
virtual const Cheat* GetCheats(u32& numberOfCheats) const;
protected:
ICheatCategory() = default;
};

View File

@@ -1,12 +1,14 @@
#pragma once #pragma once
#include "GameCheats.h" #include "GameCheats.h"
#include "fat/FastFileRef.h"
class ICheatRepository class ICheatRepository
{ {
public: public:
virtual ~ICheatRepository() { } virtual ~ICheatRepository() { }
virtual std::unique_ptr<GameCheats> GetCheatsForGame(u32 gameCode, u32 headerCrc32) const = 0; virtual std::unique_ptr<GameCheats> GetCheatsForGame(const FastFileRef& romFile) const = 0;
virtual void UpdateEnabledCheatsForGame(const std::unique_ptr<GameCheats>& cheats) const = 0;
protected: protected:
ICheatRepository() { } ICheatRepository() { }

View File

@@ -0,0 +1,99 @@
#include "common.h"
#include <string.h>
#include "PicoLoaderCheatDataFactory.h"
pload_cheats_t* PicoLoaderCheatDataFactory::CreateCheatData(const std::unique_ptr<GameCheats>& gameCheats) const
{
pload_cheats_t* cheatData = nullptr;
if (gameCheats)
{
u32 totalNumberOfCheats = 0;
u32 requiredSize = GetCheatCategoryRequiredSize(gameCheats.get(), totalNumberOfCheats);
if (totalNumberOfCheats != 0)
{
requiredSize += sizeof(u32) * 2;
cheatData = (pload_cheats_t*)new u8[requiredSize];
cheatData->length = requiredSize;
cheatData->numberOfCheats = totalNumberOfCheats;
u8* buffer = (u8*)&cheatData->firstCheat;
GetCheatCategoryData(gameCheats.get(), buffer);
}
}
return cheatData;
}
u32 PicoLoaderCheatDataFactory::GetCheatCategoryRequiredSize(const ICheatCategory* cheatCategory, u32& totalNumberOfCheats) const
{
u32 requiredSize = 0;
u32 numberOfCategories = 0;
auto subCategories = cheatCategory->GetCategories(numberOfCategories);
for (u32 i = 0; i < numberOfCategories; i++)
{
requiredSize += GetCheatCategoryRequiredSize(&subCategories[i], totalNumberOfCheats);
}
u32 numberOfCheats = 0;
auto cheats = cheatCategory->GetCheats(numberOfCheats);
for (u32 i = 0; i < numberOfCheats; i++)
{
u32 cheatRequiredSize = GetCheatRequiredSize(&cheats[i]);
if (cheatRequiredSize != 0)
{
requiredSize += cheatRequiredSize;
totalNumberOfCheats++;
}
}
return requiredSize;
}
u32 PicoLoaderCheatDataFactory::GetCheatRequiredSize(const Cheat* cheat) const
{
if (cheat->GetIsCheatActive())
{
u32 cheatDataLength = 0;
cheat->GetCheatData(cheatDataLength);
return sizeof(u32) + ((cheatDataLength + 7) & ~7);
}
else
{
return 0;
}
}
void PicoLoaderCheatDataFactory::GetCheatCategoryData(const ICheatCategory* cheatCategory, u8*& buffer) const
{
u32 numberOfCategories = 0;
auto subCategories = cheatCategory->GetCategories(numberOfCategories);
for (u32 i = 0; i < numberOfCategories; i++)
{
GetCheatCategoryData(&subCategories[i], buffer);
}
u32 numberOfCheats = 0;
auto cheats = cheatCategory->GetCheats(numberOfCheats);
for (u32 i = 0; i < numberOfCheats; i++)
{
GetCheatData(&cheats[i], buffer);
}
}
void PicoLoaderCheatDataFactory::GetCheatData(const Cheat* cheat, u8*& buffer) const
{
if (cheat->GetIsCheatActive())
{
u32 cheatDataLength = 0;
auto cheatData = cheat->GetCheatData(cheatDataLength);
u32 paddedCheatDataLength = (cheatDataLength + 7) & ~7;
*(u32*)buffer = paddedCheatDataLength;
buffer += sizeof(u32);
memcpy(buffer, cheatData, cheatDataLength);
if (cheatDataLength != paddedCheatDataLength)
{
memset(buffer + cheatDataLength, 0, paddedCheatDataLength - cheatDataLength);
}
buffer += paddedCheatDataLength;
}
}

View File

@@ -0,0 +1,16 @@
#pragma once
#include <memory>
#include "GameCheats.h"
#include "picoLoader7.h"
class PicoLoaderCheatDataFactory
{
public:
pload_cheats_t* CreateCheatData(const std::unique_ptr<GameCheats>& gameCheats) const;
private:
u32 GetCheatCategoryRequiredSize(const ICheatCategory* cheatCategory, u32& totalNumberOfCheats) const;
u32 GetCheatRequiredSize(const Cheat* cheat) const;
void GetCheatCategoryData(const ICheatCategory* cheatCategory, u8*& buffer) const;
void GetCheatData(const Cheat* cheat, u8*& buffer) const;
};

View File

@@ -3,6 +3,68 @@
#include <string.h> #include <string.h>
#include "UsrCheatRepository.h" #include "UsrCheatRepository.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;
}
std::unique_ptr<GameCheats> UsrCheatRepository::GetCheatsForGame(const FastFileRef& romFile) const
{
auto file = std::make_unique<File>();
file->Open(romFile, 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 nullptr;
}
file->Close();
u32 gameCode = *(u32*)(headerBuffer.get() + 0xC);
u32 crc = crc32(headerBuffer.get(), 512);
headerBuffer.reset();
return GetCheatsForGame(gameCode, crc);
}
void UsrCheatRepository::UpdateEnabledCheatsForGame(const std::unique_ptr<GameCheats>& cheats) const
{
u32 cheatDataLength = 0;
auto cheatData = cheats->GetCheatData(cheatDataLength);
if (_usrCheatFile->Seek(cheats->GetFileOffset()) != FR_OK)
{
LOG_ERROR("Failed to seek to cheat data\n");
return;
}
u32 bytesWritten = 0;
if (_usrCheatFile->Write(cheatData, cheatDataLength, bytesWritten) != FR_OK ||
bytesWritten != cheatDataLength)
{
LOG_ERROR("Failed to write cheat data\n");
return;
}
if (_usrCheatFile->Sync() != FR_OK)
{
LOG_ERROR("Failed to flush cheat data\n");
}
}
std::unique_ptr<GameCheats> UsrCheatRepository::GetCheatsForGame(u32 gameCode, u32 headerCrc32) const std::unique_ptr<GameCheats> UsrCheatRepository::GetCheatsForGame(u32 gameCode, u32 headerCrc32) const
{ {
auto index = FindIndex(gameCode, headerCrc32); auto index = FindIndex(gameCode, headerCrc32);
@@ -14,13 +76,14 @@ std::unique_ptr<GameCheats> UsrCheatRepository::GetCheatsForGame(u32 gameCode, u
return nullptr; return nullptr;
} }
auto cheatData = std::make_unique_for_overwrite<u8[]>(index->padding); // padding was set to the size in UsrCheatRepositoryFactory const u32 cheatDataLength = index->padding; // padding was set to the size in UsrCheatRepositoryFactory
auto cheatData = std::make_unique_for_overwrite<u8[]>(cheatDataLength);
if (_usrCheatFile->Seek(index->offset) != FR_OK) if (_usrCheatFile->Seek(index->offset) != FR_OK)
{ {
LOG_ERROR("Failed to seek to cheat data\n"); LOG_ERROR("Failed to seek to cheat data\n");
return nullptr; return nullptr;
} }
if (!_usrCheatFile->ReadExact(cheatData.get(), index->padding)) if (!_usrCheatFile->ReadExact(cheatData.get(), cheatDataLength))
{ {
LOG_ERROR("Failed to read cheat data\n"); LOG_ERROR("Failed to read cheat data\n");
return nullptr; return nullptr;
@@ -38,7 +101,7 @@ std::unique_ptr<GameCheats> UsrCheatRepository::GetCheatsForGame(u32 gameCode, u
// flags // flags
u32 flags = *(u32*)ptr; u32 flags = *(u32*)ptr;
u32 totalNumberOfItems = flags & 0x0FFFFFFF; u32 totalNumberOfItems = flags & 0x0FFFFFFF;
u32 gameActive = flags >> 28; // u32 gameActive = flags >> 28;
ptr += 4; ptr += 4;
// master codes // master codes
@@ -50,7 +113,7 @@ std::unique_ptr<GameCheats> UsrCheatRepository::GetCheatsForGame(u32 gameCode, u
auto cheats = (Cheat*)malloc(totalNumberOfItems * sizeof(Cheat)); auto cheats = (Cheat*)malloc(totalNumberOfItems * sizeof(Cheat));
u32 cheatCount = 0; u32 cheatCount = 0;
while (ptr < cheatData.get() + index->padding) while (ptr < cheatData.get() + cheatDataLength)
{ {
u32 itemFlags = *(u32*)ptr; u32 itemFlags = *(u32*)ptr;
bool isCategory = ((itemFlags >> 28) & 1) == 1; bool isCategory = ((itemFlags >> 28) & 1) == 1;
@@ -69,7 +132,8 @@ std::unique_ptr<GameCheats> UsrCheatRepository::GetCheatsForGame(u32 gameCode, u
categories = (CheatCategory*)realloc(categories, categoryCount * sizeof(CheatCategory)); categories = (CheatCategory*)realloc(categories, categoryCount * sizeof(CheatCategory));
cheats = (Cheat*)realloc(cheats, cheatCount * sizeof(Cheat)); cheats = (Cheat*)realloc(cheats, cheatCount * sizeof(Cheat));
return std::make_unique<GameCheats>(std::move(cheatData), gameName, categories, categoryCount, cheats, cheatCount); return std::make_unique<GameCheats>(
std::move(cheatData), cheatDataLength, index->offset, gameName, categories, categoryCount, cheats, cheatCount);
} }
const usr_cheat_index_entry_t* UsrCheatRepository::FindIndex(u32 gameCode, u32 headerCrc32) const const usr_cheat_index_entry_t* UsrCheatRepository::FindIndex(u32 gameCode, u32 headerCrc32) const
@@ -142,15 +206,14 @@ void UsrCheatRepository::ParseCategory(CheatCategory& category, u8*& ptr) const
categories = (CheatCategory*)realloc(categories, categoryCount * sizeof(CheatCategory)); categories = (CheatCategory*)realloc(categories, categoryCount * sizeof(CheatCategory));
cheats = (Cheat*)realloc(cheats, cheatCount * sizeof(Cheat)); cheats = (Cheat*)realloc(cheats, cheatCount * sizeof(Cheat));
category = CheatCategory(itemName, itemDescription, isMaxOneCheatActive, categories, categoryCount, cheats, cheatCount); new (&category) CheatCategory (itemName, itemDescription, isMaxOneCheatActive, categories, categoryCount, cheats, cheatCount);
} }
void UsrCheatRepository::ParseCheat(Cheat& cheat, u8*& ptr) const void UsrCheatRepository::ParseCheat(Cheat& cheat, u8*& ptr) const
{ {
// flags // flags
u32 itemFlags = *(u32*)ptr; u32* flagsPtr = (u32*)ptr;
ptr += 4; ptr += 4;
bool isCheatActive = ((itemFlags >> 24) & 1) == 1;
// item name // item name
const char* itemName = (const char*)ptr; const char* itemName = (const char*)ptr;
@@ -167,7 +230,7 @@ void UsrCheatRepository::ParseCheat(Cheat& cheat, u8*& ptr) const
u32 numberOfCodeWords = *(u32*)ptr; u32 numberOfCodeWords = *(u32*)ptr;
ptr += 4; ptr += 4;
cheat = Cheat(itemName, itemDescription, isCheatActive, ptr, numberOfCodeWords * 4); new (&cheat) Cheat(itemName, itemDescription, flagsPtr, ptr, numberOfCodeWords * 4);
// code // code
ptr += numberOfCodeWords * 4; ptr += numberOfCodeWords * 4;

View File

@@ -12,13 +12,15 @@ public:
: _usrCheatFile(std::move(usrCheatDatFile)) : _usrCheatFile(std::move(usrCheatDatFile))
, _sortedIndices(std::move(sortedIndices)), _numberOfIndices(numberOfIndices) { } , _sortedIndices(std::move(sortedIndices)), _numberOfIndices(numberOfIndices) { }
std::unique_ptr<GameCheats> GetCheatsForGame(u32 gameCode, u32 headerCrc32) const override; std::unique_ptr<GameCheats> GetCheatsForGame(const FastFileRef& romFile) const override;
void UpdateEnabledCheatsForGame(const std::unique_ptr<GameCheats>& cheats) const override;
private: private:
std::unique_ptr<File> _usrCheatFile; std::unique_ptr<File> _usrCheatFile;
std::unique_ptr<usr_cheat_index_entry_t[]> _sortedIndices; std::unique_ptr<usr_cheat_index_entry_t[]> _sortedIndices;
u32 _numberOfIndices; u32 _numberOfIndices;
std::unique_ptr<GameCheats> GetCheatsForGame(u32 gameCode, u32 headerCrc32) const;
const usr_cheat_index_entry_t* FindIndex(u32 gameCode, u32 headerCrc32) const; const usr_cheat_index_entry_t* FindIndex(u32 gameCode, u32 headerCrc32) const;
void ParseCategory(CheatCategory& category, u8*& ptr) const; void ParseCategory(CheatCategory& category, u8*& ptr) const;
void ParseCheat(Cheat& cheat, u8*& ptr) const; void ParseCheat(Cheat& cheat, u8*& ptr) const;

View File

@@ -65,7 +65,7 @@ template <>
class Task<void> : public TaskBase class Task<void> : public TaskBase
{ {
protected: protected:
virtual TaskResult<void> ExecuteFunc() const = 0; virtual TaskResult<void> ExecuteFunc() = 0;
private: private:
TaskState ExecuteDirect() override TaskState ExecuteDirect() override
@@ -79,11 +79,11 @@ template <class T, typename FuncType>
class FuncTask : public Task<T> class FuncTask : public Task<T>
{ {
public: public:
FuncTask(const FuncType& function) FuncTask(FuncType&& function)
: _function(function) { } : _function(std::move(function)) { }
private: private:
const FuncType _function; FuncType _function;
TaskResult<T> ExecuteFunc() const override { return _function((const volatile u8&)this->_cancelRequested); } TaskResult<T> ExecuteFunc() override { return _function((const volatile u8&)this->_cancelRequested); }
}; };

View File

@@ -79,12 +79,12 @@ public:
template <typename FuncType> template <typename FuncType>
[[gnu::noinline]] [[gnu::noinline]]
auto Enqueue(const FuncType& function) -> QueueTask<decltype(TaskResultToResultType(function(*new vu8())))> auto Enqueue(FuncType&& function) -> QueueTask<decltype(TaskResultToResultType(function(*new vu8())))>
{ {
using TaskType = FuncTask<decltype(TaskResultToResultType(function(*new vu8()))), FuncType>; using TaskType = FuncTask<decltype(TaskResultToResultType(function(*new vu8()))), FuncType>;
// static_assert(sizeof(TaskType) <= MaxTaskSize, "Task is too big for this pool"); // static_assert(sizeof(TaskType) <= MaxTaskSize, "Task is too big for this pool");
void* slot = GetSlot(); void* slot = GetSlot();
auto task = new (slot) TaskType(function); auto task = new (slot) TaskType(std::move(function));
Enqueue(task); Enqueue(task);
return QueueTask(task, this); return QueueTask(task, this);
} }

View File

@@ -84,6 +84,7 @@ public:
u32 AllocRow(const IPalette& palette, int yStart, int yEnd) override u32 AllocRow(const IPalette& palette, int yStart, int yEnd) override
{ {
yEnd++; // avoid back to back allocation
return AllocRowInternal(_rows[_curSet], palette, yStart, yEnd); return AllocRowInternal(_rows[_curSet], palette, yStart, yEnd);
} }

View File

@@ -39,6 +39,10 @@ void RecyclerView::SetAdapter(const RecyclerAdapter* adapter, int initialSelecte
_viewPool.reset(); _viewPool.reset();
_viewPoolFreeCount = 0; _viewPoolFreeCount = 0;
_viewPoolTotalCount = 0; _viewPoolTotalCount = 0;
_xOffset = 0;
_yOffset = 0;
_curRangeStart = 0;
_curRangeLength = 0;
} }
_adapter = adapter; _adapter = adapter;
_adapter->GetViewSize(_itemWidth, _itemHeight); _adapter->GetViewSize(_itemWidth, _itemHeight);

View File

@@ -21,6 +21,7 @@ typedef void (*pico_loader_9_func_t)(void);
static pload_params_t sLoadParams; static pload_params_t sLoadParams;
static char sLauncherPath[256] alignas(32); static char sLauncherPath[256] alignas(32);
static PicoLoaderBootDrive sBootDrive; static PicoLoaderBootDrive sBootDrive;
static const pload_cheats_t* sCheatData = nullptr;
pload_params_t* pload_getLoadParams() pload_params_t* pload_getLoadParams()
{ {
@@ -37,6 +38,11 @@ void pload_setLauncherPath(const char* launcherPath)
StringUtil::Copy(sLauncherPath, launcherPath, sizeof(sLauncherPath)); StringUtil::Copy(sLauncherPath, launcherPath, sizeof(sLauncherPath));
} }
void pload_setCheatData(const pload_cheats_t* cheatData)
{
sCheatData = cheatData;
}
void pload_start() void pload_start()
{ {
mem_setVramAMapping(MEM_VRAM_AB_LCDC); mem_setVramAMapping(MEM_VRAM_AB_LCDC);
@@ -92,6 +98,10 @@ void pload_start()
{ {
dma_ntrCopy16(3, &sLauncherPath, &header->v2.launcherPath, sizeof(header->v2.launcherPath)); dma_ntrCopy16(3, &sLauncherPath, &header->v2.launcherPath, sizeof(header->v2.launcherPath));
} }
if (header->apiVersion >= 3)
{
header->v3.cheats = sCheatData;
}
mem_setVramCMapping(MEM_VRAM_C_ARM7_00000); mem_setVramCMapping(MEM_VRAM_C_ARM7_00000);
mem_setVramDMapping(MEM_VRAM_D_ARM7_20000); mem_setVramDMapping(MEM_VRAM_D_ARM7_20000);
ipc_sendFifoMessage(IPC_CHANNEL_LOADER, 1); ipc_sendFifoMessage(IPC_CHANNEL_LOADER, 1);

View File

@@ -4,4 +4,5 @@
pload_params_t* pload_getLoadParams(); pload_params_t* pload_getLoadParams();
void pload_setBootDrive(PicoLoaderBootDrive bootDrive); void pload_setBootDrive(PicoLoaderBootDrive bootDrive);
void pload_setLauncherPath(const char* launcherPath); void pload_setLauncherPath(const char* launcherPath);
void pload_setCheatData(const pload_cheats_t* cheatData);
void pload_start(); void pload_start();

View File

@@ -7,6 +7,7 @@
#include "SdFolderFactory.h" #include "SdFolderFactory.h"
#include "services/settings/IAppSettingsService.h" #include "services/settings/IAppSettingsService.h"
#include "cheats/UsrCheatRepositoryFactory.h" #include "cheats/UsrCheatRepositoryFactory.h"
#include "cheats/PicoLoaderCheatDataFactory.h"
#include "RomBrowserController.h" #include "RomBrowserController.h"
RomBrowserController::RomBrowserController( RomBrowserController::RomBrowserController(
@@ -182,15 +183,35 @@ void RomBrowserController::HandleLaunchTrigger()
{ {
LOG_DEBUG("RomBrowserStateTrigger::Launch\n"); LOG_DEBUG("RomBrowserStateTrigger::Launch\n");
_ioTaskQueue->Enqueue([this] (const vu8& cancelRequested) _ioTaskQueue->Enqueue([this] (const vu8& cancelRequested)
{
UpdateLastUsedFilepath();
SetPicoLoaderParams();
LoadCheats();
return TaskResult<void>::Completed();
});
}
void RomBrowserController::HandleChangeDisplayModeTrigger()
{
LOG_DEBUG("RomBrowserStateTrigger::ChangeDisplayMode\n");
_romBrowserViewModel = SharedPtr(new RomBrowserViewModel(this));
}
void RomBrowserController::UpdateLastUsedFilepath()
{ {
f_getcwd(_navigatePath, sizeof(_navigatePath) / sizeof(_navigatePath[0])); f_getcwd(_navigatePath, sizeof(_navigatePath) / sizeof(_navigatePath[0]));
int idx = strlcat(_navigatePath, "/", sizeof(_navigatePath)); int idx = strlcat(_navigatePath, "/", sizeof(_navigatePath));
if (_navigatePath[idx - 2] == '/') if (_navigatePath[idx - 2] == '/')
{
_navigatePath[idx - 1] = 0; _navigatePath[idx - 1] = 0;
}
strlcat(_navigatePath, _triggerFileInfo.GetFileName(), sizeof(_navigatePath)); strlcat(_navigatePath, _triggerFileInfo.GetFileName(), sizeof(_navigatePath));
_appSettingsService->GetAppSettings().lastUsedFilePath = _navigatePath; _appSettingsService->GetAppSettings().lastUsedFilePath = _navigatePath;
_appSettingsService->Save(); _appSettingsService->Save();
}
void RomBrowserController::SetPicoLoaderParams() const
{
auto loadParams = pload_getLoadParams(); auto loadParams = pload_getLoadParams();
loadParams->savePath[0] = 0; loadParams->savePath[0] = 0;
loadParams->arguments[0] = 0; loadParams->arguments[0] = 0;
@@ -203,12 +224,11 @@ void RomBrowserController::HandleLaunchTrigger()
{ {
LOG_FATAL("Failed to set launch parameters.\n"); LOG_FATAL("Failed to set launch parameters.\n");
} }
return TaskResult<void>::Completed();
});
} }
void RomBrowserController::HandleChangeDisplayModeTrigger() void RomBrowserController::LoadCheats() const
{ {
LOG_DEBUG("RomBrowserStateTrigger::ChangeDisplayMode\n"); auto cheats = _cheatRepository->GetCheatsForGame(_triggerFileInfo.GetFastFileRef());
_romBrowserViewModel = SharedPtr(new RomBrowserViewModel(this)); auto cheatData = PicoLoaderCheatDataFactory().CreateCheatData(cheats);
pload_setCheatData(cheatData);
} }

View File

@@ -74,4 +74,7 @@ private:
void HandleFolderLoadDoneTrigger(); void HandleFolderLoadDoneTrigger();
void HandleLaunchTrigger(); void HandleLaunchTrigger();
void HandleChangeDisplayModeTrigger(); void HandleChangeDisplayModeTrigger();
void UpdateLastUsedFilepath();
void SetPicoLoaderParams() const;
void LoadCheats() const;
}; };

View File

@@ -3,57 +3,16 @@
#include "fat/File.h" #include "fat/File.h"
#include "CheatsViewModel.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) CheatsViewModel::CheatsViewModel(const FileInfo& romFileInfo, IRomBrowserController* romBrowserController)
: _romFileInfo(romFileInfo), _romBrowserController(romBrowserController) : _romFileInfo(romFileInfo), _romBrowserController(romBrowserController)
{ {
_categoryStack.fill(nullptr);
_loadCheatsTask = _romBrowserController->GetIoTaskQueue()->Enqueue([this] (const vu8& cancelRequested) _loadCheatsTask = _romBrowserController->GetIoTaskQueue()->Enqueue([this] (const vu8& cancelRequested)
{ {
LOG_DEBUG("%s\n", _romFileInfo.GetFileName()); _cheats = _romBrowserController->GetCheatRepository().GetCheatsForGame(_romFileInfo.GetFastFileRef());
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) if (_cheats)
{ {
_categoryStack[0] = _cheats.get();
_state = State::DisplayCheats; _state = State::DisplayCheats;
} }
else else
@@ -64,3 +23,64 @@ CheatsViewModel::CheatsViewModel(const FileInfo& romFileInfo, IRomBrowserControl
return TaskResult<void>::Completed(); return TaskResult<void>::Completed();
}); });
} }
void CheatsViewModel::ItemActivated()
{
auto cheatCategory = GetCurrentCheatCategory();
u32 numberOfCategories = 0;
auto categories = cheatCategory->GetCategories(numberOfCategories);
u32 numberOfCheats = 0;
auto cheats = cheatCategory->GetCheats(numberOfCheats);
if (_selectedItem < (int)numberOfCategories)
{
// Category activated
if (_categoryStackLevel + 1 != _categoryStack.size())
{
_categoryStack[++_categoryStackLevel] = &categories[_selectedItem];
}
}
else
{
// Toggle cheat on/off
auto& cheat = cheats[_selectedItem - numberOfCategories];
bool isEnabled = !cheat.GetIsCheatActive();
if (isEnabled && cheatCategory->GetIsMaxOneCheatActive())
{
for (u32 i = 0; i < numberOfCheats; i++)
{
cheats[i].SetIsCheatActive(false);
}
}
cheat.SetIsCheatActive(isEnabled);
_changed = true;
}
}
void CheatsViewModel::Back()
{
if (_categoryStackLevel == 0)
{
Close();
}
else
{
_categoryStack[_categoryStackLevel--] = nullptr;
}
}
void CheatsViewModel::Close()
{
if (_changed)
{
// Save which cheats are enabled/disabled
_romBrowserController->GetIoTaskQueue()->Enqueue(
[romBrowserController = _romBrowserController, cheats = move(_cheats)] (const vu8& cancelRequested)
{
romBrowserController->GetCheatRepository().UpdateEnabledCheatsForGame(cheats);
return TaskResult<void>::Completed();
});
}
_romBrowserController->HideGameInfo();
}

View File

@@ -1,4 +1,5 @@
#pragma once #pragma once
#include <array>
#include <memory> #include <memory>
#include "core/task/TaskQueue.h" #include "core/task/TaskQueue.h"
#include "cheats/GameCheats.h" #include "cheats/GameCheats.h"
@@ -18,13 +19,15 @@ public:
CheatsViewModel(const FileInfo& romFileInfo, IRomBrowserController* romBrowserController); CheatsViewModel(const FileInfo& romFileInfo, IRomBrowserController* romBrowserController);
void Close() void ItemActivated();
{ void Back();
_romBrowserController->HideGameInfo(); void Close();
}
State GetState() const { return _state; } State GetState() const { return _state; }
const GameCheats* GetCheats() const { return _cheats.get(); } const ICheatCategory* GetCurrentCheatCategory() const { return _categoryStack[_categoryStackLevel]; }
constexpr int GetSelectedItem() const { return _selectedItem; }
void SetSelectedItem(int selectedItem) { _selectedItem = selectedItem; }
private: private:
FileInfo _romFileInfo; FileInfo _romFileInfo;
@@ -32,4 +35,8 @@ private:
QueueTask<void> _loadCheatsTask; QueueTask<void> _loadCheatsTask;
std::unique_ptr<GameCheats> _cheats; std::unique_ptr<GameCheats> _cheats;
State _state = State::Loading; State _state = State::Loading;
int _selectedItem = -1;
bool _changed = false;
u32 _categoryStackLevel = 0;
std::array<const ICheatCategory*, 8> _categoryStack;
}; };

View File

@@ -6,14 +6,16 @@
#include "gui/OamBuilder.h" #include "gui/OamBuilder.h"
#include "CheatListItemView.h" #include "CheatListItemView.h"
#define ICON_X 20 #define ICON_X 4
#define ICON_Y 3 #define ICON_Y 4
#define NAME_LABEL_X 40 #define NAME_LABEL_X 24
#define NAME_LABEL_Y 4 #define NAME_LABEL_Y 5
CheatListItemView::CheatListItemView(const MaterialColorScheme* materialColorScheme, const IFontRepository* fontRepository) CheatListItemView::CheatListItemView(const VramOffsets& vramOffsets,
const MaterialColorScheme* materialColorScheme, const IFontRepository* fontRepository)
: _nameLabel(200, 16, 64, fontRepository->GetFont(FontType::Regular10)) : _nameLabel(200, 16, 64, fontRepository->GetFont(FontType::Regular10))
, _vramOffsets(vramOffsets)
, _materialColorScheme(materialColorScheme) , _materialColorScheme(materialColorScheme)
{ {
_nameLabel.SetEllipsis(true); _nameLabel.SetEllipsis(true);
@@ -22,15 +24,57 @@ CheatListItemView::CheatListItemView(const MaterialColorScheme* materialColorSch
void CheatListItemView::Update() void CheatListItemView::Update()
{ {
_nameLabel.SetPosition(NAME_LABEL_X, _position.y + NAME_LABEL_Y); _nameLabel.SetPosition(_position.x + NAME_LABEL_X, _position.y + NAME_LABEL_Y);
if (_cheat != nullptr)
{
_iconVramOffset = _cheat->GetIsCheatActive()
? _vramOffsets.checkboxCheckedIconVramOffset
: _vramOffsets.checkboxUncheckedIconVramOffset;
}
ViewContainer::Update(); ViewContainer::Update();
} }
void CheatListItemView::Draw(GraphicsContext& graphicsContext) void CheatListItemView::Draw(GraphicsContext& graphicsContext)
{ {
if (!graphicsContext.IsVisible(GetBounds()))
{
return;
}
auto backColor = _materialColorScheme->GetColor(md::sys::color::surfaceContainerLow); auto backColor = _materialColorScheme->GetColor(md::sys::color::surfaceContainerLow);
if (IsFocused())
{
auto selectorFullColor = _materialColorScheme->GetColor(md::sys::color::onSurface);
backColor = RgbMixer::Lerp(backColor, selectorFullColor, 10, 100);
}
_nameLabel.SetBackgroundColor(backColor); _nameLabel.SetBackgroundColor(backColor);
_nameLabel.SetForegroundColor(_materialColorScheme->onSurface); _nameLabel.SetForegroundColor(_materialColorScheme->onSurface);
if (IsFocused())
{
u32 selectorPaletteRow = graphicsContext.GetPaletteManager().AllocRow(
GradientPalette(backColor, backColor),
_position.y, _position.y + 24);
auto selectorOam = graphicsContext.GetOamManager().AllocOams(4);
OamBuilder::OamWithSize<64, 32>(_position.x, _position.y, _vramOffsets.cheatSelectorVramOffset >> 7)
.WithPalette16(selectorPaletteRow)
.WithPriority(graphicsContext.GetPriority())
.Build(selectorOam[0]);
OamBuilder::OamWithSize<64, 32>(_position.x + 64, _position.y, _vramOffsets.cheatSelectorVramOffset >> 7)
.WithPalette16(selectorPaletteRow)
.WithPriority(graphicsContext.GetPriority())
.Build(selectorOam[1]);
OamBuilder::OamWithSize<64, 32>(_position.x + 2 * 64, _position.y, _vramOffsets.cheatSelectorVramOffset >> 7)
.WithPalette16(selectorPaletteRow)
.WithPriority(graphicsContext.GetPriority())
.Build(selectorOam[2]);
OamBuilder::OamWithSize<64, 32>(_position.x + 2 * 64 + 32, _position.y, _vramOffsets.cheatSelectorVramOffset >> 7)
.WithPalette16(selectorPaletteRow)
.WithPriority(graphicsContext.GetPriority())
.Build(selectorOam[3]);
}
ViewContainer::Draw(graphicsContext); ViewContainer::Draw(graphicsContext);
if (graphicsContext.IsVisible(Rectangle(_position.x + ICON_X, _position.y + ICON_Y, 16, 16))) if (graphicsContext.IsVisible(Rectangle(_position.x + ICON_X, _position.y + ICON_Y, 16, 16)))

View File

@@ -1,6 +1,8 @@
#pragma once #pragma once
#include "gui/views/ViewContainer.h" #include "gui/views/ViewContainer.h"
#include "gui/views/Label2DView.h" #include "gui/views/Label2DView.h"
#include "cheats/CheatCategory.h"
#include "cheats/Cheat.h"
class MaterialColorScheme; class MaterialColorScheme;
class IFontRepository; class IFontRepository;
@@ -8,14 +10,22 @@ class IFontRepository;
class CheatListItemView : public ViewContainer class CheatListItemView : public ViewContainer
{ {
public: public:
CheatListItemView(const MaterialColorScheme* materialColorScheme, const IFontRepository* fontRepository); struct VramOffsets
{
u32 folderIconVramOffset = 0;
u32 checkboxUncheckedIconVramOffset = 0;
u32 checkboxCheckedIconVramOffset = 0;
u32 cheatSelectorVramOffset = 0;
};
CheatListItemView(const VramOffsets& vramOffsets, const MaterialColorScheme* materialColorScheme, const IFontRepository* fontRepository);
void Update() override; void Update() override;
void Draw(GraphicsContext& graphicsContext) override; void Draw(GraphicsContext& graphicsContext) override;
Rectangle GetBounds() const override Rectangle GetBounds() const override
{ {
return Rectangle(_position.x, _position.y, 256, 24); return Rectangle(_position.x, _position.y, 224, 24);
} }
void SetName(const char* name) void SetName(const char* name)
@@ -23,13 +33,23 @@ public:
_nameLabel.SetText(name); _nameLabel.SetText(name);
} }
void SetIcon(u32 iconVramOffset) void SetCategory(const CheatCategory* cheatCategory)
{ {
_iconVramOffset = iconVramOffset; _cheat = nullptr;
_nameLabel.SetText(cheatCategory->GetName());
_iconVramOffset = _vramOffsets.folderIconVramOffset;
}
void SetCheat(const Cheat* cheat)
{
_cheat = cheat;
_nameLabel.SetText(_cheat->GetName());
} }
private: private:
Label2DView _nameLabel; Label2DView _nameLabel;
u32 _iconVramOffset = 0; VramOffsets _vramOffsets;
const MaterialColorScheme* _materialColorScheme; const MaterialColorScheme* _materialColorScheme;
u32 _iconVramOffset = 0;
const Cheat* _cheat = nullptr;
}; };

View File

@@ -7,30 +7,30 @@
class CheatsAdapter : public RecyclerAdapter class CheatsAdapter : public RecyclerAdapter
{ {
public: public:
CheatsAdapter(const CheatCategory* categories, u32 numberOfCategories, const Cheat* cheats, u32 numberOfCheats, CheatsAdapter(const ICheatCategory* cheatCategory, const MaterialColorScheme* materialColorScheme,
const MaterialColorScheme* materialColorScheme, const IFontRepository* fontRepository, const IFontRepository* fontRepository, const CheatListItemView::VramOffsets& vramOffsets)
u32 folderIconVramOffset, u32 checkboxUncheckedIconVramOffset, u32 checkboxCheckedIconVramOffset) : _cheatCategory(cheatCategory), _materialColorScheme(materialColorScheme)
: _categories(categories), _numberOfCategories(numberOfCategories), _cheats(cheats), _numberOfCheats(numberOfCheats) , _fontRepository(fontRepository), _vramOffsets(vramOffsets) { }
, _materialColorScheme(materialColorScheme), _fontRepository(fontRepository)
, _folderIconVramOffset(folderIconVramOffset)
, _checkboxUncheckedIconVramOffset(checkboxUncheckedIconVramOffset)
, _checkboxCheckedIconVramOffset(checkboxCheckedIconVramOffset) { }
u32 GetItemCount() const override u32 GetItemCount() const override
{ {
return _numberOfCategories + _numberOfCheats; u32 numberOfCategories = 0;
_cheatCategory->GetCategories(numberOfCategories);
u32 numberOfCheats = 0;
_cheatCategory->GetCheats(numberOfCheats);
return numberOfCategories + numberOfCheats;
} }
void GetViewSize(int& width, int& height) const override void GetViewSize(int& width, int& height) const override
{ {
width = 256; width = 224;
height = 24; height = 24;
} }
View* CreateView() const override View* CreateView() const override
{ {
LOG_DEBUG("CheatsAdapter::CreateView\n"); LOG_DEBUG("CheatsAdapter::CreateView\n");
return new CheatListItemView(_materialColorScheme, _fontRepository); return new CheatListItemView(_vramOffsets, _materialColorScheme, _fontRepository);
} }
void DestroyView(View* view) const override void DestroyView(View* view) const override
@@ -43,16 +43,18 @@ public:
{ {
LOG_DEBUG("CheatsAdapter::BindView\n"); LOG_DEBUG("CheatsAdapter::BindView\n");
auto listItemView = static_cast<CheatListItemView*>(view); auto listItemView = static_cast<CheatListItemView*>(view);
if ((u32)index < _numberOfCategories) u32 numberOfCategories = 0;
auto categories = _cheatCategory->GetCategories(numberOfCategories);
if ((u32)index < numberOfCategories)
{ {
listItemView->SetName(_categories[index].GetName()); listItemView->SetCategory(&categories[index]);
listItemView->SetIcon(_folderIconVramOffset);
} }
else else
{ {
index -= _numberOfCategories; index -= numberOfCategories;
listItemView->SetName(_cheats[index].GetName()); u32 numberOfCheats = 0;
listItemView->SetIcon(_cheats[index].GetIsCheatActive() ? _checkboxCheckedIconVramOffset : _checkboxUncheckedIconVramOffset); auto cheats = _cheatCategory->GetCheats(numberOfCheats);
listItemView->SetCheat(&cheats[index]);
} }
} }
@@ -62,13 +64,8 @@ public:
} }
private: private:
const CheatCategory* _categories; const ICheatCategory* _cheatCategory;
u32 _numberOfCategories;
const Cheat* _cheats;
u32 _numberOfCheats;
const MaterialColorScheme* _materialColorScheme; const MaterialColorScheme* _materialColorScheme;
const IFontRepository* _fontRepository; const IFontRepository* _fontRepository;
u32 _folderIconVramOffset; CheatListItemView::VramOffsets _vramOffsets;
u32 _checkboxUncheckedIconVramOffset;
u32 _checkboxCheckedIconVramOffset;
}; };

View File

@@ -4,27 +4,34 @@
#include "themes/IFontRepository.h" #include "themes/IFontRepository.h"
#include "gui/input/InputProvider.h" #include "gui/input/InputProvider.h"
#include "gui/VramContext.h" #include "gui/VramContext.h"
#include "gui/palette/GradientPalette.h"
#include "gui/OamBuilder.h"
#include "folderIcon.h" #include "folderIcon.h"
#include "checkboxChecked.h" #include "checkboxChecked.h"
#include "checkboxUnchecked.h" #include "checkboxUnchecked.h"
#include "cheatSelector.h"
#include "gui/DescendingStackVramManager.h"
#include "CheatsBottomSheetView.h" #include "CheatsBottomSheetView.h"
#define TITLE_LABEL_X 20 #define TITLE_LABEL_X 20
#define TITLE_LABEL_Y 16 #define TITLE_LABEL_Y 16
#define LIST_X 16
#define LIST_Y 36
CheatsBottomSheetView::CheatsBottomSheetView(std::unique_ptr<CheatsViewModel> viewModel, CheatsBottomSheetView::CheatsBottomSheetView(std::unique_ptr<CheatsViewModel> viewModel,
const MaterialColorScheme* materialColorScheme, const IFontRepository* fontRepository, const MaterialColorScheme* materialColorScheme, const IFontRepository* fontRepository,
FocusManager* focusManager) FocusManager* focusManager)
: _viewModel(std::move(viewModel)) : _viewModel(std::move(viewModel))
, _titleLabel(128, 16, 25, fontRepository->GetFont(FontType::Medium11)) , _titleLabel(128, 16, 25, fontRepository->GetFont(FontType::Medium11))
, _cheatListRecycler(0, 36, 256, 124, RecyclerView::Mode::VerticalList) , _cheatListRecycler(std::make_unique<RecyclerView>(LIST_X, LIST_Y, 224, 124, RecyclerView::Mode::VerticalList))
, _materialColorScheme(materialColorScheme) , _materialColorScheme(materialColorScheme)
, _fontRepository(fontRepository) , _fontRepository(fontRepository)
, _focusManager(focusManager) , _focusManager(focusManager)
{ {
_titleLabel.SetText(u"Cheats"); _titleLabel.SetText(u"Cheats");
AddChildTail(&_titleLabel); AddChildTail(&_titleLabel);
AddChildTail(&_cheatListRecycler); AddChildTail(_cheatListRecycler.get());
} }
void CheatsBottomSheetView::InitVram(const VramContext& vramContext) void CheatsBottomSheetView::InitVram(const VramContext& vramContext)
@@ -34,17 +41,21 @@ void CheatsBottomSheetView::InitVram(const VramContext& vramContext)
const auto objVramManager = vramContext.GetObjVramManager(); const auto objVramManager = vramContext.GetObjVramManager();
if (objVramManager) if (objVramManager)
{ {
_folderIconVramOffset = objVramManager->Alloc(folderIconTilesLen); _vramOffsets.folderIconVramOffset = objVramManager->Alloc(folderIconTilesLen);
dma_ntrCopy32(3, folderIconTiles, dma_ntrCopy32(3, folderIconTiles,
objVramManager->GetVramAddress(_folderIconVramOffset), folderIconTilesLen); objVramManager->GetVramAddress(_vramOffsets.folderIconVramOffset), folderIconTilesLen);
_checkboxUncheckedIconVramOffset = objVramManager->Alloc(checkboxUncheckedTilesLen); _vramOffsets.checkboxUncheckedIconVramOffset = objVramManager->Alloc(checkboxUncheckedTilesLen);
dma_ntrCopy32(3, checkboxUncheckedTiles, dma_ntrCopy32(3, checkboxUncheckedTiles,
objVramManager->GetVramAddress(_checkboxUncheckedIconVramOffset), checkboxUncheckedTilesLen); objVramManager->GetVramAddress(_vramOffsets.checkboxUncheckedIconVramOffset), checkboxUncheckedTilesLen);
_checkboxCheckedIconVramOffset = objVramManager->Alloc(checkboxCheckedTilesLen); _vramOffsets.checkboxCheckedIconVramOffset = objVramManager->Alloc(checkboxCheckedTilesLen);
dma_ntrCopy32(3, checkboxCheckedTiles, dma_ntrCopy32(3, checkboxCheckedTiles,
objVramManager->GetVramAddress(_checkboxCheckedIconVramOffset), checkboxCheckedTilesLen); objVramManager->GetVramAddress(_vramOffsets.checkboxCheckedIconVramOffset), checkboxCheckedTilesLen);
_vramOffsets.cheatSelectorVramOffset = objVramManager->Alloc(cheatSelectorTilesLen);
dma_ntrCopy32(3, cheatSelectorTiles,
objVramManager->GetVramAddress(_vramOffsets.cheatSelectorVramOffset), cheatSelectorTilesLen);
} }
_objVramManager = vramContext.GetObjVramManager(); _objVramManager = vramContext.GetObjVramManager();
@@ -53,27 +64,24 @@ 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);
_cheatListRecycler.SetPosition(0, _position.y + 36); _cheatListRecycler->SetPosition(LIST_X, _position.y + LIST_Y);
if (_viewModel->GetState() == CheatsViewModel::State::DisplayCheats) if (_viewModel->GetState() == CheatsViewModel::State::DisplayCheats)
{ {
if (_cheatsAdapter == nullptr) if (_cheatsAdapter == nullptr)
{ {
LOG_DEBUG("Setting adapter\n"); _cheatsAdapter = new CheatsAdapter(
auto gameCheats = _viewModel->GetCheats(); _viewModel->GetCurrentCheatCategory(), _materialColorScheme, _fontRepository, _vramOffsets);
u32 numberOfCategories = 0; _cheatListRecycler->SetAdapter(_cheatsAdapter);
auto categories = gameCheats->GetCheatCategories(numberOfCategories);
u32 numberOfCheats = 0; // Ugly hack
auto cheats = gameCheats->GetCheats(numberOfCheats); _savedVramState = ((DescendingStackVramManager*)_objVramManager)->GetState();
_cheatsAdapter = new CheatsAdapter(categories, numberOfCategories, cheats, numberOfCheats,
_materialColorScheme, _fontRepository, _folderIconVramOffset, _cheatListRecycler->InitVram(VramContext(nullptr, _objVramManager, nullptr, nullptr));
_checkboxUncheckedIconVramOffset, _checkboxCheckedIconVramOffset); _cheatListRecycler->Focus(*_focusManager);
_cheatListRecycler.SetAdapter(_cheatsAdapter);
_cheatListRecycler.InitVram(VramContext(nullptr, _objVramManager, nullptr, nullptr));
_cheatListRecycler.Focus(*_focusManager);
LOG_DEBUG("Setting adapter done\n");
} }
} }
BottomSheetView::Update(); BottomSheetView::Update();
_viewModel->SetSelectedItem(_cheatListRecycler->GetSelectedItem());
} }
void CheatsBottomSheetView::Draw(GraphicsContext& graphicsContext) void CheatsBottomSheetView::Draw(GraphicsContext& graphicsContext)
@@ -81,12 +89,36 @@ void CheatsBottomSheetView::Draw(GraphicsContext& graphicsContext)
graphicsContext.SetClipArea(GetBounds()); graphicsContext.SetClipArea(GetBounds());
u32 oldPrio = graphicsContext.SetPriority(1); u32 oldPrio = graphicsContext.SetPriority(1);
{ {
_titleLabel.SetBackgroundColor(_materialColorScheme->GetColor(md::sys::color::surfaceContainerLow)); graphicsContext.SetClipArea(_cheatListRecycler->GetBounds());
_cheatListRecycler->Draw(graphicsContext);
graphicsContext.SetClipArea(GetBounds());
auto backColor = _materialColorScheme->GetColor(md::sys::color::surfaceContainerLow);
u32 maskPaletteRow = graphicsContext.GetPaletteManager().AllocRow(
GradientPalette(backColor, backColor),
_position.y + LIST_Y - 24, _position.y + LIST_Y);
auto maskOam = graphicsContext.GetOamManager().AllocOams(4);
OamBuilder::OamWithSize<64, 32>(LIST_X, _position.y + LIST_Y - 24, _vramOffsets.cheatSelectorVramOffset >> 7)
.WithPalette16(maskPaletteRow)
.WithPriority(graphicsContext.GetPriority())
.Build(maskOam[0]);
OamBuilder::OamWithSize<64, 32>(LIST_X + 64, _position.y + LIST_Y - 24, _vramOffsets.cheatSelectorVramOffset >> 7)
.WithPalette16(maskPaletteRow)
.WithPriority(graphicsContext.GetPriority())
.Build(maskOam[1]);
OamBuilder::OamWithSize<64, 32>(LIST_X + 2 * 64, _position.y + LIST_Y - 24, _vramOffsets.cheatSelectorVramOffset >> 7)
.WithPalette16(maskPaletteRow)
.WithPriority(graphicsContext.GetPriority())
.Build(maskOam[2]);
OamBuilder::OamWithSize<64, 32>(LIST_X + 2 * 64 + 32, _position.y + LIST_Y - 24, _vramOffsets.cheatSelectorVramOffset >> 7)
.WithPalette16(maskPaletteRow)
.WithPriority(graphicsContext.GetPriority())
.Build(maskOam[3]);
_titleLabel.SetBackgroundColor(backColor);
_titleLabel.SetForegroundColor(_materialColorScheme->onSurface); _titleLabel.SetForegroundColor(_materialColorScheme->onSurface);
_titleLabel.Draw(graphicsContext); _titleLabel.Draw(graphicsContext);
graphicsContext.SetClipArea(_cheatListRecycler.GetBounds());
_cheatListRecycler.Draw(graphicsContext);
// BottomSheetView::Draw(graphicsContext);
} }
graphicsContext.SetPriority(oldPrio); graphicsContext.SetPriority(oldPrio);
graphicsContext.ResetClipArea(); graphicsContext.ResetClipArea();
@@ -94,10 +126,48 @@ void CheatsBottomSheetView::Draw(GraphicsContext& graphicsContext)
bool CheatsBottomSheetView::HandleInput(const InputProvider& inputProvider, FocusManager& focusManager) bool CheatsBottomSheetView::HandleInput(const InputProvider& inputProvider, FocusManager& focusManager)
{ {
if (inputProvider.Triggered(InputKey::B)) if (inputProvider.Triggered(InputKey::A))
{
if (focusManager.IsFocusInside(_cheatListRecycler.get()))
{
auto oldCategory = _viewModel->GetCurrentCheatCategory();
_viewModel->ItemActivated();
if (oldCategory != _viewModel->GetCurrentCheatCategory())
{
UpdateCheatList();
}
return true;
}
}
else if (inputProvider.Triggered(InputKey::B))
{
auto oldCategory = _viewModel->GetCurrentCheatCategory();
_viewModel->Back();
if (oldCategory != _viewModel->GetCurrentCheatCategory())
{
UpdateCheatList();
}
return true;
}
else if (inputProvider.Triggered(InputKey::Y))
{ {
_viewModel->Close(); _viewModel->Close();
return true; return true;
} }
return false; return false;
} }
void CheatsBottomSheetView::UpdateCheatList()
{
auto oldAdapter = _cheatsAdapter;
_cheatsAdapter = new CheatsAdapter(_viewModel->GetCurrentCheatCategory(), _materialColorScheme, _fontRepository, _vramOffsets);
_cheatListRecycler->SetAdapter(_cheatsAdapter);
delete oldAdapter;
// Ugly hack
((DescendingStackVramManager*)_objVramManager)->SetState(_savedVramState);
_cheatListRecycler->InitVram(VramContext(nullptr, _objVramManager, nullptr, nullptr));
_cheatListRecycler->Focus(*_focusManager);
}

View File

@@ -5,6 +5,7 @@
#include "gui/views/RecyclerView.h" #include "gui/views/RecyclerView.h"
#include "romBrowser/viewModels/CheatsViewModel.h" #include "romBrowser/viewModels/CheatsViewModel.h"
#include "CheatsAdapter.h" #include "CheatsAdapter.h"
#include "CheatListItemView.h"
class MaterialColorScheme; class MaterialColorScheme;
class IFontRepository; class IFontRepository;
@@ -17,6 +18,15 @@ public:
const MaterialColorScheme* materialColorScheme, const IFontRepository* fontRepository, const MaterialColorScheme* materialColorScheme, const IFontRepository* fontRepository,
FocusManager* focusManager); FocusManager* focusManager);
~CheatsBottomSheetView() override
{
_cheatListRecycler.reset();
if (_cheatsAdapter != nullptr)
{
delete _cheatsAdapter;
}
}
void InitVram(const VramContext& vramContext) override; void InitVram(const VramContext& vramContext) override;
void Update() override; void Update() override;
void Draw(GraphicsContext& graphicsContext) override; void Draw(GraphicsContext& graphicsContext) override;
@@ -24,19 +34,20 @@ public:
void Focus(FocusManager& focusManager) override void Focus(FocusManager& focusManager) override
{ {
_cheatListRecycler.Focus(focusManager); _cheatListRecycler->Focus(focusManager);
} }
private: private:
std::unique_ptr<CheatsViewModel> _viewModel; std::unique_ptr<CheatsViewModel> _viewModel;
Label2DView _titleLabel; Label2DView _titleLabel;
RecyclerView _cheatListRecycler; std::unique_ptr<RecyclerView> _cheatListRecycler;
CheatsAdapter* _cheatsAdapter = nullptr; CheatsAdapter* _cheatsAdapter = nullptr;
const MaterialColorScheme* _materialColorScheme; const MaterialColorScheme* _materialColorScheme;
const IFontRepository* _fontRepository; const IFontRepository* _fontRepository;
IVramManager* _objVramManager; IVramManager* _objVramManager;
FocusManager* _focusManager; FocusManager* _focusManager;
u32 _folderIconVramOffset = 0; CheatListItemView::VramOffsets _vramOffsets;
u32 _checkboxUncheckedIconVramOffset = 0; u32 _savedVramState = 0;
u32 _checkboxCheckedIconVramOffset = 0;
void UpdateCheatList();
}; };

View File

@@ -1,7 +1,7 @@
#pragma once #pragma once
/// @brief The Pico Loader API version supported by this header file. /// @brief The Pico Loader API version supported by this header file.
#define PICO_LOADER_API_VERSION 2 #define PICO_LOADER_API_VERSION 3
/// @brief Enum to specify the drive to boot from. /// @brief Enum to specify the drive to boot from.
typedef enum typedef enum
@@ -43,6 +43,46 @@ typedef struct
char launcherPath[256]; char launcherPath[256];
} pload_header7_v2_t; } pload_header7_v2_t;
/// @brief Struct representing a single Action Replay cheat opcode.
typedef struct
{
/// @brief The first part of the opcode.
u32 a;
/// @brief The second part of the opcode.
u32 b;
} pload_cheat_opcode_t;
/// @brief Struct representing a single Action Replay cheat.
typedef struct
{
/// @brief Length of \see opcodes in bytes.
u32 length;
/// @brief The cheat opcodes.
pload_cheat_opcode_t opcodes[1];
} pload_cheat_t;
/// @brief Struct representing one or more cheats. Cheats are adjacent starting from the firstCheat field.
typedef struct
{
/// @brief Length of this stucture (length field + numberOfCheats field + all cheats).
u32 length;
/// @brief The number of cheats.
u32 numberOfCheats;
/// @brief The first cheat.
pload_cheat_t firstCheat;
} pload_cheats_t;
/// @brief Struct representing the API version 3 part of the header of picoLoader7.bin.
typedef struct
{
/// @brief Pointer to the cheats, or \c nullptr when there are no cheats.
const pload_cheats_t* cheats;
} pload_header7_v3_t;
/// @brief Struct representing the header of picoLoader7.bin. /// @brief Struct representing the header of picoLoader7.bin.
typedef struct typedef struct
{ {
@@ -63,4 +103,7 @@ typedef struct
/// @brief The API version 2 part of the header. Only access this when \see apiVersion >= 2. /// @brief The API version 2 part of the header. Only access this when \see apiVersion >= 2.
pload_header7_v2_t v2; pload_header7_v2_t v2;
/// @brief The API version 3 part of the header. Only access this when \see apiVersion >= 3.
pload_header7_v3_t v3;
} pload_header7_t; } pload_header7_t;