mirror of
https://github.com/LNH-team/pico-launcher.git
synced 2026-06-02 09:06:54 +02:00
Further work on support for cheats
Cheats can now be enabled/disabled and games can be launched with cheats
This commit is contained in:
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
17
arm9/source/cheats/ICheatCategory.h
Normal file
17
arm9/source/cheats/ICheatCategory.h
Normal 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;
|
||||
};
|
||||
@@ -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() { }
|
||||
|
||||
99
arm9/source/cheats/PicoLoaderCheatDataFactory.cpp
Normal file
99
arm9/source/cheats/PicoLoaderCheatDataFactory.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
16
arm9/source/cheats/PicoLoaderCheatDataFactory.h
Normal file
16
arm9/source/cheats/PicoLoaderCheatDataFactory.h
Normal 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;
|
||||
};
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user