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

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

View File

@@ -2,8 +2,9 @@
#include <memory>
#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;

View File

@@ -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<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)
: _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<u8[]> _cheatData;
u32 _cheatDataLength;
u32 _fileOffset;
const char* _gameName;
CheatCategory* _categories;
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
#include "GameCheats.h"
#include "fat/FastFileRef.h"
class ICheatRepository
{
public:
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:
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 "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
{
auto index = FindIndex(gameCode, headerCrc32);
@@ -14,13 +76,14 @@ std::unique_ptr<GameCheats> UsrCheatRepository::GetCheatsForGame(u32 gameCode, u
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)
{
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<GameCheats> 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<GameCheats> 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<GameCheats> UsrCheatRepository::GetCheatsForGame(u32 gameCode, u
categories = (CheatCategory*)realloc(categories, categoryCount * sizeof(CheatCategory));
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
@@ -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;

View File

@@ -12,13 +12,15 @@ public:
: _usrCheatFile(std::move(usrCheatDatFile))
, _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:
std::unique_ptr<File> _usrCheatFile;
std::unique_ptr<usr_cheat_index_entry_t[]> _sortedIndices;
u32 _numberOfIndices;
std::unique_ptr<GameCheats> 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;