Cheat System: Rearchitect the system for loading cheat database files in preparation for adding new features to the database file presentation. Also do a bunch of code cleanup and fix multiple bugs.

- The file description is no longer limited to 16 characters.
- Folder notes are now included in the description strings of exported cheat items. These can be important for cheats that include operating instructions in their associated folder notes.
- Fix a bug where reading the last game entry of the database file would fail.
- Fix a potential bug where reading a game entry from an encrypted database file would fail if the initial entry data resides very close to a 512-byte boundary.
- Fix a bug where deleting a CHEATSEXPORT object without calling CHEATSEXPORT.close() would result in its associated file remaining open.
- Fix a bug where deleting a CHEATSEXPORT object without calling CHEATSEXPORT.close() would result in CHEATSEXPORT.cheats leaking memory.
This commit is contained in:
rogerman 2023-07-18 11:13:42 -07:00
parent 50f02ae172
commit 0c90e8f4e3
6 changed files with 1027 additions and 307 deletions

File diff suppressed because it is too large Load Diff

View File

@ -24,10 +24,8 @@
#define CHEAT_VERSION_MAJOR 2
#define CHEAT_VERSION_MINOR 0
#define MAX_CHEAT_LIST 100
#define MAX_XX_CODE 1024
#define CHEAT_FILE_MIN_FGETS_BUFFER 32768
#define CHEAT_DB_GAME_TITLE_SIZE 256
#define CHEAT_TYPE_EMPTY 0xFF
#define CHEAT_TYPE_INTERNAL 0
@ -39,7 +37,7 @@ struct CHEATS_LIST
CHEATS_LIST()
{
memset(this,0,sizeof(*this));
type = 0xFF;
type = CHEAT_TYPE_EMPTY;
}
u8 type;
u8 enabled;
@ -48,7 +46,18 @@ struct CHEATS_LIST
// 1 - can decrease
// 2 - can increase
u32 code[MAX_XX_CODE][2];
char description[1024];
union
{
char description[1024];
struct
{
char descriptionMajor[512];
char descriptionMinor[512];
};
};
u32 num;
u8 size;
};
@ -151,71 +160,153 @@ public:
void getListReset();
};
#define CHEATDB_OFFSET_FILE_DESCRIPTION 0x00000010
#define CHEATDB_FILEOFFSET_FIRST_FAT_ENTRY 0x00000100
enum CHEATS_DB_TYPE
{
CHEATS_DB_R4 = 0
};
// This struct maps to the FAT entries in the R4 cheat database file.
#pragma pack(push)
#pragma pack(1)
typedef struct FAT_R4
{
u8 serial[4];
u32 CRC;
u8 serial[4];
u32 CRC;
u64 addr;
} FAT_R4;
#pragma pack(pop)
// Wrapper for a single entry in a memory block that contains data read from a cheat database file.
// This struct also maintains the hierarchical relationships between entries.
struct CheatDBEntry
{
u8 *base; // Pointer to the entry's base location in memory.
char *name; // Pointer to the entry's name string.
char *note; // Pointer to the entry's note string.
u32 *codeLength; // Pointer to the entry's 32-bit code length in bytes. This value is NULL if the entry is a directory.
u32 *codeData; // Pointer to the entry's code data, provided in pairs of 32-bit values. This value is NULL if the entry is a directory.
CheatDBEntry *parent;
std::vector<CheatDBEntry> child;
};
typedef struct CheatDBEntry CheatDBEntry;
class CheatDBGame
{
protected:
u32 _baseOffset; // This offset is relative to the file head.
u32 _firstEntryOffset; // This offset is relative to the file head.
u32 _encryptOffset; // This offset is relative to the memory address of this->_entryDataRawPtr.
u32 _dataSize;
u32 _crc;
u32 _entryCount;
std::string _title;
char _serial[4 + 1];
u8 *_entryDataRawPtr;
u8 *_entryData;
CheatDBEntry _entryRoot;
u32 _cheatItemCount;
void _UpdateDirectoryParents(CheatDBEntry &directory);
bool _CreateCheatItemFromCheatEntry(const CheatDBEntry &inEntry, const bool isHierarchical, CHEATS_LIST &outCheatItem);
size_t _DirectoryAddCheatsFlat(const CheatDBEntry &directory, const bool isHierarchical, size_t cheatIndex, CHEATS_LIST *outCheatsList);
public:
CheatDBGame();
CheatDBGame(const u32 encryptOffset, const FAT_R4 &fat, const u32 dataSize);
CheatDBGame(FILE *fp, const bool isEncrypted, const u32 encryptOffset, const FAT_R4 &fat, const u32 dataSize, u8 (&workingBuffer)[1024]);
~CheatDBGame();
void SetInitialProperties(const u32 dataSize, const u32 encryptOffset, const FAT_R4 &fat);
void LoadPropertiesFromFile(FILE *fp, const bool isEncrypted, u8 (&workingBuffer)[1024]);
u32 GetBaseOffset() const;
u32 GetFirstEntryOffset() const;
u32 GetEncryptOffset() const;
u32 GetDataSize() const;
u32 GetCRC() const;
u32 GetEntryCount() const;
u32 GetCheatItemCount() const;
const char* GetTitle() const;
const char* GetSerial() const;
const CheatDBEntry& GetEntryRoot() const;
const u8* GetEntryRawData() const;
bool IsEntryDataLoaded() const;
u8* LoadEntryData(FILE *fp, const bool isEncrypted);
size_t ParseEntriesToCheatsListFlat(CHEATS_LIST *outCheatsList);
};
typedef std::vector<CheatDBGame> CheatDBGameList;
class CheatDBFile
{
protected:
std::string _path;
std::string _description;
CHEATS_DB_TYPE _type;
bool _isEncrypted;
size_t _size;
FILE *_fp;
CheatDBGame _ReadGame(const u32 encryptOffset, const FAT_R4 &fat, const u32 gameDataSize, u8 (&workingBuffer)[1024]);
public:
CheatDBFile();
~CheatDBFile();
static void R4Decrypt(u8 *buf, const size_t len, u64 n);
static bool ReadToBuffer(FILE *fp, const size_t fileOffset, const bool isEncrypted, const size_t encryptOffset, const size_t requestedSize, u8 *outBuffer);
FILE* GetFilePtr() const;
bool IsEncrypted() const;
const char* GetDescription() const;
int OpenFile(const char *filePath);
void CloseFile();
u32 LoadGameList(const char *gameCode, const u32 gameDatabaseCRC, CheatDBGameList &outList);
};
class CHEATSEXPORT
{
private:
CHEATS_DB_TYPE type;
bool encrypted;
FILE *fp;
size_t fsize;
u64 dataSize;
u64 encOffset;
FAT_R4 fat;
bool search();
bool getCodes();
void R4decrypt(u8 *buf, const size_t len, u64 n);
CheatDBFile _dbFile;
CheatDBGame *_selectedDbGame;
CHEATS_LIST *_cheats;
u32 numCheats;
CHEATS_LIST *cheats;
u8 error; // 0 - no errors
// 1 - open failed/file not found
// 2 - file format is wrong (no valid header ID)
// 3 - cheat not found in database
// 4 - export error from database
u8 _error; // 0 - no errors
// 1 - open failed/file not found
// 2 - file format is wrong (no valid header ID)
// 3 - cheat not found in database
// 4 - export error from database
public:
CHEATSEXPORT() :
fp(NULL),
fsize(0),
dataSize(0),
encOffset(0),
type(CHEATS_DB_R4),
encrypted(false),
numCheats(0),
cheats(0),
CRC(0),
error(0)
{
memset(&fat, 0, sizeof(fat));
memset(gametitle, 0, sizeof(gametitle));
memset(date, 0, sizeof(date));
}
u8 gametitle[CHEAT_DB_GAME_TITLE_SIZE];
u8 date[17];
u32 CRC;
bool load(char *path);
void close();
CHEATS_LIST *getCheats();
u32 getCheatsNum();
u8 getErrorCode() { return error; }
CHEATSEXPORT();
~CHEATSEXPORT();
bool load(const char *path);
void close();
CHEATS_LIST *getCheats() const;
size_t getCheatsNum() const;
const char* getGameTitle() const;
const char* getDescription() const;
u8 getErrorCode() const;
};
CheatDBGame* GetCheatDBGameEntryFromList(const CheatDBGameList &gameList, const char *gameCode, const u32 gameDatabaseCRC);
void CheatItemGenerateDescriptionHierarchical(const char *itemName, const char *itemNote, CHEATS_LIST &outCheatItem);
void CheatItemGenerateDescriptionFlat(const char *folderName, const char *folderNote, const char *itemName, const char *itemNote, CHEATS_LIST &outCheatItem);
extern CHEATS *cheats;
extern CHEATSEARCH *cheatSearch;

