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): \ (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)))) ((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) void S9xStartCheatSearch (SCheatData *d)
{ {
memmove(d->CWRAM, d->RAM, 0x20000); memmove(d->CWRAM, d->RAM, 0x20000);

View File

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

View File

@ -4,37 +4,25 @@
For further information, consult the LICENSE file in the root directory. 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 "snes9x.h"
#include "memmap.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 block = (Address & 0xffffff) >> MEMMAP_SHIFT;
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;
uint8 *GetAddress = Memory.Map[block]; uint8 *GetAddress = Memory.Map[block];
uint8 byte; uint8 byte;
if (GetAddress >= (uint8 *) CMemory::MAP_LAST) if (GetAddress >= (uint8 *)CMemory::MAP_LAST)
{ {
byte = *(GetAddress + (Address & 0xffff)); byte = *(GetAddress + (Address & 0xffff));
return (byte); return (byte);
} }
switch ((pint) GetAddress) switch ((pint)GetAddress)
{ {
case CMemory::MAP_CPU: case CMemory::MAP_CPU:
byte = S9xGetCPU(Address & 0xffff); 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; int block = (Address & 0xffffff) >> MEMMAP_SHIFT;
uint8 *SetAddress = Memory.Map[block]; uint8 *SetAddress = Memory.Map[block];
if (SetAddress >= (uint8 *) CMemory::MAP_LAST) if (SetAddress >= (uint8 *)CMemory::MAP_LAST)
{ {
*(SetAddress + (Address & 0xffff)) = Byte; *(SetAddress + (Address & 0xffff)) = Byte;
return; return;
} }
switch ((pint) SetAddress) switch ((pint)SetAddress)
{ {
case CMemory::MAP_CPU: case CMemory::MAP_CPU:
S9xSetCPU(Byte, Address & 0xffff); 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++) for (unsigned int i = 0; i < sizeof(watches) / sizeof(watches[0]); i++)
watches[i].on = false; watches[i].on = false;
} }
void S9xInitCheatData (void) void S9xInitCheatData(void)
{ {
Cheat.RAM = Memory.RAM; Cheat.RAM = Memory.RAM;
Cheat.SRAM = Memory.SRAM; Cheat.SRAM = Memory.SRAM;
Cheat.FillRAM = Memory.FillRAM; 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; uint8 byte;
if (!c->enabled) if (!c.enabled)
return; 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 */ /* 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 */ /* 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; c.cond_true = true;
S9xSetByteFree (c->byte, c->address); S9xSetByteFree(c.byte, c.address);
} }
} }
else 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.cond_true = true;
c->saved_byte = byte; c.saved_byte = byte;
S9xSetByteFree (c->byte, c->address); S9xSetByteFree(c.byte, c.address);
} }
} }
} }
void S9xDisableCheat (SCheat *c) void S9xDisableCheat(SCheat &c)
{ {
if (!c->enabled) if (!c.enabled)
return; return;
if (!Cheat.enabled) if (!Cheat.enabled)
{ {
c->enabled = false; c.enabled = false;
return; return;
} }
/* Make sure we restore the up-to-date written byte */ /* Make sure we restore the up-to-date written byte */
S9xUpdateCheatInMemory (c); S9xUpdateCheatInMemory(c);
c->enabled = false; c.enabled = false;
if (c->conditional && !c->cond_true) if (c.conditional && !c.cond_true)
return; return;
S9xSetByteFree (c->saved_byte, c->address); S9xSetByteFree (c.saved_byte, c.address);
c->cond_true = false; c.cond_true = false;
} }
void S9xDeleteCheatGroup (uint32 g) void S9xDeleteCheatGroup(uint32 g)
{ {
unsigned int i; unsigned int i;
if (g >= Cheat.g.size ()) if (g >= Cheat.group.size())
return; 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.group.erase(Cheat.group.begin() + g);
Cheat.g.erase (Cheat.g.begin () + g);
} }
void S9xDeleteCheats (void) void S9xDeleteCheats(void)
{ {
unsigned int i; for (size_t i = 0; i < Cheat.group.size(); i++)
for (i = 0; i < Cheat.g.size (); i++)
{ {
S9xDisableCheatGroup (i); S9xDisableCheatGroup(i);
delete[] Cheat.g[i].name;
} }
Cheat.g.clear (); Cheat.group.clear();
} }
void S9xEnableCheat (SCheat *c) void S9xEnableCheat(SCheat &c)
{ {
uint8 byte; uint8 byte;
if (c->enabled) if (c.enabled)
return; return;
c->enabled = true; c.enabled = true;
if (!Cheat.enabled) if (!Cheat.enabled)
return; 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; return;
c->cond_true = true; c.cond_true = true;
} }
c->saved_byte = byte; c.saved_byte = byte;
S9xSetByteFree (c->byte, c->address); 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++) Cheat.group[num].enabled = true;
{
S9xEnableCheat (&Cheat.g[num].c[i]);
}
Cheat.g[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++) Cheat.group[num].enabled = false;
{
S9xDisableCheat (&Cheat.g[num].c[i]);
}
Cheat.g[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; SCheat c;
unsigned int byte = 0; unsigned int byte = 0;
@ -364,34 +416,28 @@ SCheat S9xTextToCheat (char *text)
c.enabled = false; c.enabled = false;
c.conditional = false; c.conditional = false;
if (!S9xGameGenieToRaw (text, c.address, c.byte)) if (S9xGameGenieToRaw(text, c.address, c.byte))
{ {
byte = c.byte; byte = c.byte;
} }
else if (S9xProActionReplayToRaw(text, c.address, c.byte))
else if (!S9xProActionReplayToRaw (text, c.address, c.byte))
{ {
byte = c.byte; byte = c.byte;
} }
else if (sscanf(text.c_str(), "%x = %x ? %x", &c.address, &cond_byte, &byte) == 3)
else if (sscanf (text, "%x = %x ? %x", &c.address, &cond_byte, &byte) == 3)
{ {
c.conditional = true; c.conditional = true;
} }
else if (sscanf(text.c_str(), "%x = %x", &c.address, &byte) == 2)
else if (sscanf (text, "%x = %x", &c.address, &byte) == 2)
{ {
} }
else if (sscanf(text.c_str(), "%x / %x / %x", &c.address, &cond_byte, &byte) == 3)
else if (sscanf (text, "%x / %x / %x", &c.address, &cond_byte, &byte) == 3)
{ {
c.conditional = true; c.conditional = true;
} }
else if (sscanf(text.c_str(), "%x / %x", &c.address, &byte) == 2)
else if (sscanf (text, "%x / %x", &c.address, &byte) == 2)
{ {
} }
else else
{ {
c.address = 0; c.address = 0;
@ -404,313 +450,282 @@ SCheat S9xTextToCheat (char *text)
return c; 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; std::vector<std::string> tokens;
char *code_string = strdup (cheat); size_t pos = 0;
char *code_ptr = code_string; size_t index;
int len;
g.name = strdup (name); while (pos < str.length())
g.enabled = false;
for (len = strcspn (code_ptr, "+"); len; len = strcspn (code_ptr, "+"))
{ {
char *code = code_ptr; index = str.find(delim, pos);
code_ptr += len + (code_ptr[len] == '\0' ? 0 : 1); if (index == std::string::npos)
code[len] = '\0'; {
code = trim (code); if (pos < str.length())
{
tokens.push_back(trim(str.substr(pos)));
}
SCheat c = S9xTextToCheat (code); break;
if (c.address) }
g.c.push_back (c); 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; 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); SCheatGroup g = S9xCreateCheatGroup(name, cheat);
if (g.c.size () == 0) if (g.cheat.size() == 0)
return -1; 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; return -1;
S9xDisableCheatGroup (num); S9xDisableCheatGroup(num);
delete[] Cheat.g[num].name;
Cheat.g[num] = S9xCreateCheatGroup (name, cheat); Cheat.group[num] = S9xCreateCheatGroup(name, cheat);
return num; return num;
} }
char *S9xCheatToText (SCheat *c) std::string S9xCheatToText(const SCheat &c)
{ {
int size = 10; /* 6 address, 1 =, 2 byte, 1 NUL */ if (c.conditional)
char *text; return fmt::format("{:06x}={:02x}?{:02x}", c.address, c.cond_byte, c.byte);
if (c->conditional) return fmt::format("{:06x}={:02x}", c.address, c.byte);
size += 3; /* additional 2 byte, 1 ? */ }
text = new char[size]; std::string S9xCheatGroupToText(SCheatGroup &g)
{
std::string text = "";
if (c->conditional) for (size_t i = 0; i < g.cheat.size(); i++)
snprintf (text, size, "%06x=%02x?%02x", c->address, c->cond_byte, c->byte); {
else text += S9xCheatToText(g.cheat[i]);
snprintf (text, size, "%06x=%02x", c->address, c->byte); if (i != g.cheat.size() - 1)
text += "+";
}
return text; return text;
} }
char *S9xCheatGroupToText (SCheatGroup *g) std::string S9xCheatValidate(const std::string &code_string)
{ {
std::string text = ""; SCheatGroup g = S9xCreateCheatGroup("temp", code_string);
unsigned int i;
if (g->c.size () == 0) if (g.cheat.size() > 0)
return NULL;
for (i = 0; i < g->c.size (); i++)
{ {
char *tmp = S9xCheatToText (&g->c[i]); return S9xCheatGroupToText(g);
if (i != 0)
text += " + ";
text += tmp;
delete[] tmp;
} }
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; return S9xCheatGroupToText(Cheat.group[num]);
if (g.c.size() > 0)
{
return S9xCheatGroupToText (&g);
}
return NULL;
} }
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) if (!Cheat.enabled)
return; return;
for (i = 0; i < Cheat.g.size (); i++) for (auto &group : Cheat.group)
{ for (auto &cheat : group.cheat)
for (j = 0; j < Cheat.g[i].c.size (); j++) S9xUpdateCheatInMemory(cheat);
{
S9xUpdateCheatInMemory (&Cheat.g[i].c[j]);
}
}
} }
static int S9xCheatIsDuplicate (const char *name, const char *code) static bool S9xCheatIsDuplicate(const std::string &name, const std::string &code)
{ {
unsigned int i; for (size_t i = 0; i < Cheat.group.size(); i++)
for (i = 0; i < Cheat.g.size(); i++)
{ {
if (!strcmp (name, Cheat.g[i].name)) if (Cheat.group[i].name == name)
{ {
char *code_string = S9xCheatGroupToText (i); auto code_string = S9xCheatGroupToText(i);
char *validated = S9xCheatValidate (code); auto validated_string = S9xCheatValidate(code);
if (validated && !strcmp (code_string, validated)) if (validated_string == code_string)
{ return true;
free (code_string);
free (validated);
return TRUE;
}
free (code_string);
free (validated);
} }
} }
return FALSE; return false;
} }
static void S9xLoadCheatsFromBMLNode (bml_node *n) static void S9xLoadCheatsFromBMLNode(bml_node &n)
{ {
unsigned int i; for (auto &c : n.child)
for (i = 0; i < n->child.size (); i++)
{ {
if (!strcasecmp (n->child[i].name.c_str(), "cheat")) if (strcasecmp(c.name.c_str(), "cheat"))
{ continue;
const char *desc = NULL;
const char *code = NULL;
bool8 enabled = false;
bml_node *c = &n->child[i]; auto subnode = c.find_subnode("code");
bml_node *tmp = NULL; if (!subnode)
continue;
std::string code = subnode->data;
tmp = c->find_subnode("name"); std::string name;
if (!tmp) subnode = c.find_subnode("name");
desc = (char *) ""; if (subnode)
else name = subnode->data;
desc = tmp->data.c_str();
tmp = c->find_subnode("code"); bool enable = false;
if (tmp) if (c.find_subnode("enable"))
code = tmp->data.c_str(); enable = true;
if (c->find_subnode("enable")) if (S9xCheatIsDuplicate(name, code))
enabled = true; continue;
if (code && !S9xCheatIsDuplicate (desc, code)) auto index = S9xAddCheatGroup(name, code);
{ if (enable)
int index = S9xAddCheatGroup (desc, code); S9xEnableCheatGroup(index);
if (enabled)
S9xEnableCheatGroup (index);
}
}
} }
return; return;
} }
static bool8 S9xLoadCheatFileClassic (const char *filename) static bool8 S9xLoadCheatFileClassic(const std::string &filename)
{ {
FILE *fs; FILE *fs;
uint8 data[28]; uint8 data[28];
fs = fopen(filename, "rb"); fs = fopen(filename.c_str(), "rb");
if (!fs) if (!fs)
return (FALSE); return (FALSE);
while (fread ((void *) data, 1, 28, fs) == 28) while (fread(data, 1, 28, fs) == 28)
{ {
SCheat c; SCheat c;
char name[21];
char cheat[10];
c.enabled = (data[0] & 4) == 0; c.enabled = (data[0] & 4) == 0;
c.byte = data[1]; c.byte = data[1];
c.address = data[2] | (data[3] << 8) | (data[4] << 16); c.address = data[2] | (data[3] << 8) | (data[4] << 16);
memcpy (name, &data[8], 20);
name[20] = 0;
snprintf (cheat, 10, "%x=%x", c.address, c.byte); std::string name((const char *)&data[8], 20);
S9xAddCheatGroup (name, cheat); auto cheat = fmt::format("{:x}={:x}", c.address, c.byte);
S9xAddCheatGroup(name, cheat);
if (c.enabled) if (c.enabled)
S9xEnableCheatGroup (Cheat.g.size () - 1); S9xEnableCheatGroup(Cheat.group.size() - 1);
} }
fclose(fs); fclose(fs);
return (TRUE); return TRUE;
} }
bool8 S9xLoadCheatFile (const char *filename) bool8 S9xLoadCheatFile(const std::string &filename)
{ {
bml_node bml; bml_node bml;
if (!bml.parse_file(filename)) if (!bml.parse_file(filename))
{ {
return S9xLoadCheatFileClassic (filename); return S9xLoadCheatFileClassic(filename);
} }
bml_node *n = bml.find_subnode("cheat"); bml_node *n = bml.find_subnode("cheat");
if (n) if (n)
{ {
S9xLoadCheatsFromBMLNode (&bml); S9xLoadCheatsFromBMLNode(bml);
} }
if (!n) if (!n)
{ {
return S9xLoadCheatFileClassic (filename); return S9xLoadCheatFileClassic(filename);
} }
return (TRUE); return (TRUE);
} }
bool8 S9xSaveCheatFile (const char *filename) bool8 S9xSaveCheatFile(const std::string &filename)
{ {
unsigned int i; unsigned int i;
FILE *file = NULL; FILE *file = NULL;
if (Cheat.g.size () == 0) if (Cheat.group.size() == 0)
{ {
remove (filename); remove(filename.c_str());
return TRUE; return TRUE;
} }
file = fopen (filename, "w"); file = fopen(filename.c_str(), "w");
if (!file) if (!file)
return FALSE; return FALSE;
for (i = 0; i < Cheat.g.size (); i++) for (i = 0; i < Cheat.group.size(); i++)
{ {
char *txt = S9xCheatGroupToText (i); fmt::print(file,
"cheat\n"
fprintf (file, " name: {}\n"
"cheat\n" " code: {}\n"
" name: %s\n" "{}\n",
" code: %s\n" Cheat.group[i].name,
"%s\n", S9xCheatGroupToText(i),
Cheat.g[i].name ? Cheat.g[i].name : "", Cheat.group[i].enabled ? " enable\n" : "");
txt,
Cheat.g[i].enabled ? " enable\n" : ""
);
delete[] txt;
} }
fclose (file); fclose(file);
return TRUE; return TRUE;
} }
void S9xCheatsDisable (void) void S9xCheatsDisable(void)
{ {
unsigned int i; unsigned int i;
if (!Cheat.enabled) if (!Cheat.enabled)
return; 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); S9xDisableCheatGroup(i);
Cheat.g[i].enabled = TRUE; Cheat.group[i].enabled = TRUE;
} }
} }
Cheat.enabled = FALSE; Cheat.enabled = FALSE;
} }
void S9xCheatsEnable (void) void S9xCheatsEnable(void)
{ {
unsigned int i; unsigned int i;
@ -719,17 +734,17 @@ void S9xCheatsEnable (void)
Cheat.enabled = TRUE; 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; Cheat.group[i].enabled = FALSE;
S9xEnableCheatGroup (i); S9xEnableCheatGroup(i);
} }
} }
} }
int S9xImportCheatsFromDatabase (const char *filename) int S9xImportCheatsFromDatabase(const std::string &filename)
{ {
char sha256_txt[65]; char sha256_txt[65];
char hextable[] = "0123456789abcdef"; char hextable[] = "0123456789abcdef";
@ -746,19 +761,16 @@ int S9xImportCheatsFromDatabase (const char *filename)
} }
sha256_txt[64] = '\0'; 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(c);
{ return 0;
S9xLoadCheatsFromBMLNode (&bml.child[i]);
return 0;
}
} }
} }
} }

