mirror of https://github.com/snes9xgit/snes9x.git
Modernize cheat stuff.
This commit is contained in:
parent
9ed560de2f
commit
2e39f2f477
106
cheats.cpp
106
cheats.cpp
|
@ -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);
|
||||||
|
|
38
cheats.h
38
cheats.h
|
@ -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
|
||||||
|
|
570
cheats2.cpp
570
cheats2.cpp
|
@ -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 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 *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())
|
||||||
|
{
|
||||||
SCheat c = S9xTextToCheat (code);
|
tokens.push_back(trim(str.substr(pos)));
|
||||||
if (c.address)
|
|
||||||
g.c.push_back (c);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
free(code_string);
|
break;
|
||||||
|
}
|
||||||
|
else if (index > pos)
|
||||||
|
{
|
||||||
|
tokens.push_back(trim(str.substr(pos, index - pos)));
|
||||||
|
}
|
||||||
|
|
||||||
|
pos = index + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
|
||||||
fprintf (file,
|
|
||||||
"cheat\n"
|
"cheat\n"
|
||||||
" name: %s\n"
|
" name: {}\n"
|
||||||
" code: %s\n"
|
" code: {}\n"
|
||||||
"%s\n",
|
"{}\n",
|
||||||
Cheat.g[i].name ? Cheat.g[i].name : "",
|
Cheat.group[i].name,
|
||||||
txt,
|
S9xCheatGroupToText(i),
|
||||||
Cheat.g[i].enabled ? " enable\n" : ""
|
Cheat.group[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,22 +761,19 @@ 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);
|
||||||
{
|
|
||||||
S9xLoadCheatsFromBMLNode (&bml.child[i]);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return -2; /* No codes */
|
return -2; /* No codes */
|
||||||
}
|
}
|
||||||
|
|
4
cpu.cpp
4
cpu.cpp
|
@ -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();
|
||||||
|
|
|
@ -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,9 +286,13 @@ 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) })
|
||||||
|
{
|
||||||
|
filename = dir + "/cheats.bml";
|
||||||
|
result = S9xImportCheatsFromDatabase(filename);
|
||||||
|
if (result == 0)
|
||||||
{
|
{
|
||||||
refresh_tree_view();
|
refresh_tree_view();
|
||||||
return;
|
return;
|
||||||
|
@ -298,43 +300,13 @@ void Snes9xCheats::search_database()
|
||||||
|
|
||||||
if (result < reason)
|
if (result < reason)
|
||||||
reason = result;
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
1
msu1.cpp
1
msu1.cpp
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue