diff --git a/arm9/gfx/cheatSelector.grit b/arm9/gfx/cheatSelector.grit new file mode 100644 index 0000000..9392dda --- /dev/null +++ b/arm9/gfx/cheatSelector.grit @@ -0,0 +1,7 @@ +# tile format +-gt + +# graphics bit depth is 4 (16 color) +-gB4 + +-p! \ No newline at end of file diff --git a/arm9/gfx/cheatSelector.png b/arm9/gfx/cheatSelector.png new file mode 100644 index 0000000..a311771 Binary files /dev/null and b/arm9/gfx/cheatSelector.png differ diff --git a/arm9/source/cheats/Cheat.h b/arm9/source/cheats/Cheat.h index a77b54e..e4b7ead 100644 --- a/arm9/source/cheats/Cheat.h +++ b/arm9/source/cheats/Cheat.h @@ -7,22 +7,34 @@ public: Cheat() : _cheatData(nullptr), _cheatDataLength(0) { } - Cheat(const char* name, const char* description, bool isCheatActive, const void* cheatData, u32 cheatDataLength) - : CheatTreeItem(name, description), _isCheatActive(isCheatActive) + Cheat(const char* name, const char* description, u32* flagsPointer, const void* cheatData, u32 cheatDataLength) + : CheatTreeItem(name, description), _flagsPointer(flagsPointer) , _cheatData(cheatData), _cheatDataLength(cheatDataLength) { } + const void* GetCheatData(u32& cheatDataLength) const + { + cheatDataLength = _cheatDataLength; + return _cheatData; + } + 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: - bool _isCheatActive; + u32* _flagsPointer; const void* _cheatData; u32 _cheatDataLength; }; diff --git a/arm9/source/cheats/CheatCategory.h b/arm9/source/cheats/CheatCategory.h index 733a6ea..470b325 100644 --- a/arm9/source/cheats/CheatCategory.h +++ b/arm9/source/cheats/CheatCategory.h @@ -2,8 +2,9 @@ #include #include "CheatTreeItem.h" #include "Cheat.h" +#include "ICheatCategory.h" -class CheatCategory : public CheatTreeItem +class CheatCategory : public CheatTreeItem, public ICheatCategory { public: CheatCategory() @@ -55,18 +56,18 @@ public: } } - bool GetIsMaxOneCheatActive() const + bool GetIsMaxOneCheatActive() const override { return _isMaxOneCheatActive; } - const CheatCategory* GetSubCategories(u32& numberOfSubCategories) const + const CheatCategory* GetCategories(u32& numberOfCategories) const override { - numberOfSubCategories = _numberOfSubCategories; + numberOfCategories = _numberOfSubCategories; return _subCategories; } - const Cheat* GetCheats(u32& numberOfCheats) const + const Cheat* GetCheats(u32& numberOfCheats) const override { numberOfCheats = _numberOfCheats; return _cheats; diff --git a/arm9/source/cheats/GameCheats.h b/arm9/source/cheats/GameCheats.h index 4ff1f29..7fa52e2 100644 --- a/arm9/source/cheats/GameCheats.h +++ b/arm9/source/cheats/GameCheats.h @@ -1,13 +1,14 @@ #pragma once #include "Cheat.h" #include "CheatCategory.h" +#include "ICheatCategory.h" -class GameCheats +class GameCheats : public ICheatCategory { public: - GameCheats(std::unique_ptr cheatData, const char* gameName, + GameCheats(std::unique_ptr cheatData, u32 cheatDataLength, u32 fileOffset, const char* gameName, 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) , _cheats(cheats), _numberOfCheats(numberOfCheats) { } @@ -28,20 +29,38 @@ public: return _gameName; } - const CheatCategory* GetCheatCategories(u32& numberOfCategories) const + bool GetIsMaxOneCheatActive() const override + { + return false; + } + + const CheatCategory* GetCategories(u32& numberOfCategories) const override { numberOfCategories = _numberOfCategories; return _categories; } - const Cheat* GetCheats(u32& numberOfCheats) const + const Cheat* GetCheats(u32& numberOfCheats) const override { numberOfCheats = _numberOfCheats; return _cheats; } + u8* GetCheatData(u32& cheatDataLength) const + { + cheatDataLength = _cheatDataLength; + return _cheatData.get(); + } + + u32 GetFileOffset() const + { + return _fileOffset; + } + private: std::unique_ptr _cheatData; + u32 _cheatDataLength; + u32 _fileOffset; const char* _gameName; CheatCategory* _categories; u32 _numberOfCategories; diff --git a/arm9/source/cheats/ICheatCategory.h b/arm9/source/cheats/ICheatCategory.h new file mode 100644 index 0000000..d90e7d1 --- /dev/null +++ b/arm9/source/cheats/ICheatCategory.h @@ -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; +}; diff --git a/arm9/source/cheats/ICheatRepository.h b/arm9/source/cheats/ICheatRepository.h index cdfab67..49e3eee 100644 --- a/arm9/source/cheats/ICheatRepository.h +++ b/arm9/source/cheats/ICheatRepository.h @@ -1,12 +1,14 @@ #pragma once #include "GameCheats.h" +#include "fat/FastFileRef.h" class ICheatRepository { public: virtual ~ICheatRepository() { } - virtual std::unique_ptr GetCheatsForGame(u32 gameCode, u32 headerCrc32) const = 0; + virtual std::unique_ptr GetCheatsForGame(const FastFileRef& romFile) const = 0; + virtual void UpdateEnabledCheatsForGame(const std::unique_ptr& cheats) const = 0; protected: ICheatRepository() { } diff --git a/arm9/source/cheats/PicoLoaderCheatDataFactory.cpp b/arm9/source/cheats/PicoLoaderCheatDataFactory.cpp new file mode 100644 index 0000000..42fb5ae --- /dev/null +++ b/arm9/source/cheats/PicoLoaderCheatDataFactory.cpp @@ -0,0 +1,99 @@ +#include "common.h" +#include +#include "PicoLoaderCheatDataFactory.h" + +pload_cheats_t* PicoLoaderCheatDataFactory::CreateCheatData(const std::unique_ptr& 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; + } +} diff --git a/arm9/source/cheats/PicoLoaderCheatDataFactory.h b/arm9/source/cheats/PicoLoaderCheatDataFactory.h new file mode 100644 index 0000000..771d773 --- /dev/null +++ b/arm9/source/cheats/PicoLoaderCheatDataFactory.h @@ -0,0 +1,16 @@ +#pragma once +#include +#include "GameCheats.h" +#include "picoLoader7.h" + +class PicoLoaderCheatDataFactory +{ +public: + pload_cheats_t* CreateCheatData(const std::unique_ptr& 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; +}; diff --git a/arm9/source/cheats/UsrCheatRepository.cpp b/arm9/source/cheats/UsrCheatRepository.cpp index f430a2e..902c57b 100644 --- a/arm9/source/cheats/UsrCheatRepository.cpp +++ b/arm9/source/cheats/UsrCheatRepository.cpp @@ -3,6 +3,68 @@ #include #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 UsrCheatRepository::GetCheatsForGame(const FastFileRef& romFile) const +{ + auto file = std::make_unique(); + file->Open(romFile, FA_READ); + auto headerBuffer = std::make_unique_for_overwrite(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& 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 UsrCheatRepository::GetCheatsForGame(u32 gameCode, u32 headerCrc32) const { auto index = FindIndex(gameCode, headerCrc32); @@ -14,13 +76,14 @@ std::unique_ptr UsrCheatRepository::GetCheatsForGame(u32 gameCode, u return nullptr; } - auto cheatData = std::make_unique_for_overwrite(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(cheatDataLength); if (_usrCheatFile->Seek(index->offset) != FR_OK) { LOG_ERROR("Failed to seek to cheat data\n"); return nullptr; } - if (!_usrCheatFile->ReadExact(cheatData.get(), index->padding)) + if (!_usrCheatFile->ReadExact(cheatData.get(), cheatDataLength)) { LOG_ERROR("Failed to read cheat data\n"); return nullptr; @@ -38,7 +101,7 @@ std::unique_ptr UsrCheatRepository::GetCheatsForGame(u32 gameCode, u // flags u32 flags = *(u32*)ptr; u32 totalNumberOfItems = flags & 0x0FFFFFFF; - u32 gameActive = flags >> 28; + // u32 gameActive = flags >> 28; ptr += 4; // master codes @@ -50,7 +113,7 @@ std::unique_ptr UsrCheatRepository::GetCheatsForGame(u32 gameCode, u auto cheats = (Cheat*)malloc(totalNumberOfItems * sizeof(Cheat)); u32 cheatCount = 0; - while (ptr < cheatData.get() + index->padding) + while (ptr < cheatData.get() + cheatDataLength) { u32 itemFlags = *(u32*)ptr; bool isCategory = ((itemFlags >> 28) & 1) == 1; @@ -69,7 +132,8 @@ std::unique_ptr UsrCheatRepository::GetCheatsForGame(u32 gameCode, u categories = (CheatCategory*)realloc(categories, categoryCount * sizeof(CheatCategory)); cheats = (Cheat*)realloc(cheats, cheatCount * sizeof(Cheat)); - return std::make_unique(std::move(cheatData), gameName, categories, categoryCount, cheats, cheatCount); + return std::make_unique( + std::move(cheatData), cheatDataLength, index->offset, gameName, categories, categoryCount, cheats, cheatCount); } 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)); 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 { // flags - u32 itemFlags = *(u32*)ptr; + u32* flagsPtr = (u32*)ptr; ptr += 4; - bool isCheatActive = ((itemFlags >> 24) & 1) == 1; // item name const char* itemName = (const char*)ptr; @@ -167,7 +230,7 @@ void UsrCheatRepository::ParseCheat(Cheat& cheat, u8*& ptr) const u32 numberOfCodeWords = *(u32*)ptr; ptr += 4; - cheat = Cheat(itemName, itemDescription, isCheatActive, ptr, numberOfCodeWords * 4); + new (&cheat) Cheat(itemName, itemDescription, flagsPtr, ptr, numberOfCodeWords * 4); // code ptr += numberOfCodeWords * 4; diff --git a/arm9/source/cheats/UsrCheatRepository.h b/arm9/source/cheats/UsrCheatRepository.h index 4274b45..47ed51e 100644 --- a/arm9/source/cheats/UsrCheatRepository.h +++ b/arm9/source/cheats/UsrCheatRepository.h @@ -12,13 +12,15 @@ public: : _usrCheatFile(std::move(usrCheatDatFile)) , _sortedIndices(std::move(sortedIndices)), _numberOfIndices(numberOfIndices) { } - std::unique_ptr GetCheatsForGame(u32 gameCode, u32 headerCrc32) const override; + std::unique_ptr GetCheatsForGame(const FastFileRef& romFile) const override; + void UpdateEnabledCheatsForGame(const std::unique_ptr& cheats) const override; private: std::unique_ptr _usrCheatFile; std::unique_ptr _sortedIndices; u32 _numberOfIndices; + std::unique_ptr GetCheatsForGame(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 ParseCheat(Cheat& cheat, u8*& ptr) const; diff --git a/arm9/source/core/task/Task.h b/arm9/source/core/task/Task.h index 645130a..8268c15 100644 --- a/arm9/source/core/task/Task.h +++ b/arm9/source/core/task/Task.h @@ -65,7 +65,7 @@ template <> class Task : public TaskBase { protected: - virtual TaskResult ExecuteFunc() const = 0; + virtual TaskResult ExecuteFunc() = 0; private: TaskState ExecuteDirect() override @@ -79,11 +79,11 @@ template class FuncTask : public Task { public: - FuncTask(const FuncType& function) - : _function(function) { } + FuncTask(FuncType&& function) + : _function(std::move(function)) { } private: - const FuncType _function; + FuncType _function; - TaskResult ExecuteFunc() const override { return _function((const volatile u8&)this->_cancelRequested); } + TaskResult ExecuteFunc() override { return _function((const volatile u8&)this->_cancelRequested); } }; diff --git a/arm9/source/core/task/TaskQueue.h b/arm9/source/core/task/TaskQueue.h index cef629a..2b07f2c 100644 --- a/arm9/source/core/task/TaskQueue.h +++ b/arm9/source/core/task/TaskQueue.h @@ -79,12 +79,12 @@ public: template [[gnu::noinline]] - auto Enqueue(const FuncType& function) -> QueueTask + auto Enqueue(FuncType&& function) -> QueueTask { using TaskType = FuncTask; // static_assert(sizeof(TaskType) <= MaxTaskSize, "Task is too big for this pool"); void* slot = GetSlot(); - auto task = new (slot) TaskType(function); + auto task = new (slot) TaskType(std::move(function)); Enqueue(task); return QueueTask(task, this); } diff --git a/arm9/source/gui/AdvancedPaletteManager.h b/arm9/source/gui/AdvancedPaletteManager.h index fd3b628..7605c87 100644 --- a/arm9/source/gui/AdvancedPaletteManager.h +++ b/arm9/source/gui/AdvancedPaletteManager.h @@ -84,6 +84,7 @@ public: u32 AllocRow(const IPalette& palette, int yStart, int yEnd) override { + yEnd++; // avoid back to back allocation return AllocRowInternal(_rows[_curSet], palette, yStart, yEnd); } diff --git a/arm9/source/gui/views/RecyclerView.cpp b/arm9/source/gui/views/RecyclerView.cpp index 3b306ae..f849134 100644 --- a/arm9/source/gui/views/RecyclerView.cpp +++ b/arm9/source/gui/views/RecyclerView.cpp @@ -39,6 +39,10 @@ void RecyclerView::SetAdapter(const RecyclerAdapter* adapter, int initialSelecte _viewPool.reset(); _viewPoolFreeCount = 0; _viewPoolTotalCount = 0; + _xOffset = 0; + _yOffset = 0; + _curRangeStart = 0; + _curRangeLength = 0; } _adapter = adapter; _adapter->GetViewSize(_itemWidth, _itemHeight); diff --git a/arm9/source/picoLoaderBootstrap.cpp b/arm9/source/picoLoaderBootstrap.cpp index 4bcaffe..6c33548 100644 --- a/arm9/source/picoLoaderBootstrap.cpp +++ b/arm9/source/picoLoaderBootstrap.cpp @@ -21,6 +21,7 @@ typedef void (*pico_loader_9_func_t)(void); static pload_params_t sLoadParams; static char sLauncherPath[256] alignas(32); static PicoLoaderBootDrive sBootDrive; +static const pload_cheats_t* sCheatData = nullptr; pload_params_t* pload_getLoadParams() { @@ -37,6 +38,11 @@ void pload_setLauncherPath(const char* launcherPath) StringUtil::Copy(sLauncherPath, launcherPath, sizeof(sLauncherPath)); } +void pload_setCheatData(const pload_cheats_t* cheatData) +{ + sCheatData = cheatData; +} + void pload_start() { mem_setVramAMapping(MEM_VRAM_AB_LCDC); @@ -92,6 +98,10 @@ void pload_start() { 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_setVramDMapping(MEM_VRAM_D_ARM7_20000); ipc_sendFifoMessage(IPC_CHANNEL_LOADER, 1); diff --git a/arm9/source/picoLoaderBootstrap.h b/arm9/source/picoLoaderBootstrap.h index 75b6d88..0249d6d 100644 --- a/arm9/source/picoLoaderBootstrap.h +++ b/arm9/source/picoLoaderBootstrap.h @@ -4,4 +4,5 @@ pload_params_t* pload_getLoadParams(); void pload_setBootDrive(PicoLoaderBootDrive bootDrive); void pload_setLauncherPath(const char* launcherPath); +void pload_setCheatData(const pload_cheats_t* cheatData); void pload_start(); diff --git a/arm9/source/romBrowser/RomBrowserController.cpp b/arm9/source/romBrowser/RomBrowserController.cpp index e4dd551..5fe1433 100644 --- a/arm9/source/romBrowser/RomBrowserController.cpp +++ b/arm9/source/romBrowser/RomBrowserController.cpp @@ -7,6 +7,7 @@ #include "SdFolderFactory.h" #include "services/settings/IAppSettingsService.h" #include "cheats/UsrCheatRepositoryFactory.h" +#include "cheats/PicoLoaderCheatDataFactory.h" #include "RomBrowserController.h" RomBrowserController::RomBrowserController( @@ -183,26 +184,9 @@ void RomBrowserController::HandleLaunchTrigger() LOG_DEBUG("RomBrowserStateTrigger::Launch\n"); _ioTaskQueue->Enqueue([this] (const vu8& cancelRequested) { - f_getcwd(_navigatePath, sizeof(_navigatePath) / sizeof(_navigatePath[0])); - int idx = strlcat(_navigatePath, "/", sizeof(_navigatePath)); - if (_navigatePath[idx - 2] == '/') - _navigatePath[idx - 1] = 0; - strlcat(_navigatePath, _triggerFileInfo.GetFileName(), sizeof(_navigatePath)); - _appSettingsService->GetAppSettings().lastUsedFilePath = _navigatePath; - _appSettingsService->Save(); - - auto loadParams = pload_getLoadParams(); - loadParams->savePath[0] = 0; - loadParams->arguments[0] = 0; - loadParams->argumentsLength = 0; - if (_triggerFileInfo.GetFileType()->TrySetLaunchParameters(loadParams, _navigatePath)) - { - gProcessManager.Goto(); - } - else - { - LOG_FATAL("Failed to set launch parameters.\n"); - } + UpdateLastUsedFilepath(); + SetPicoLoaderParams(); + LoadCheats(); return TaskResult::Completed(); }); } @@ -212,3 +196,39 @@ void RomBrowserController::HandleChangeDisplayModeTrigger() LOG_DEBUG("RomBrowserStateTrigger::ChangeDisplayMode\n"); _romBrowserViewModel = SharedPtr(new RomBrowserViewModel(this)); } + +void RomBrowserController::UpdateLastUsedFilepath() +{ + f_getcwd(_navigatePath, sizeof(_navigatePath) / sizeof(_navigatePath[0])); + int idx = strlcat(_navigatePath, "/", sizeof(_navigatePath)); + if (_navigatePath[idx - 2] == '/') + { + _navigatePath[idx - 1] = 0; + } + strlcat(_navigatePath, _triggerFileInfo.GetFileName(), sizeof(_navigatePath)); + _appSettingsService->GetAppSettings().lastUsedFilePath = _navigatePath; + _appSettingsService->Save(); +} + +void RomBrowserController::SetPicoLoaderParams() const +{ + auto loadParams = pload_getLoadParams(); + loadParams->savePath[0] = 0; + loadParams->arguments[0] = 0; + loadParams->argumentsLength = 0; + if (_triggerFileInfo.GetFileType()->TrySetLaunchParameters(loadParams, _navigatePath)) + { + gProcessManager.Goto(); + } + else + { + LOG_FATAL("Failed to set launch parameters.\n"); + } +} + +void RomBrowserController::LoadCheats() const +{ + auto cheats = _cheatRepository->GetCheatsForGame(_triggerFileInfo.GetFastFileRef()); + auto cheatData = PicoLoaderCheatDataFactory().CreateCheatData(cheats); + pload_setCheatData(cheatData); +} diff --git a/arm9/source/romBrowser/RomBrowserController.h b/arm9/source/romBrowser/RomBrowserController.h index f24e48c..3b2ba4f 100644 --- a/arm9/source/romBrowser/RomBrowserController.h +++ b/arm9/source/romBrowser/RomBrowserController.h @@ -74,4 +74,7 @@ private: void HandleFolderLoadDoneTrigger(); void HandleLaunchTrigger(); void HandleChangeDisplayModeTrigger(); + void UpdateLastUsedFilepath(); + void SetPicoLoaderParams() const; + void LoadCheats() const; }; diff --git a/arm9/source/romBrowser/viewModels/CheatsViewModel.cpp b/arm9/source/romBrowser/viewModels/CheatsViewModel.cpp index 797e047..bc6c3d0 100644 --- a/arm9/source/romBrowser/viewModels/CheatsViewModel.cpp +++ b/arm9/source/romBrowser/viewModels/CheatsViewModel.cpp @@ -3,57 +3,16 @@ #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) { + _categoryStack.fill(nullptr); _loadCheatsTask = _romBrowserController->GetIoTaskQueue()->Enqueue([this] (const vu8& cancelRequested) { - LOG_DEBUG("%s\n", _romFileInfo.GetFileName()); - auto file = std::make_unique(); - file->Open(_romFileInfo.GetFastFileRef(), FA_READ); - auto headerBuffer = std::make_unique_for_overwrite(512); - if (!file->ReadExact(headerBuffer.get(), 512)) - { - LOG_ERROR("Could not read rom header\n"); - return TaskResult::Failed(); - } - file->Close(); - - if (cancelRequested) - { - return TaskResult::Canceled(); - } - - u32 gameCode = *(u32*)(headerBuffer.get() + 0xC); - u32 crc = crc32(headerBuffer.get(), 512); - headerBuffer.reset(); - - if (cancelRequested) - { - return TaskResult::Canceled(); - } - - _cheats = _romBrowserController->GetCheatRepository().GetCheatsForGame(gameCode, crc); + _cheats = _romBrowserController->GetCheatRepository().GetCheatsForGame(_romFileInfo.GetFastFileRef()); if (_cheats) { + _categoryStack[0] = _cheats.get(); _state = State::DisplayCheats; } else @@ -64,3 +23,64 @@ CheatsViewModel::CheatsViewModel(const FileInfo& romFileInfo, IRomBrowserControl return TaskResult::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::Completed(); + }); + } + + _romBrowserController->HideGameInfo(); +} diff --git a/arm9/source/romBrowser/viewModels/CheatsViewModel.h b/arm9/source/romBrowser/viewModels/CheatsViewModel.h index 51ec564..d507334 100644 --- a/arm9/source/romBrowser/viewModels/CheatsViewModel.h +++ b/arm9/source/romBrowser/viewModels/CheatsViewModel.h @@ -1,4 +1,5 @@ #pragma once +#include #include #include "core/task/TaskQueue.h" #include "cheats/GameCheats.h" @@ -18,13 +19,15 @@ public: CheatsViewModel(const FileInfo& romFileInfo, IRomBrowserController* romBrowserController); - void Close() - { - _romBrowserController->HideGameInfo(); - } + void ItemActivated(); + void Back(); + void Close(); 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: FileInfo _romFileInfo; @@ -32,4 +35,8 @@ private: QueueTask _loadCheatsTask; std::unique_ptr _cheats; State _state = State::Loading; + int _selectedItem = -1; + bool _changed = false; + u32 _categoryStackLevel = 0; + std::array _categoryStack; }; diff --git a/arm9/source/romBrowser/views/cheats/CheatListItemView.cpp b/arm9/source/romBrowser/views/cheats/CheatListItemView.cpp index 80f550f..ff01dda 100644 --- a/arm9/source/romBrowser/views/cheats/CheatListItemView.cpp +++ b/arm9/source/romBrowser/views/cheats/CheatListItemView.cpp @@ -6,14 +6,16 @@ #include "gui/OamBuilder.h" #include "CheatListItemView.h" -#define ICON_X 20 -#define ICON_Y 3 +#define ICON_X 4 +#define ICON_Y 4 -#define NAME_LABEL_X 40 -#define NAME_LABEL_Y 4 +#define NAME_LABEL_X 24 +#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)) + , _vramOffsets(vramOffsets) , _materialColorScheme(materialColorScheme) { _nameLabel.SetEllipsis(true); @@ -22,15 +24,57 @@ CheatListItemView::CheatListItemView(const MaterialColorScheme* materialColorSch 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(); } void CheatListItemView::Draw(GraphicsContext& graphicsContext) { + if (!graphicsContext.IsVisible(GetBounds())) + { + return; + } + 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.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); if (graphicsContext.IsVisible(Rectangle(_position.x + ICON_X, _position.y + ICON_Y, 16, 16))) diff --git a/arm9/source/romBrowser/views/cheats/CheatListItemView.h b/arm9/source/romBrowser/views/cheats/CheatListItemView.h index 6168d21..8776715 100644 --- a/arm9/source/romBrowser/views/cheats/CheatListItemView.h +++ b/arm9/source/romBrowser/views/cheats/CheatListItemView.h @@ -1,6 +1,8 @@ #pragma once #include "gui/views/ViewContainer.h" #include "gui/views/Label2DView.h" +#include "cheats/CheatCategory.h" +#include "cheats/Cheat.h" class MaterialColorScheme; class IFontRepository; @@ -8,14 +10,22 @@ class IFontRepository; class CheatListItemView : public ViewContainer { 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 Draw(GraphicsContext& graphicsContext) 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) @@ -23,13 +33,23 @@ public: _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: Label2DView _nameLabel; - u32 _iconVramOffset = 0; + VramOffsets _vramOffsets; const MaterialColorScheme* _materialColorScheme; + u32 _iconVramOffset = 0; + const Cheat* _cheat = nullptr; }; diff --git a/arm9/source/romBrowser/views/cheats/CheatsAdapter.h b/arm9/source/romBrowser/views/cheats/CheatsAdapter.h index 80ac062..8938f49 100644 --- a/arm9/source/romBrowser/views/cheats/CheatsAdapter.h +++ b/arm9/source/romBrowser/views/cheats/CheatsAdapter.h @@ -7,30 +7,30 @@ 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) { } + CheatsAdapter(const ICheatCategory* cheatCategory, const MaterialColorScheme* materialColorScheme, + const IFontRepository* fontRepository, const CheatListItemView::VramOffsets& vramOffsets) + : _cheatCategory(cheatCategory), _materialColorScheme(materialColorScheme) + , _fontRepository(fontRepository), _vramOffsets(vramOffsets) { } 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 { - width = 256; + width = 224; height = 24; } View* CreateView() const override { LOG_DEBUG("CheatsAdapter::CreateView\n"); - return new CheatListItemView(_materialColorScheme, _fontRepository); + return new CheatListItemView(_vramOffsets, _materialColorScheme, _fontRepository); } void DestroyView(View* view) const override @@ -43,16 +43,18 @@ public: { LOG_DEBUG("CheatsAdapter::BindView\n"); auto listItemView = static_cast(view); - if ((u32)index < _numberOfCategories) + u32 numberOfCategories = 0; + auto categories = _cheatCategory->GetCategories(numberOfCategories); + if ((u32)index < numberOfCategories) { - listItemView->SetName(_categories[index].GetName()); - listItemView->SetIcon(_folderIconVramOffset); + listItemView->SetCategory(&categories[index]); } else { - index -= _numberOfCategories; - listItemView->SetName(_cheats[index].GetName()); - listItemView->SetIcon(_cheats[index].GetIsCheatActive() ? _checkboxCheckedIconVramOffset : _checkboxUncheckedIconVramOffset); + index -= numberOfCategories; + u32 numberOfCheats = 0; + auto cheats = _cheatCategory->GetCheats(numberOfCheats); + listItemView->SetCheat(&cheats[index]); } } @@ -62,13 +64,8 @@ public: } private: - const CheatCategory* _categories; - u32 _numberOfCategories; - const Cheat* _cheats; - u32 _numberOfCheats; + const ICheatCategory* _cheatCategory; const MaterialColorScheme* _materialColorScheme; const IFontRepository* _fontRepository; - u32 _folderIconVramOffset; - u32 _checkboxUncheckedIconVramOffset; - u32 _checkboxCheckedIconVramOffset; + CheatListItemView::VramOffsets _vramOffsets; }; diff --git a/arm9/source/romBrowser/views/cheats/CheatsBottomSheetView.cpp b/arm9/source/romBrowser/views/cheats/CheatsBottomSheetView.cpp index 4b4fb2d..aabae25 100644 --- a/arm9/source/romBrowser/views/cheats/CheatsBottomSheetView.cpp +++ b/arm9/source/romBrowser/views/cheats/CheatsBottomSheetView.cpp @@ -4,27 +4,34 @@ #include "themes/IFontRepository.h" #include "gui/input/InputProvider.h" #include "gui/VramContext.h" +#include "gui/palette/GradientPalette.h" +#include "gui/OamBuilder.h" #include "folderIcon.h" #include "checkboxChecked.h" #include "checkboxUnchecked.h" +#include "cheatSelector.h" +#include "gui/DescendingStackVramManager.h" #include "CheatsBottomSheetView.h" #define TITLE_LABEL_X 20 #define TITLE_LABEL_Y 16 +#define LIST_X 16 +#define LIST_Y 36 + CheatsBottomSheetView::CheatsBottomSheetView(std::unique_ptr 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) + , _cheatListRecycler(std::make_unique(LIST_X, LIST_Y, 224, 124, RecyclerView::Mode::VerticalList)) , _materialColorScheme(materialColorScheme) , _fontRepository(fontRepository) , _focusManager(focusManager) { _titleLabel.SetText(u"Cheats"); AddChildTail(&_titleLabel); - AddChildTail(&_cheatListRecycler); + AddChildTail(_cheatListRecycler.get()); } void CheatsBottomSheetView::InitVram(const VramContext& vramContext) @@ -34,17 +41,21 @@ void CheatsBottomSheetView::InitVram(const VramContext& vramContext) const auto objVramManager = vramContext.GetObjVramManager(); if (objVramManager) { - _folderIconVramOffset = objVramManager->Alloc(folderIconTilesLen); + _vramOffsets.folderIconVramOffset = objVramManager->Alloc(folderIconTilesLen); 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, - objVramManager->GetVramAddress(_checkboxUncheckedIconVramOffset), checkboxUncheckedTilesLen); + objVramManager->GetVramAddress(_vramOffsets.checkboxUncheckedIconVramOffset), checkboxUncheckedTilesLen); - _checkboxCheckedIconVramOffset = objVramManager->Alloc(checkboxCheckedTilesLen); + _vramOffsets.checkboxCheckedIconVramOffset = objVramManager->Alloc(checkboxCheckedTilesLen); 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(); @@ -53,27 +64,24 @@ void CheatsBottomSheetView::InitVram(const VramContext& vramContext) void CheatsBottomSheetView::Update() { _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 (_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"); + _cheatsAdapter = new CheatsAdapter( + _viewModel->GetCurrentCheatCategory(), _materialColorScheme, _fontRepository, _vramOffsets); + _cheatListRecycler->SetAdapter(_cheatsAdapter); + + // Ugly hack + _savedVramState = ((DescendingStackVramManager*)_objVramManager)->GetState(); + + _cheatListRecycler->InitVram(VramContext(nullptr, _objVramManager, nullptr, nullptr)); + _cheatListRecycler->Focus(*_focusManager); } } BottomSheetView::Update(); + _viewModel->SetSelectedItem(_cheatListRecycler->GetSelectedItem()); } void CheatsBottomSheetView::Draw(GraphicsContext& graphicsContext) @@ -81,12 +89,36 @@ void CheatsBottomSheetView::Draw(GraphicsContext& graphicsContext) graphicsContext.SetClipArea(GetBounds()); 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.Draw(graphicsContext); - graphicsContext.SetClipArea(_cheatListRecycler.GetBounds()); - _cheatListRecycler.Draw(graphicsContext); - // BottomSheetView::Draw(graphicsContext); } graphicsContext.SetPriority(oldPrio); graphicsContext.ResetClipArea(); @@ -94,10 +126,48 @@ void CheatsBottomSheetView::Draw(GraphicsContext& graphicsContext) 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(); return true; } 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); +} diff --git a/arm9/source/romBrowser/views/cheats/CheatsBottomSheetView.h b/arm9/source/romBrowser/views/cheats/CheatsBottomSheetView.h index 7d51394..d86f25a 100644 --- a/arm9/source/romBrowser/views/cheats/CheatsBottomSheetView.h +++ b/arm9/source/romBrowser/views/cheats/CheatsBottomSheetView.h @@ -5,6 +5,7 @@ #include "gui/views/RecyclerView.h" #include "romBrowser/viewModels/CheatsViewModel.h" #include "CheatsAdapter.h" +#include "CheatListItemView.h" class MaterialColorScheme; class IFontRepository; @@ -17,6 +18,15 @@ public: const MaterialColorScheme* materialColorScheme, const IFontRepository* fontRepository, FocusManager* focusManager); + ~CheatsBottomSheetView() override + { + _cheatListRecycler.reset(); + if (_cheatsAdapter != nullptr) + { + delete _cheatsAdapter; + } + } + void InitVram(const VramContext& vramContext) override; void Update() override; void Draw(GraphicsContext& graphicsContext) override; @@ -24,19 +34,20 @@ public: void Focus(FocusManager& focusManager) override { - _cheatListRecycler.Focus(focusManager); + _cheatListRecycler->Focus(focusManager); } private: std::unique_ptr _viewModel; Label2DView _titleLabel; - RecyclerView _cheatListRecycler; + std::unique_ptr _cheatListRecycler; CheatsAdapter* _cheatsAdapter = nullptr; const MaterialColorScheme* _materialColorScheme; const IFontRepository* _fontRepository; IVramManager* _objVramManager; FocusManager* _focusManager; - u32 _folderIconVramOffset = 0; - u32 _checkboxUncheckedIconVramOffset = 0; - u32 _checkboxCheckedIconVramOffset = 0; + CheatListItemView::VramOffsets _vramOffsets; + u32 _savedVramState = 0; + + void UpdateCheatList(); }; diff --git a/common/picoLoader7.h b/common/picoLoader7.h index 859e3fc..ee2f6e4 100644 --- a/common/picoLoader7.h +++ b/common/picoLoader7.h @@ -1,7 +1,7 @@ #pragma once /// @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. typedef enum @@ -43,6 +43,46 @@ typedef struct char launcherPath[256]; } 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. typedef struct { @@ -63,4 +103,7 @@ typedef struct /// @brief The API version 2 part of the header. Only access this when \see apiVersion >= 2. 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;