View File

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

View File

@ -132,20 +132,20 @@ void Snes9xCheats::show()
static void cheat_move(int src, int dst) 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) if (dst < src)
src++; src++;
Cheat.g.erase(Cheat.g.begin() + src); Cheat.group.erase(Cheat.group.begin() + src);
} }
static void cheat_gather_enabled() static void cheat_gather_enabled()
{ {
unsigned int enabled = 0; 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); cheat_move(i, enabled);
enabled++; enabled++;
@ -199,22 +199,21 @@ void Snes9xCheats::refresh_tree_view()
auto list_size = store->children().size(); auto list_size = store->children().size();
if (Cheat.g.size() == 0) if (Cheat.group.size() == 0)
return; 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(); store->append();
auto iter = store->children().begin(); 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); auto str = S9xCheatGroupToText(i);
Glib::ustring description = Cheat.g[i].name[0] == '\0' ? "" :Cheat.g[i].name; Glib::ustring description = Cheat.group[i].name[0] == '\0' ? "" :Cheat.group[i].name;
iter->set_value(COLUMN_ENABLED, Cheat.g[i].enabled); iter->set_value(COLUMN_ENABLED, Cheat.group[i].enabled);
iter->set_value(COLUMN_DESCRIPTION, description); iter->set_value(COLUMN_DESCRIPTION, description);
iter->set_value(COLUMN_CHEAT, Glib::ustring(str)); iter->set_value(COLUMN_CHEAT, Glib::ustring(str));
iter++; iter++;
delete[] str;
} }
enable_dnd(true); enable_dnd(true);
@ -228,15 +227,14 @@ void Snes9xCheats::add_code()
if (description.empty()) if (description.empty())
description = _("No description"); 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.")); display_errorbox(_("Couldn't find any cheat codes in input."));
return; return;
} }
auto parsed_code = S9xCheatGroupToText(Cheat.g.size() - 1); auto parsed_code = S9xCheatGroupToText(Cheat.group.size() - 1);
set_entry_text("code_entry", parsed_code); set_entry_text("code_entry", parsed_code);
delete[] parsed_code;
get_object<Gtk::Entry>("code_entry")->grab_focus(); 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(); auto selection = get_object<Gtk::TreeView>("cheat_treeview")->get_selection();
Gtk::TreePath path; Gtk::TreePath path;
path.push_back(Cheat.g.size() - 1); path.push_back(Cheat.group.size() - 1);
selection->select(path); selection->select(path);
auto adj = get_object<Gtk::ScrolledWindow>("cheat_scrolledwindow")->get_vadjustment(); auto adj = get_object<Gtk::ScrolledWindow>("cheat_scrolledwindow")->get_vadjustment();
@ -288,53 +286,27 @@ void Snes9xCheats::search_database()
int result; int result;
int reason = 0; int reason = 0;
filename = S9xGetDirectory(CHEAT_DIR); for (const auto &dir : { S9xGetDirectory(CHEAT_DIR),
filename += "/cheats.bml"; get_config_dir(),
if (!(result = S9xImportCheatsFromDatabase(filename.c_str()))) std::string(DATADIR) })
{ {
refresh_tree_view(); filename = dir + "/cheats.bml";
return; 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); 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 " 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.") "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 " : _("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.run();
dialog.hide(); dialog.hide();
} }
@ -348,12 +320,10 @@ void Snes9xCheats::sort_cheats()
void Snes9xCheats::row_activated(const Gtk::TreeModel::Path &path, Gtk::TreeViewColumn *column) void Snes9xCheats::row_activated(const Gtk::TreeModel::Path &path, Gtk::TreeViewColumn *column)
{ {
int index = get_index_from_path(path); 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); set_entry_text("code_entry", cheat_text);
delete[] cheat_text; set_entry_text("description_entry", Cheat.group[index].name);
set_entry_text("description_entry", Cheat.g[index].name);
} }
void Snes9xCheats::toggle_code(const Glib::ustring &path) void Snes9xCheats::toggle_code(const Glib::ustring &path)
@ -383,16 +353,15 @@ void Snes9xCheats::update_code()
if (description.empty()) if (description.empty())
description = _("No description"); description = _("No description");
auto parsed_code = S9xCheatValidate(code.c_str()); auto parsed_code = S9xCheatValidate(code);
if (!parsed_code) if (parsed_code.empty())
{ {
display_errorbox(_("Couldn't find any cheat codes in input.")); display_errorbox(_("Couldn't find any cheat codes in input."));
return; return;
} }
S9xModifyCheatGroup(index, description.c_str(), parsed_code); S9xModifyCheatGroup(index, description, parsed_code);
set_entry_text("code_entry", parsed_code); set_entry_text("code_entry", parsed_code);
delete[] parsed_code;
get_object<Gtk::Entry>("code_entry")->grab_focus(); get_object<Gtk::Entry>("code_entry")->grab_focus();
@ -401,9 +370,9 @@ void Snes9xCheats::update_code()
void Snes9xCheats::disable_all() 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); S9xDisableCheatGroup(i);
} }

View File

@ -98,7 +98,7 @@ std::string S9xGetDirectory(enum s9x_getdirtype dirtype)
} }
/* Anything else, use ROM filename path */ /* Anything else, use ROM filename path */
if (dirname == "" && gui_config && gui_config->rom_loaded) if (dirname == "" && !Memory.ROMFilename.empty())
{ {
fs::path path(Memory.ROMFilename); 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); int port = unzFindExtension(unzFile, msu_ext, true, true, true);
if (port == UNZ_OK) if (port == UNZ_OK)
{ {
printf(" in %s.\n", zip_filename.c_str());
file = new unzStream(unzFile); file = new unzStream(unzFile);
} }
else else

View File

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