mirror of https://github.com/bsnes-emu/bsnes.git
195 lines
5.3 KiB
C++
Executable File
195 lines
5.3 KiB
C++
Executable File
#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;
|
|
|
|
unsigned addr = mirror(code.addr[n]);
|
|
bitmask[addr >> 3] |= 1 << (addr & 7);
|
|
if((addr & 0xffe000) == 0x7e0000) {
|
|
//mirror $7e:0000-1fff to $00-3f|80-bf:0000-1fff
|
|
unsigned mirroraddr;
|
|
for(unsigned x = 0; x <= 0x3f; x++) {
|
|
mirroraddr = ((0x00 + x) << 16) + (addr & 0x1fff);
|
|
bitmask[mirroraddr >> 3] |= 1 << (mirroraddr & 7);
|
|
|
|
mirroraddr = ((0x80 + x) << 16) + (addr & 0x1fff);
|
|
bitmask[mirroraddr >> 3] |= 1 << (mirroraddr & 7);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
cheat_enabled = system_enabled && code_enabled;
|
|
}
|
|
|
|
bool Cheat::read(unsigned addr, uint8 &data) const {
|
|
addr = mirror(addr);
|
|
|
|
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(addr == mirror(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 = strhex((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 = strhex((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(strhex<6>(addr), strhex<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(strhex<2>(data), strhex<2>(addr >> 16), "-", strhex<4>(addr & 0xffff));
|
|
s.transform("0123456789abcdef", "df4709156bc8a23e");
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
//========
|
|
//internal
|
|
//========
|
|
|
|
unsigned Cheat::mirror(unsigned addr) const {
|
|
//$00-3f|80-bf:0000-1fff -> $7e:0000-1fff
|
|
if((addr & 0x40e000) == 0x000000) return (0x7e0000 + (addr & 0x1fff));
|
|
return addr;
|
|
}
|
|
|
|
//=========
|
|
//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;
|
|
}
|
|
|
|
}
|