View File

@ -95,7 +95,8 @@ protected:
bool _willAddFromDB;
CheatType _cheatType;
std::string _descriptionString;
std::string _descriptionMajorString;
std::string _descriptionMinorString;
// Internal cheat type parameters
CheatFreezeType _freezeType;
@ -132,8 +133,8 @@ public:
void SetType(CheatType requestedType);
bool IsSupportedType() const;
const char* GetDescription() const;
void SetDescription(const char *descriptionString);
const char* GetMajorDescription() const;
void SetMajorDescription(const char *descriptionString);
CheatFreezeType GetFreezeType() const;
void SetFreezeType(CheatFreezeType theFreezeType);
@ -224,7 +225,7 @@ class ClientCheatDatabase
protected:
ClientCheatList *_list;
std::string _title;
std::string _date;
std::string _description;
std::string _lastFilePath;
public:
@ -235,7 +236,7 @@ public:
ClientCheatList* LoadFromFile(const char *dbFilePath);
const char* GetTitle() const;
const char* GetDate() const;
const char* GetDescription() const;
};
class ClientCheatManager
@ -289,7 +290,7 @@ public:
ClientCheatList* GetDatabaseList() const;
ClientCheatList* DatabaseListLoadFromFile(const char *dbFilePath);
const char* GetDatabaseTitle() const;
const char* GetDatabaseDate() const;
const char* GetDatabaseDescription() const;
bool SearchDidStart() const;
void SearchReset();

