Modernize cheat stuff.

This commit is contained in:
BearOso 2022-05-10 20:47:12 -05:00
parent 9ed560de2f
commit 2e39f2f477
8 changed files with 350 additions and 480 deletions

View File

@ -37,112 +37,6 @@
(s) == S9X_24_BITS ? (((int32) ((*((m) + (o)) + (*((m) + (o) + 1) << 8) + (*((m) + (o) + 2) << 16)) << 8)) >> 8): \
((int32) (*((m) + (o)) + (*((m) + (o) + 1) << 8) + (*((m) + (o) + 2) << 16) + (*((m) + (o) + 3) << 24))))
static bool8 S9xAllHex (const char *, int);
static bool8 S9xAllHex (const char *code, int len)
{
for (int i = 0; i < len; i++)
if ((code[i] < '0' || code[i] > '9') && (code[i] < 'a' || code[i] > 'f') && (code[i] < 'A' || code[i] > 'F'))
return (FALSE);
return (TRUE);
}
const char * S9xProActionReplayToRaw (const char *code, uint32 &address, uint8 &byte)
{
uint32 data = 0;
if (strlen(code) != 8 || !S9xAllHex(code, 8) || sscanf(code, "%x", &data) != 1)
return ("Invalid Pro Action Replay code - should be 8 hex digits in length.");
address = data >> 8;
byte = (uint8) data;
return (NULL);
}
const char * S9xGoldFingerToRaw (const char *code, uint32 &address, bool8 &sram, uint8 &num_bytes, uint8 bytes[3])
{
char tmp[15];
int i;
if (strlen(code) != 14)
return ("Invalid Gold Finger code - should be 14 hex digits in length.");
strncpy(tmp, code, 5);
tmp[5] = 0;
if (sscanf(tmp, "%x", &address) != 1)
return ("Invalid Gold Finger code.");
// Correct GoldFinger Address
address = (address & 0x7FFF) | ((address & 0x7F8000) << 1) | 0x8000;
for (i = 0; i < 3; i++)
{
unsigned int byte;
strncpy(tmp, code + 5 + i * 2, 2);
tmp[2] = 0;
if (sscanf(tmp, "%x", &byte) != 1)
break;
bytes[i] = (uint8) byte;
}
num_bytes = i;
sram = code[13] == '1';
return (NULL);
}
const char * S9xGameGenieToRaw (const char *code, uint32 &address, uint8 &byte)
{
char new_code[12];
if (strlen(code) != 9 || *(code + 4) != '-' || !S9xAllHex(code, 4) || !S9xAllHex(code + 5, 4))
return ("Invalid Game Genie(tm) code - should be 'xxxx-xxxx'.");
strcpy(new_code, "0x");
strncpy(new_code + 2, code, 4);
strcpy(new_code + 6, code + 5);
static const char *real_hex = "0123456789ABCDEF";
static const char *genie_hex = "DF4709156BC8A23E";
for (int i = 2; i < 10; i++)
{
if (islower(new_code[i]))
new_code[i] = toupper(new_code[i]);
int j;
for (j = 0; j < 16; j++)
{
if (new_code[i] == genie_hex[j])
{
new_code[i] = real_hex[j];
break;
}
}
if (j == 16)
return ("Invalid hex-character in Game Genie(tm) code.");
}
uint32 data = 0;
sscanf(new_code, "%x", &data);
byte = (uint8) (data >> 24);
address = data & 0xffffff;
address = ((address & 0x003c00) << 10) +
((address & 0x00003c) << 14) +
((address & 0xf00000) >> 8) +
((address & 0x000003) << 10) +
((address & 0x00c000) >> 6) +
((address & 0x0f0000) >> 12) +
((address & 0x0003c0) >> 6);
return (NULL);
}
void S9xStartCheatSearch (SCheatData *d)
{
memmove(d->CWRAM, d->RAM, 0x20000);

View File

@ -23,14 +23,14 @@ struct SCheat
struct SCheatGroup
{
char *name;
std::string name;
bool8 enabled;
std::vector<struct SCheat> c;
std::vector<struct SCheat> cheat;
};
struct SCheatData
{
std::vector<struct SCheatGroup> g;
std::vector<struct SCheatGroup> group;
bool8 enabled;
uint8 CWRAM[0x20000];
uint8 CSRAM[0x80000];
@ -73,20 +73,20 @@ typedef enum
extern SCheatData Cheat;
extern Watch watches[16];
int S9xAddCheatGroup (const char *name, const char *cheat);
int S9xModifyCheatGroup (uint32 index, const char *name, const char *cheat);
void S9xEnableCheatGroup (uint32 index);
void S9xDisableCheatGroup (uint32 index);
void S9xDeleteCheats (void);
char *S9xCheatGroupToText (uint32 index);
void S9xDeleteCheatGroup (uint32 index);
bool8 S9xLoadCheatFile (const char *filename);
bool8 S9xSaveCheatFile (const char *filename);
void S9xUpdateCheatsInMemory (void);
int S9xImportCheatsFromDatabase(const char *filename);
void S9xCheatsDisable (void);
void S9xCheatsEnable (void);
char *S9xCheatValidate (const char *cheat);
int S9xAddCheatGroup(const std::string &name, const std::string &cheat);
int S9xModifyCheatGroup(uint32 index, const std::string &name, const std::string &cheat);
void S9xEnableCheatGroup(uint32 index);
void S9xDisableCheatGroup(uint32 index);
void S9xDeleteCheats(void);
std::string S9xCheatGroupToText(uint32 index);
void S9xDeleteCheatGroup(uint32 index);
bool8 S9xLoadCheatFile(const std::string &filename);
bool8 S9xSaveCheatFile(const std::string &filename);
void S9xUpdateCheatsInMemory(void);
int S9xImportCheatsFromDatabase(const std::string &filename);
void S9xCheatsDisable(void);
void S9xCheatsEnable(void);
std::string S9xCheatValidate(const std::string &cheat);
void S9xInitCheatData (void);
void S9xInitWatchedAddress (void);
@ -96,8 +96,4 @@ void S9xSearchForValue (SCheatData *, S9xCheatComparisonType, S9xCheatDataSize,
void S9xSearchForAddress (SCheatData *, S9xCheatComparisonType, S9xCheatDataSize, uint32, bool8);
void S9xOutputCheatSearchResults (SCheatData *);
const char * S9xGameGenieToRaw (const char *, uint32 &, uint8 &);
const char * S9xProActionReplayToRaw (const char *, uint32 &, uint8 &);
const char * S9xGoldFingerToRaw (const char *, uint32 &, bool8 &, uint8 &, uint8 bytes[3]);
#endif

View File

@ -4,37 +4,25 @@
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#include <ctype.h>
#include "bml.h"
#include "cheats.h"
#include "fmt/format.h"
#include "snes9x.h"
#include "memmap.h"
#include "cheats.h"
#include "bml.h"
static inline char *trim (char *string)
static inline uint8 S9xGetByteFree(uint32 Address)
{
int start;
int end;
for (start = 0; string[start] && isspace (string[start]); start++) {}
for (end = start; string[end] && !isspace (string[end]); end++) {}
string[end] = '\0';
return &string[start];
}
static inline uint8 S9xGetByteFree (uint32 Address)
{
int block = (Address & 0xffffff) >> MEMMAP_SHIFT;
int block = (Address & 0xffffff) >> MEMMAP_SHIFT;
uint8 *GetAddress = Memory.Map[block];
uint8 byte;
if (GetAddress >= (uint8 *) CMemory::MAP_LAST)
if (GetAddress >= (uint8 *)CMemory::MAP_LAST)
{
byte = *(GetAddress + (Address & 0xffff));
return (byte);
}
switch ((pint) GetAddress)
switch ((pint)GetAddress)
{
case CMemory::MAP_CPU:
byte = S9xGetCPU(Address & 0xffff);
@ -108,18 +96,18 @@ static inline uint8 S9xGetByteFree (uint32 Address)
}
}
static inline void S9xSetByteFree (uint8 Byte, uint32 Address)
static inline void S9xSetByteFree(uint8 Byte, uint32 Address)
{
int block = (Address & 0xffffff) >> MEMMAP_SHIFT;
uint8 *SetAddress = Memory.Map[block];
if (SetAddress >= (uint8 *) CMemory::MAP_LAST)
if (SetAddress >= (uint8 *)CMemory::MAP_LAST)
{
*(SetAddress + (Address & 0xffff)) = Byte;
return;
}
switch ((pint) SetAddress)
switch ((pint)SetAddress)
{
case CMemory::MAP_CPU:
S9xSetCPU(Byte, Address & 0xffff);
@ -197,165 +185,229 @@ static inline void S9xSetByteFree (uint8 Byte, uint32 Address)
}
}
void S9xInitWatchedAddress (void)
void S9xInitWatchedAddress(void)
{
for (unsigned int i = 0; i < sizeof(watches) / sizeof(watches[0]); i++)
watches[i].on = false;
}
void S9xInitCheatData (void)
void S9xInitCheatData(void)
{
Cheat.RAM = Memory.RAM;
Cheat.SRAM = Memory.SRAM;
Cheat.FillRAM = Memory.FillRAM;
}
static inline std::string trim(const std::string &&string)
{
auto start = string.find_first_not_of(" \t\n\r");
auto end = string.find_last_not_of(" \t\n\r");
if (start != std::string::npos && end != std::string::npos)
return string.substr(start, end - start + 1);
return "";
}
void S9xUpdateCheatInMemory (SCheat *c)
void S9xUpdateCheatInMemory(SCheat &c)
{
uint8 byte;
if (!c->enabled)
if (!c.enabled)
return;
byte = S9xGetByteFree (c->address);
byte = S9xGetByteFree(c.address);
if (byte != c->byte)
if (byte != c.byte)
{
/* The game wrote a different byte to the address, update saved_byte */
c->saved_byte = byte;
c.saved_byte = byte;
if (c->conditional)
if (c.conditional)
{
if (c->saved_byte != c->cond_byte && c->cond_true)
if (c.saved_byte != c.cond_byte && c.cond_true)
{
/* Condition is now false, let the byte stand */
c->cond_true = false;
c.cond_true = false;
}
else if (c->saved_byte == c->cond_byte && !c->cond_true)
else if (c.saved_byte == c.cond_byte && !c.cond_true)
{
c->cond_true = true;
S9xSetByteFree (c->byte, c->address);
c.cond_true = true;
S9xSetByteFree(c.byte, c.address);
}
}
else
S9xSetByteFree (c->byte, c->address);
S9xSetByteFree(c.byte, c.address);
}
else if (c->conditional)
else if (c.conditional)
{
if (byte == c->cond_byte)
if (byte == c.cond_byte)
{
c->cond_true = true;
c->saved_byte = byte;
S9xSetByteFree (c->byte, c->address);
c.cond_true = true;
c.saved_byte = byte;
S9xSetByteFree(c.byte, c.address);
}
}
}
void S9xDisableCheat (SCheat *c)
void S9xDisableCheat(SCheat &c)
{
if (!c->enabled)
if (!c.enabled)
return;
if (!Cheat.enabled)
{
c->enabled = false;
c.enabled = false;
return;
}
/* Make sure we restore the up-to-date written byte */
S9xUpdateCheatInMemory (c);
c->enabled = false;
S9xUpdateCheatInMemory(c);
c.enabled = false;
if (c->conditional && !c->cond_true)
if (c.conditional && !c.cond_true)
return;
S9xSetByteFree (c->saved_byte, c->address);
c->cond_true = false;
S9xSetByteFree (c.saved_byte, c.address);
c.cond_true = false;
}
void S9xDeleteCheatGroup (uint32 g)
void S9xDeleteCheatGroup(uint32 g)
{
unsigned int i;
if (g >= Cheat.g.size ())
if (g >= Cheat.group.size())
return;
for (i = 0; i < Cheat.g[g].c.size (); i++)
for (i = 0; i < Cheat.group[g].cheat.size(); i++)
{
S9xDisableCheat (&Cheat.g[g].c[i]);
S9xDisableCheat(Cheat.group[g].cheat[i]);
}
delete[] Cheat.g[g].name;
Cheat.g.erase (Cheat.g.begin () + g);
Cheat.group.erase(Cheat.group.begin() + g);
}
void S9xDeleteCheats (void)
void S9xDeleteCheats(void)
{
unsigned int i;
for (i = 0; i < Cheat.g.size (); i++)
for (size_t i = 0; i < Cheat.group.size(); i++)
{
S9xDisableCheatGroup (i);
delete[] Cheat.g[i].name;
S9xDisableCheatGroup(i);
}
Cheat.g.clear ();
Cheat.group.clear();
}
void S9xEnableCheat (SCheat *c)
void S9xEnableCheat(SCheat &c)
{
uint8 byte;
if (c->enabled)
if (c.enabled)
return;
c->enabled = true;
c.enabled = true;
if (!Cheat.enabled)
return;
byte = S9xGetByteFree(c->address);
byte = S9xGetByteFree(c.address);
if (c->conditional)
if (c.conditional)
{
if (byte != c->cond_byte)
if (byte != c.cond_byte)
return;
c->cond_true = true;
c.cond_true = true;
}
c->saved_byte = byte;
S9xSetByteFree (c->byte, c->address);
c.saved_byte = byte;
S9xSetByteFree(c.byte, c.address);
}
void S9xEnableCheatGroup (uint32 num)
void S9xEnableCheatGroup(uint32 num)
{
unsigned int i;
for (auto &c : Cheat.group[num].cheat)
S9xEnableCheat(c);
for (i = 0; i < Cheat.g[num].c.size (); i++)
{
S9xEnableCheat (&Cheat.g[num].c[i]);
}
Cheat.g[num].enabled = true;
Cheat.group[num].enabled = true;
}
void S9xDisableCheatGroup (uint32 num)
void S9xDisableCheatGroup(uint32 num)
{
unsigned int i;
for (auto &c : Cheat.group[num].cheat)
S9xDisableCheat(c);
for (i = 0; i < Cheat.g[num].c.size (); i++)
{
S9xDisableCheat (&Cheat.g[num].c[i]);
}
Cheat.g[num].enabled = false;
Cheat.group[num].enabled = false;
}
SCheat S9xTextToCheat (char *text)
static bool is_all_hex(const std::string &code)
{
for (const auto &c : code)
{
if ((c < '0' || c > '9') &&
(c < 'a' || c > 'f') &&
(c < 'A' || c > 'F'))
return false;
}
return true;
}
bool S9xProActionReplayToRaw(const std::string &code, uint32 &address, uint8 &byte)
{
if (code.length() != 8 || !is_all_hex(code))
return false;
uint32 data = std::strtoul(code.c_str(), nullptr, 16);
address = data >> 8;
byte = (uint8)data;
return true;
}
bool S9xGameGenieToRaw(const std::string &code, uint32 &address, uint8 &byte)
{
if (code.length() != 9)
return false;
if (code[4] != '-')
return false;
if (!is_all_hex(code.substr(0, 4)))
return false;
if (!is_all_hex(code.substr(5, 4)))
return false;
auto new_code = code.substr(0, 4) + code.substr(5, 4);
static const char *real_hex = "0123456789ABCDEF";
static const char *genie_hex = "DF4709156BC8A23E";
for (auto &c : new_code)
{
c = toupper(c);
for (int i = 0; i < 16; i++)
{
if (genie_hex[i] == c)
{
c = real_hex[i];
break;
}
}
}
uint32 data = strtoul(new_code.c_str(), nullptr, 16);
byte = (uint8)(data >> 24);
address = data & 0xffffff;
address = ((address & 0x003c00) << 10) +
((address & 0x00003c) << 14) +
((address & 0xf00000) >> 8) +
((address & 0x000003) << 10) +
((address & 0x00c000) >> 6) +
((address & 0x0f0000) >> 12) +
((address & 0x0003c0) >> 6);
return true;
}
SCheat S9xTextToCheat(const std::string &text)
{
SCheat c;
unsigned int byte = 0;
@ -364,34 +416,28 @@ SCheat S9xTextToCheat (char *text)
c.enabled = false;
c.conditional = false;
if (!S9xGameGenieToRaw (text, c.address, c.byte))
if (S9xGameGenieToRaw(text, c.address, c.byte))
{
byte = c.byte;
}
else if (!S9xProActionReplayToRaw (text, c.address, c.byte))
else if (S9xProActionReplayToRaw(text, c.address, c.byte))
{
byte = c.byte;
}
else if (sscanf (text, "%x = %x ? %x", &c.address, &cond_byte, &byte) == 3)
else if (sscanf(text.c_str(), "%x = %x ? %x", &c.address, &cond_byte, &byte) == 3)
{
c.conditional = true;
}
else if (sscanf (text, "%x = %x", &c.address, &byte) == 2)
else if (sscanf(text.c_str(), "%x = %x", &c.address, &byte) == 2)
{
}
else if (sscanf (text, "%x / %x / %x", &c.address, &cond_byte, &byte) == 3)
else if (sscanf(text.c_str(), "%x / %x / %x", &c.address, &cond_byte, &byte) == 3)
{
c.conditional = true;
}
else if (sscanf (text, "%x / %x", &c.address, &byte) == 2)
else if (sscanf(text.c_str(), "%x / %x", &c.address, &byte) == 2)
{
}
else
{
c.address = 0;
@ -404,313 +450,282 @@ SCheat S9xTextToCheat (char *text)
return c;
}
SCheatGroup S9xCreateCheatGroup (const char *name, const char *cheat)
std::vector<std::string> split_string(const std::string &str, unsigned char delim)
{
SCheatGroup g;
char *code_string = strdup (cheat);
char *code_ptr = code_string;
int len;
std::vector<std::string> tokens;
size_t pos = 0;
size_t index;
g.name = strdup (name);
g.enabled = false;
for (len = strcspn (code_ptr, "+"); len; len = strcspn (code_ptr, "+"))
while (pos < str.length())
{
char *code = code_ptr;
code_ptr += len + (code_ptr[len] == '\0' ? 0 : 1);
code[len] = '\0';
code = trim (code);
index = str.find(delim, pos);
if (index == std::string::npos)
{
if (pos < str.length())
{
tokens.push_back(trim(str.substr(pos)));
}
SCheat c = S9xTextToCheat (code);
if (c.address)
g.c.push_back (c);
break;
}
else if (index > pos)
{
tokens.push_back(trim(str.substr(pos, index - pos)));
}
pos = index + 1;
}
free(code_string);
return tokens;
}
SCheatGroup S9xCreateCheatGroup(const std::string &name, const std::string &cheat)
{
SCheatGroup g;
g.name = name;
g.enabled = false;
auto cheats = split_string(cheat, '+');
for (const auto &c : cheats)
{
SCheat new_cheat = S9xTextToCheat(c);
if (new_cheat.address)
g.cheat.push_back(new_cheat);
}
return g;
}
int S9xAddCheatGroup (const char *name, const char *cheat)
int S9xAddCheatGroup(const std::string &name, const std::string &cheat)
{
SCheatGroup g = S9xCreateCheatGroup (name, cheat);
if (g.c.size () == 0)
SCheatGroup g = S9xCreateCheatGroup(name, cheat);
if (g.cheat.size() == 0)
return -1;
Cheat.g.push_back (g);
Cheat.group.push_back(g);
return Cheat.g.size () - 1;
return Cheat.group.size() - 1;
}
int S9xModifyCheatGroup (uint32 num, const char *name, const char *cheat)
int S9xModifyCheatGroup(uint32 num, const std::string &name, const std::string &cheat)
{
if (num >= Cheat.g.size())
if (num >= Cheat.group.size())
return -1;
S9xDisableCheatGroup (num);
delete[] Cheat.g[num].name;
S9xDisableCheatGroup(num);
Cheat.g[num] = S9xCreateCheatGroup (name, cheat);
Cheat.group[num] = S9xCreateCheatGroup(name, cheat);
return num;
}
char *S9xCheatToText (SCheat *c)
std::string S9xCheatToText(const SCheat &c)
{
int size = 10; /* 6 address, 1 =, 2 byte, 1 NUL */
char *text;
if (c.conditional)
return fmt::format("{:06x}={:02x}?{:02x}", c.address, c.cond_byte, c.byte);
if (c->conditional)
size += 3; /* additional 2 byte, 1 ? */
return fmt::format("{:06x}={:02x}", c.address, c.byte);
}
text = new char[size];
std::string S9xCheatGroupToText(SCheatGroup &g)
{
std::string text = "";
if (c->conditional)
snprintf (text, size, "%06x=%02x?%02x", c->address, c->cond_byte, c->byte);
else
snprintf (text, size, "%06x=%02x", c->address, c->byte);
for (size_t i = 0; i < g.cheat.size(); i++)
{
text += S9xCheatToText(g.cheat[i]);
if (i != g.cheat.size() - 1)
text += "+";
}
return text;
}
char *S9xCheatGroupToText (SCheatGroup *g)
std::string S9xCheatValidate(const std::string &code_string)
{
std::string text = "";
unsigned int i;
SCheatGroup g = S9xCreateCheatGroup("temp", code_string);
if (g->c.size () == 0)
return NULL;
for (i = 0; i < g->c.size (); i++)
if (g.cheat.size() > 0)
{
char *tmp = S9xCheatToText (&g->c[i]);
if (i != 0)
text += " + ";
text += tmp;
delete[] tmp;
return S9xCheatGroupToText(g);
}
return strdup (text.c_str ());
return "";
}
char *S9xCheatValidate (const char *code_string)
std::string S9xCheatGroupToText(uint32 num)
{
SCheatGroup g = S9xCreateCheatGroup ("temp", code_string);
if (num >= Cheat.group.size())
return "";
delete[] g.name;
if (g.c.size() > 0)
{
return S9xCheatGroupToText (&g);
}
return NULL;
return S9xCheatGroupToText(Cheat.group[num]);
}
char *S9xCheatGroupToText (uint32 num)
void S9xUpdateCheatsInMemory(void)
{
if (num >= Cheat.g.size ())
return NULL;
return S9xCheatGroupToText (&Cheat.g[num]);
}
void S9xUpdateCheatsInMemory (void)
{
unsigned int i;
unsigned int j;
if (!Cheat.enabled)
return;
for (i = 0; i < Cheat.g.size (); i++)
{
for (j = 0; j < Cheat.g[i].c.size (); j++)
{
S9xUpdateCheatInMemory (&Cheat.g[i].c[j]);
}
}
for (auto &group : Cheat.group)
for (auto &cheat : group.cheat)
S9xUpdateCheatInMemory(cheat);
}
static int S9xCheatIsDuplicate (const char *name, const char *code)
static bool S9xCheatIsDuplicate(const std::string &name, const std::string &code)
{
unsigned int i;
for (i = 0; i < Cheat.g.size(); i++)
for (size_t i = 0; i < Cheat.group.size(); i++)
{
if (!strcmp (name, Cheat.g[i].name))
if (Cheat.group[i].name == name)
{
char *code_string = S9xCheatGroupToText (i);
char *validated = S9xCheatValidate (code);
auto code_string = S9xCheatGroupToText(i);
auto validated_string = S9xCheatValidate(code);
if (validated && !strcmp (code_string, validated))
{
free (code_string);
free (validated);
return TRUE;
}
free (code_string);
free (validated);
if (validated_string == code_string)
return true;
}
}
return FALSE;
return false;
}
static void S9xLoadCheatsFromBMLNode (bml_node *n)
static void S9xLoadCheatsFromBMLNode(bml_node &n)
{
unsigned int i;
for (i = 0; i < n->child.size (); i++)
for (auto &c : n.child)
{
if (!strcasecmp (n->child[i].name.c_str(), "cheat"))
{
const char *desc = NULL;
const char *code = NULL;
bool8 enabled = false;
if (strcasecmp(c.name.c_str(), "cheat"))
continue;
bml_node *c = &n->child[i];
bml_node *tmp = NULL;
auto subnode = c.find_subnode("code");
if (!subnode)
continue;
std::string code = subnode->data;
tmp = c->find_subnode("name");
if (!tmp)
desc = (char *) "";
else
desc = tmp->data.c_str();
std::string name;
subnode = c.find_subnode("name");
if (subnode)
name = subnode->data;
tmp = c->find_subnode("code");
if (tmp)
code = tmp->data.c_str();
bool enable = false;
if (c.find_subnode("enable"))
enable = true;
if (c->find_subnode("enable"))
enabled = true;
if (S9xCheatIsDuplicate(name, code))
continue;
if (code && !S9xCheatIsDuplicate (desc, code))
{
int index = S9xAddCheatGroup (desc, code);
if (enabled)
S9xEnableCheatGroup (index);
}
}
auto index = S9xAddCheatGroup(name, code);
if (enable)
S9xEnableCheatGroup(index);
}
return;
}
static bool8 S9xLoadCheatFileClassic (const char *filename)
static bool8 S9xLoadCheatFileClassic(const std::string &filename)
{
FILE *fs;
uint8 data[28];
fs = fopen(filename, "rb");
fs = fopen(filename.c_str(), "rb");
if (!fs)
return (FALSE);
while (fread ((void *) data, 1, 28, fs) == 28)
while (fread(data, 1, 28, fs) == 28)
{
SCheat c;
char name[21];
char cheat[10];
c.enabled = (data[0] & 4) == 0;
c.byte = data[1];
c.address = data[2] | (data[3] << 8) | (data[4] << 16);
memcpy (name, &data[8], 20);
name[20] = 0;
c.address = data[2] | (data[3] << 8) | (data[4] << 16);
snprintf (cheat, 10, "%x=%x", c.address, c.byte);
S9xAddCheatGroup (name, cheat);
std::string name((const char *)&data[8], 20);
auto cheat = fmt::format("{:x}={:x}", c.address, c.byte);
S9xAddCheatGroup(name, cheat);
if (c.enabled)
S9xEnableCheatGroup (Cheat.g.size () - 1);
S9xEnableCheatGroup(Cheat.group.size() - 1);
}
fclose(fs);
return (TRUE);
return TRUE;
}
bool8 S9xLoadCheatFile (const char *filename)
bool8 S9xLoadCheatFile(const std::string &filename)
{
bml_node bml;
if (!bml.parse_file(filename))
{
return S9xLoadCheatFileClassic (filename);
return S9xLoadCheatFileClassic(filename);
}
bml_node *n = bml.find_subnode("cheat");
if (n)
{
S9xLoadCheatsFromBMLNode (&bml);
S9xLoadCheatsFromBMLNode(bml);
}
if (!n)
{
return S9xLoadCheatFileClassic (filename);
return S9xLoadCheatFileClassic(filename);
}
return (TRUE);
}
bool8 S9xSaveCheatFile (const char *filename)
bool8 S9xSaveCheatFile(const std::string &filename)
{
unsigned int i;
FILE *file = NULL;
if (Cheat.g.size () == 0)
if (Cheat.group.size() == 0)
{
remove (filename);
remove(filename.c_str());
return TRUE;
}
file = fopen (filename, "w");
file = fopen(filename.c_str(), "w");
if (!file)
return FALSE;
for (i = 0; i < Cheat.g.size (); i++)
for (i = 0; i < Cheat.group.size(); i++)
{
char *txt = S9xCheatGroupToText (i);
fprintf (file,
"cheat\n"
" name: %s\n"
" code: %s\n"
"%s\n",
Cheat.g[i].name ? Cheat.g[i].name : "",
txt,
Cheat.g[i].enabled ? " enable\n" : ""
);
delete[] txt;
fmt::print(file,
"cheat\n"
" name: {}\n"
" code: {}\n"
"{}\n",
Cheat.group[i].name,
S9xCheatGroupToText(i),
Cheat.group[i].enabled ? " enable\n" : "");
}
fclose (file);
fclose(file);
return TRUE;
}
void S9xCheatsDisable (void)
void S9xCheatsDisable(void)
{
unsigned int i;
if (!Cheat.enabled)
return;
for (i = 0; i < Cheat.g.size (); i++)
for (i = 0; i < Cheat.group.size(); i++)
{
if (Cheat.g[i].enabled)
if (Cheat.group[i].enabled)
{
S9xDisableCheatGroup (i);
Cheat.g[i].enabled = TRUE;
S9xDisableCheatGroup(i);
Cheat.group[i].enabled = TRUE;
}
}
Cheat.enabled = FALSE;
}
void S9xCheatsEnable (void)
void S9xCheatsEnable(void)
{
unsigned int i;
@ -719,17 +734,17 @@ void S9xCheatsEnable (void)
Cheat.enabled = TRUE;
for (i = 0; i < Cheat.g.size (); i++)
for (i = 0; i < Cheat.group.size(); i++)
{
if (Cheat.g[i].enabled)
if (Cheat.group[i].enabled)
{
Cheat.g[i].enabled = FALSE;
S9xEnableCheatGroup (i);
Cheat.group[i].enabled = FALSE;
S9xEnableCheatGroup(i);
}
}
}
int S9xImportCheatsFromDatabase (const char *filename)
int S9xImportCheatsFromDatabase(const std::string &filename)
{
char sha256_txt[65];
char hextable[] = "0123456789abcdef";
@ -746,19 +761,16 @@ int S9xImportCheatsFromDatabase (const char *filename)
}
sha256_txt[64] = '\0';
for (i = 0; i < bml.child.size (); i++)
for (auto &c : bml.child)
{
if (!strcasecmp (bml.child[i].name.c_str(), "cartridge"))
if (!strcasecmp(c.name.c_str(), "cartridge"))
{
bml_node *n;
auto n = c.find_subnode("sha256");
if ((n = bml.child[i].find_subnode ("sha256")))
if (n && !strcasecmp(n->data.c_str(), sha256_txt))
{
if (!strcasecmp (n->data.c_str(), sha256_txt))
{
S9xLoadCheatsFromBMLNode (&bml.child[i]);
return 0;
}
S9xLoadCheatsFromBMLNode(c);
return 0;
}
}
}

View File

@ -99,8 +99,8 @@ void S9xReset (void)
{
S9xResetSaveTimer(FALSE);
memset(Memory.RAM, 0x55, 0x20000);
memset(Memory.VRAM, 0x00, 0x10000);
memset(Memory.RAM, 0x55, sizeof(Memory.RAM));
memset(Memory.VRAM, 0x00, sizeof(Memory.VRAM));
memset(Memory.FillRAM, 0, 0x8000);
S9xResetBSX();

View File

@ -132,20 +132,20 @@ void Snes9xCheats::show()
static void cheat_move(int src, int dst)
{
Cheat.g.insert(Cheat.g.begin() + dst, Cheat.g[src]);
Cheat.group.insert(Cheat.group.begin() + dst, Cheat.group[src]);
if (dst < src)
src++;
Cheat.g.erase(Cheat.g.begin() + src);
Cheat.group.erase(Cheat.group.begin() + src);
}
static void cheat_gather_enabled()
{
unsigned int enabled = 0;
for (unsigned int i = 0; i < Cheat.g.size(); i++)
for (unsigned int i = 0; i < Cheat.group.size(); i++)
{
if (Cheat.g[i].enabled && i >= enabled)
if (Cheat.group[i].enabled && i >= enabled)
{
cheat_move(i, enabled);
enabled++;
@ -199,22 +199,21 @@ void Snes9xCheats::refresh_tree_view()
auto list_size = store->children().size();
if (Cheat.g.size() == 0)
if (Cheat.group.size() == 0)
return;
for (unsigned int i = 0; i < Cheat.g.size() - list_size; i++)
for (unsigned int i = 0; i < Cheat.group.size() - list_size; i++)
store->append();
auto iter = store->children().begin();
for (unsigned int i = 0; i < Cheat.g.size (); i++)
for (unsigned int i = 0; i < Cheat.group.size (); i++)
{
char *str = S9xCheatGroupToText(i);
Glib::ustring description = Cheat.g[i].name[0] == '\0' ? "" :Cheat.g[i].name;
iter->set_value(COLUMN_ENABLED, Cheat.g[i].enabled);
auto str = S9xCheatGroupToText(i);
Glib::ustring description = Cheat.group[i].name[0] == '\0' ? "" :Cheat.group[i].name;
iter->set_value(COLUMN_ENABLED, Cheat.group[i].enabled);
iter->set_value(COLUMN_DESCRIPTION, description);
iter->set_value(COLUMN_CHEAT, Glib::ustring(str));
iter++;
delete[] str;
}
enable_dnd(true);
@ -228,15 +227,14 @@ void Snes9xCheats::add_code()
if (description.empty())
description = _("No description");
if (S9xAddCheatGroup(description.c_str(), code.c_str()) < 0)
if (S9xAddCheatGroup(description, code) < 0)
{
display_errorbox(_("Couldn't find any cheat codes in input."));
return;
}
auto parsed_code = S9xCheatGroupToText(Cheat.g.size() - 1);
auto parsed_code = S9xCheatGroupToText(Cheat.group.size() - 1);
set_entry_text("code_entry", parsed_code);
delete[] parsed_code;
get_object<Gtk::Entry>("code_entry")->grab_focus();
@ -247,7 +245,7 @@ void Snes9xCheats::add_code()
auto selection = get_object<Gtk::TreeView>("cheat_treeview")->get_selection();
Gtk::TreePath path;
path.push_back(Cheat.g.size() - 1);
path.push_back(Cheat.group.size() - 1);
selection->select(path);
auto adj = get_object<Gtk::ScrolledWindow>("cheat_scrolledwindow")->get_vadjustment();
@ -288,53 +286,27 @@ void Snes9xCheats::search_database()
int result;
int reason = 0;
filename = S9xGetDirectory(CHEAT_DIR);
filename += "/cheats.bml";
if (!(result = S9xImportCheatsFromDatabase(filename.c_str())))
for (const auto &dir : { S9xGetDirectory(CHEAT_DIR),
get_config_dir(),
std::string(DATADIR) })
{
refresh_tree_view();
return;
filename = dir + "/cheats.bml";
result = S9xImportCheatsFromDatabase(filename);
if (result == 0)
{
refresh_tree_view();
return;
}
if (result < reason)
reason = result;
}
if (result < reason)
reason = result;
filename = get_config_dir() + "/cheats.bml";
if (!(result = S9xImportCheatsFromDatabase(filename.c_str())))
{
refresh_tree_view();
return;
}
if (result < reason)
reason = result;
filename = std::string(DATADIR) + "/cheats.bml";
if (!(result = S9xImportCheatsFromDatabase(filename.c_str())))
{
refresh_tree_view();
return;
}
if (result < reason)
reason = result;
filename = S9xGetDirectory(ROM_DIR);
filename += "/cheats.bml";
if (!(result = S9xImportCheatsFromDatabase(filename.c_str())))
{
refresh_tree_view();
return;
}
if (result < reason)
reason = result;
auto dialog = Gtk::MessageDialog(*window.get(), reason == -1 ? _("Couldn't Find Cheats Database") : _("No Matching Game Found"), true);
dialog.set_secondary_text(reason == -1 ? _("The database file <b>cheats.bml</b> was not found. It is normally installed with "
"Snes9x, but you may also place a custom copy in your configuration or cheats directory.")
: _("No matching game was found in the databases. If you are using a non-official "
"translation or modified copy, you may be able to find and manually enter the codes."));
"translation or modified copy, you may be able to find and manually enter the codes."), true);
dialog.run();
dialog.hide();
}
@ -348,12 +320,10 @@ void Snes9xCheats::sort_cheats()
void Snes9xCheats::row_activated(const Gtk::TreeModel::Path &path, Gtk::TreeViewColumn *column)
{
int index = get_index_from_path(path);
char *cheat_text;
cheat_text = S9xCheatGroupToText(index);
auto cheat_text = S9xCheatGroupToText(index);
set_entry_text("code_entry", cheat_text);
delete[] cheat_text;
set_entry_text("description_entry", Cheat.g[index].name);
set_entry_text("description_entry", Cheat.group[index].name);
}
void Snes9xCheats::toggle_code(const Glib::ustring &path)
@ -383,16 +353,15 @@ void Snes9xCheats::update_code()
if (description.empty())
description = _("No description");
auto parsed_code = S9xCheatValidate(code.c_str());
if (!parsed_code)
auto parsed_code = S9xCheatValidate(code);
if (parsed_code.empty())
{
display_errorbox(_("Couldn't find any cheat codes in input."));
return;
}
S9xModifyCheatGroup(index, description.c_str(), parsed_code);
S9xModifyCheatGroup(index, description, parsed_code);
set_entry_text("code_entry", parsed_code);
delete[] parsed_code;
get_object<Gtk::Entry>("code_entry")->grab_focus();
@ -401,9 +370,9 @@ void Snes9xCheats::update_code()
void Snes9xCheats::disable_all()
{
for (unsigned int i = 0; i < Cheat.g.size(); i++)
for (unsigned int i = 0; i < Cheat.group.size(); i++)
{
if (Cheat.g[i].enabled)
if (Cheat.group[i].enabled)
S9xDisableCheatGroup(i);
}

View File

@ -98,7 +98,7 @@ std::string S9xGetDirectory(enum s9x_getdirtype dirtype)
}
/* Anything else, use ROM filename path */
if (dirname == "" && gui_config && gui_config->rom_loaded)
if (dirname == "" && !Memory.ROMFilename.empty())
{
fs::path path(Memory.ROMFilename);

View File

@ -85,7 +85,6 @@ STREAM S9xMSU1OpenFile(const char *msu_ext, bool skip_unpacked)
int port = unzFindExtension(unzFile, msu_ext, true, true, true);
if (port == UNZ_OK)
{
printf(" in %s.\n", zip_filename.c_str());
file = new unzStream(unzFile);
}
else

View File

@ -463,7 +463,7 @@ void S9xParseArgsForCheats (char **argv, int argc)
}
else
{
S9xEnableCheatGroup (Cheat.g.size() - 1);
S9xEnableCheatGroup (Cheat.group.size() - 1);
}
}
else
@ -647,7 +647,7 @@ char * S9xParseArgs (char **argv, int argc)
}
else
{
S9xEnableCheatGroup (Cheat.g.size() - 1);
S9xEnableCheatGroup (Cheat.group.size() - 1);
}
}
else