mirror of
https://github.com/devinacker/bsnes-plus.git
synced 2025-04-02 10:52:46 -04:00
note: mirroring detection for cheats doesn't work for two addresses in the same 64kb bank i.e. the lowest 16 bits of both addresses must still be the same
171 lines
4.6 KiB
C++
171 lines
4.6 KiB
C++
#include <snes.hpp>
|
|
|
|
#define CHEAT_CPP
|
|
namespace SNES {
|
|
|
|
Cheat cheat;
|
|
|
|
bool Cheat::enabled() const {
|
|
return system_enabled;
|
|
}
|
|
|
|
void Cheat::enable(bool state) {
|
|
system_enabled = state;
|
|
cheat_enabled = system_enabled && code_enabled;
|
|
}
|
|
|
|
void Cheat::synchronize() {
|
|
memset(bitmask, 0x00, sizeof bitmask);
|
|
code_enabled = false;
|
|
|
|
for(unsigned i = 0; i < size(); i++) {
|
|
const CheatCode &code = operator[](i);
|
|
if(code.enabled == false) continue;
|
|
|
|
for(unsigned n = 0; n < code.addr.size(); n++) {
|
|
code_enabled = true;
|
|
|
|
uint16 addr = code.addr[n];
|
|
bitmask[addr >> 3] |= 1 << (addr & 7);
|
|
}
|
|
}
|
|
|
|
cheat_enabled = system_enabled && code_enabled;
|
|
}
|
|
|
|
bool Cheat::read(unsigned addr, uint8 &data, Bus &bus) const {
|
|
for(unsigned i = 0; i < size(); i++) {
|
|
const CheatCode &code = operator[](i);
|
|
if(code.enabled == false) continue;
|
|
|
|
for(unsigned n = 0; n < code.addr.size(); n++) {
|
|
if(bus.is_mirror(addr, code.addr[n])) {
|
|
data = code.data[n];
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
Cheat::Cheat() {
|
|
system_enabled = true;
|
|
synchronize();
|
|
}
|
|
|
|
//===============
|
|
//encode / decode
|
|
//===============
|
|
|
|
bool Cheat::decode(const char *s, unsigned &addr, uint8 &data, Type &type) {
|
|
string t = s;
|
|
t.lower();
|
|
|
|
#define ischr(n) ((n >= '0' && n <= '9') || (n >= 'a' && n <= 'f'))
|
|
|
|
if(strlen(t) == 8 || (strlen(t) == 9 && t[6] == ':')) {
|
|
//strip ':'
|
|
if(strlen(t) == 9 && t[6] == ':') t = string() << substr(t, 0, 6) << substr(t, 7);
|
|
//validate input
|
|
for(unsigned i = 0; i < 8; i++) if(!ischr(t[i])) return false;
|
|
|
|
type = Type::ProActionReplay;
|
|
unsigned r = hex((const char*)t);
|
|
addr = r >> 8;
|
|
data = r & 0xff;
|
|
return true;
|
|
} else if(strlen(t) == 9 && t[4] == '-') {
|
|
//strip '-'
|
|
t = string() << substr(t, 0, 4) << substr(t, 5);
|
|
//validate input
|
|
for(unsigned i = 0; i < 8; i++) if(!ischr(t[i])) return false;
|
|
|
|
type = Type::GameGenie;
|
|
t.transform("df4709156bc8a23e", "0123456789abcdef");
|
|
unsigned r = hex((const char*)t);
|
|
//8421 8421 8421 8421 8421 8421
|
|
//abcd efgh ijkl mnop qrst uvwx
|
|
//ijkl qrst opab cduv wxef ghmn
|
|
addr = (!!(r & 0x002000) << 23) | (!!(r & 0x001000) << 22)
|
|
| (!!(r & 0x000800) << 21) | (!!(r & 0x000400) << 20)
|
|
| (!!(r & 0x000020) << 19) | (!!(r & 0x000010) << 18)
|
|
| (!!(r & 0x000008) << 17) | (!!(r & 0x000004) << 16)
|
|
| (!!(r & 0x800000) << 15) | (!!(r & 0x400000) << 14)
|
|
| (!!(r & 0x200000) << 13) | (!!(r & 0x100000) << 12)
|
|
| (!!(r & 0x000002) << 11) | (!!(r & 0x000001) << 10)
|
|
| (!!(r & 0x008000) << 9) | (!!(r & 0x004000) << 8)
|
|
| (!!(r & 0x080000) << 7) | (!!(r & 0x040000) << 6)
|
|
| (!!(r & 0x020000) << 5) | (!!(r & 0x010000) << 4)
|
|
| (!!(r & 0x000200) << 3) | (!!(r & 0x000100) << 2)
|
|
| (!!(r & 0x000080) << 1) | (!!(r & 0x000040) << 0);
|
|
data = r >> 24;
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
|
|
#undef ischr
|
|
}
|
|
|
|
bool Cheat::encode(string &s, unsigned addr, uint8 data, Type type) {
|
|
char t[16];
|
|
|
|
if(type == Type::ProActionReplay) {
|
|
s = string(hex<6>(addr), hex<2>(data));
|
|
return true;
|
|
} else if(type == Type::GameGenie) {
|
|
unsigned r = addr;
|
|
addr = (!!(r & 0x008000) << 23) | (!!(r & 0x004000) << 22)
|
|
| (!!(r & 0x002000) << 21) | (!!(r & 0x001000) << 20)
|
|
| (!!(r & 0x000080) << 19) | (!!(r & 0x000040) << 18)
|
|
| (!!(r & 0x000020) << 17) | (!!(r & 0x000010) << 16)
|
|
| (!!(r & 0x000200) << 15) | (!!(r & 0x000100) << 14)
|
|
| (!!(r & 0x800000) << 13) | (!!(r & 0x400000) << 12)
|
|
| (!!(r & 0x200000) << 11) | (!!(r & 0x100000) << 10)
|
|
| (!!(r & 0x000008) << 9) | (!!(r & 0x000004) << 8)
|
|
| (!!(r & 0x000002) << 7) | (!!(r & 0x000001) << 6)
|
|
| (!!(r & 0x080000) << 5) | (!!(r & 0x040000) << 4)
|
|
| (!!(r & 0x020000) << 3) | (!!(r & 0x010000) << 2)
|
|
| (!!(r & 0x000800) << 1) | (!!(r & 0x000400) << 0);
|
|
s = string(hex<2>(data), hex<2>(addr >> 16), "-", hex<4>(addr & 0xffff));
|
|
s.transform("0123456789abcdef", "df4709156bc8a23e");
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
//=========
|
|
//CheatCode
|
|
//=========
|
|
|
|
bool CheatCode::operator=(string s) {
|
|
addr.reset();
|
|
data.reset();
|
|
|
|
lstring list;
|
|
list.split("+", s.replace(" ", ""));
|
|
|
|
for(unsigned i = 0; i < list.size(); i++) {
|
|
unsigned addr_;
|
|
uint8 data_;
|
|
Cheat::Type type_;
|
|
if(Cheat::decode(list[i], addr_, data_, type_) == false) {
|
|
addr.reset();
|
|
data.reset();
|
|
return false;
|
|
}
|
|
|
|
addr.append(addr_);
|
|
data.append(data_);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
CheatCode::CheatCode() {
|
|
enabled = false;
|
|
}
|
|
|
|
}
|