View File

@ -178,7 +178,8 @@ ClientCheatItem::ClientCheatItem()
_willAddFromDB = false;
_cheatType = CheatType_Internal;
_descriptionString = "No description.";
_descriptionMajorString = "No description.";
_descriptionMinorString = "";
_freezeType = CheatFreezeType_Normal;
_address = 0x02000000;
strncpy(_addressString, "0x02000000", sizeof(_addressString));
@ -202,7 +203,8 @@ void ClientCheatItem::Init(const CHEATS_LIST &inCheatItem)
this->_isEnabled = (inCheatItem.enabled) ? true : false;
this->_cheatType = (CheatType)inCheatItem.type;
this->_descriptionString = inCheatItem.description;
this->_descriptionMajorString = inCheatItem.description;
this->_descriptionMinorString = "";
this->_freezeType = (CheatFreezeType)inCheatItem.freezeType;
this->_valueLength = inCheatItem.size + 1; // CHEATS_LIST.size value range is [1...4], but starts counting from 0.
@ -238,7 +240,7 @@ void ClientCheatItem::Init(const CHEATS_LIST &inCheatItem)
void ClientCheatItem::Init(const ClientCheatItem &inCheatItem)
{
this->SetEnabled(inCheatItem.IsEnabled());
this->SetDescription(inCheatItem.GetDescription());
this->SetMajorDescription(inCheatItem.GetMajorDescription());
this->SetType(inCheatItem.GetType());
this->SetFreezeType(inCheatItem.GetFreezeType());
@ -322,20 +324,20 @@ bool ClientCheatItem::IsSupportedType() const
return (this->_cheatType != CheatType_CodeBreaker);
}
const char* ClientCheatItem::GetDescription() const
const char* ClientCheatItem::GetMajorDescription() const
{
return this->_descriptionString.c_str();
return this->_descriptionMajorString.c_str();
}
void ClientCheatItem::SetDescription(const char *descriptionString)
void ClientCheatItem::SetMajorDescription(const char *descriptionString)
{
if (descriptionString == NULL)
{
this->_descriptionString = "";
this->_descriptionMajorString = "";
}
else
{
this->_descriptionString = descriptionString;
this->_descriptionMajorString = descriptionString;
}
}
@ -608,7 +610,7 @@ void ClientCheatItem::ClientToDesmumeCheatItem(CHEATS_LIST *outCheatItem) const
outCheatItem->type = this->_cheatType;
outCheatItem->enabled = (this->_isEnabled) ? 1 : 0;
strncpy(outCheatItem->description, this->_descriptionString.c_str(), sizeof(outCheatItem->description));
strncpy(outCheatItem->description, this->_descriptionMajorString.c_str(), sizeof(outCheatItem->description));
switch (this->_cheatType)
{
@ -1062,7 +1064,7 @@ ClientCheatDatabase::ClientCheatDatabase()
{
_list = new ClientCheatList;
_title.resize(0);
_date.resize(0);
_description.resize(0);
_lastFilePath.resize(0);
}
@ -1090,8 +1092,8 @@ ClientCheatList* ClientCheatDatabase::LoadFromFile(const char *dbFilePath)
if (didFileLoad)
{
this->_list->RemoveAll();
this->_title = (const char *)exporter->gametitle;
this->_date = (const char *)exporter->date;
this->_title = exporter->getGameTitle();
this->_description = exporter->getDescription();
this->_lastFilePath = dbFilePath;
const size_t itemCount = exporter->getCheatsNum();
@ -1127,9 +1129,9 @@ const char* ClientCheatDatabase::GetTitle() const
return this->_title.c_str();
}
const char* ClientCheatDatabase::GetDate() const
const char* ClientCheatDatabase::GetDescription() const
{
return this->_date.c_str();
return this->_description.c_str();
}
#pragma mark -
@ -1242,11 +1244,11 @@ ClientCheatItem* ClientCheatManager::NewItem()
char newDesc[16];
snprintf(newDesc, sizeof(newDesc), "Untitled %ld", (unsigned long)this->_untitledCount);
newItem->SetDescription(newDesc);
newItem->SetMajorDescription(newDesc);
}
else
{
newItem->SetDescription("Untitled");
newItem->SetMajorDescription("Untitled");
}
if (newItem->IsEnabled())
@ -1407,9 +1409,9 @@ const char* ClientCheatManager::GetDatabaseTitle() const
return this->_currentDatabase->GetTitle();
}
const char* ClientCheatManager::GetDatabaseDate() const
const char* ClientCheatManager::GetDatabaseDescription() const
{
return this->_currentDatabase->GetDate();
return this->_currentDatabase->GetDescription();
}
bool ClientCheatManager::SearchDidStart() const
@ -1623,18 +1625,18 @@ static NSImage *iconCodeBreaker = nil;
- (NSString *) description
{
return [NSString stringWithCString:_internalData->GetDescription() encoding:NSUTF8StringEncoding];
return [NSString stringWithCString:_internalData->GetMajorDescription() encoding:NSUTF8StringEncoding];
}
- (void) setDescription:(NSString *)desc
{
if (desc == nil)
{
_internalData->SetDescription(NULL);
_internalData->SetMajorDescription(NULL);
}
else
{
_internalData->SetDescription([desc cStringUsingEncoding:NSUTF8StringEncoding]);
_internalData->SetMajorDescription([desc cStringUsingEncoding:NSUTF8StringEncoding]);
}
if ((workingCopy != nil) && !_disableWorkingCopyUpdate)
@ -1645,7 +1647,7 @@ static NSImage *iconCodeBreaker = nil;
- (char *) descriptionCString
{
return (char *)_internalData->GetDescription();
return (char *)_internalData->GetMajorDescription();
}
- (NSInteger) cheatType
@ -2072,7 +2074,7 @@ static NSImage *iconCodeBreaker = nil;
- (NSString *) databaseDate
{
return [NSString stringWithCString:_internalCheatManager->GetDatabaseDate() encoding:NSUTF8StringEncoding];
return [NSString stringWithCString:_internalCheatManager->GetDatabaseDescription() encoding:NSUTF8StringEncoding];
}
- (NSUInteger) itemTotalCount

View File

@ -1199,7 +1199,7 @@ void UpdateDisplayPropertiesFromStates(uint64_t displayModeStates, ClientDisplay
ClientCheatItem *newCheatItem = new ClientCheatItem;
newCheatItem->SetType(CheatType_ActionReplay); // Default to Action Replay for now
newCheatItem->SetFreezeType(CheatFreezeType_Normal);
newCheatItem->SetDescription(NULL); // OpenEmu takes care of this
newCheatItem->SetMajorDescription(NULL); // OpenEmu takes care of this
newCheatItem->SetRawCodeString([code cStringUsingEncoding:NSUTF8StringEncoding], true);
newCheatItem->SetEnabled((enabled) ? true : false);

View File

@ -1534,13 +1534,13 @@ INT_PTR CALLBACK CheatsExportProc(HWND dialog, UINT msg,WPARAM wparam,LPARAM lpa
{
case WM_INITDIALOG:
{
SetWindowText(GetDlgItem(dialog, IDC_CDATE), (LPCSTR)cheatsExport->date);
if (cheatsExport->gametitle && strlen((char*)cheatsExport->gametitle) > 0)
SetWindowText(GetDlgItem(dialog, IDC_CDATE), (LPCSTR)cheatsExport->getDescription());
if ( strlen(cheatsExport->getGameTitle()) > 0 )
{
char buf[512] = {0};
GetWindowText(dialog, &buf[0], sizeof(buf));
strcat(buf, ": ");
strcat(buf, (char*)cheatsExport->gametitle);
strcat(buf, cheatsExport->getGameTitle());
SetWindowText(dialog, (LPCSTR)buf);
}
LV_COLUMN lvColumn;