123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392 |
- #include <../base.hpp>
- Cheat cheat;
- Cheat::cheat_t& Cheat::cheat_t::operator=(const Cheat::cheat_t& source) {
- enabled = source.enabled;
- code = source.code;
- desc = source.desc;
- count = source.count;
- addr.reset();
- data.reset();
- for(unsigned n = 0; n < count; n++) {
- addr[n] = source.addr[n];
- data[n] = source.data[n];
- }
- return *this;
- }
- //used to sort cheat code list by description
- bool Cheat::cheat_t::operator<(const Cheat::cheat_t& source) {
- return strcmp(desc, source.desc) < 0;
- }
- //parse item ("0123-4567+89AB-CDEF"), return cheat_t item
- //return true if code is valid, false otherwise
- bool Cheat::decode(const char *s, Cheat::cheat_t &item) const {
- item.enabled = false;
- item.count = 0;
- lstring list;
- list.split("+", s);
- for(unsigned n = 0; n < list.size(); n++) {
- unsigned addr;
- uint8_t data;
- type_t type;
- if(decode(list[n], addr, data, type) == false) return false;
- item.addr[item.count] = addr;
- item.data[item.count] = data;
- item.count++;
- }
- return true;
- }
- //read() is used by MemBus::read() if Cheat::enabled(addr) returns true to look up cheat code.
- //returns true if cheat code was found, false if it was not.
- //when true, cheat code substitution value is stored in data.
- bool Cheat::read(unsigned addr, uint8_t &data) const {
- addr = mirror_address(addr);
- for(unsigned i = 0; i < code.size(); i++) {
- if(enabled(i) == false) continue;
- for(unsigned n = 0; n < code[i].count; n++) {
- if(addr == mirror_address(code[i].addr[n])) {
- data = code[i].data[n];
- return true;
- }
- }
- }
- //code not found, or code is disabled
- return false;
- }
- //==============
- //master control
- //==============
- //global cheat system enable/disable:
- //if disabled, *all* cheat codes are disabled;
- //otherwise only individually disabled codes are.
- bool Cheat::enabled() const {
- return cheat_system_enabled;
- }
- void Cheat::enable() {
- cheat_system_enabled = true;
- cheat_enabled = (cheat_system_enabled && cheat_enabled_code_exists);
- }
- void Cheat::disable() {
- cheat_system_enabled = false;
- cheat_enabled = false;
- }
- //================================
- //cheat list manipulation routines
- //================================
- bool Cheat::add(bool enable, const char *code_, const char *desc_) {
- cheat_t item;
- if(decode(code_, item) == false) return false;
- unsigned i = code.size();
- code[i] = item;
- code[i].enabled = enable;
- code[i].desc = desc_;
- code[i].code = code_;
- encode_description(code[i].desc);
- update(code[i]);
- update_cheat_status();
- return true;
- }
- bool Cheat::edit(unsigned i, bool enable, const char *code_, const char *desc_) {
- cheat_t item;
- if(decode(code_, item) == false) return false;
- //disable current code and clear from code lookup table
- code[i].enabled = false;
- update(code[i]);
- code[i] = item;
- code[i].enabled = enable;
- code[i].desc = desc_;
- code[i].code = code_;
- encode_description(code[i].desc);
- update(code[i]);
- update_cheat_status();
- return true;
- }
- bool Cheat::remove(unsigned i) {
- unsigned size = code.size();
- if(i >= size) return false; //also verifies size cannot be < 1
- for(unsigned n = i; n < size - 1; n++) code[n] = code[n + 1];
- code.resize(size - 1);
- update_cheat_status();
- return true;
- }
- bool Cheat::get(unsigned i, cheat_t &item) const {
- if(i >= code.size()) return false;
- item = code[i];
- decode_description(item.desc);
- return true;
- }
- //==============================
- //cheat status modifier routines
- //==============================
- bool Cheat::enabled(unsigned i) const {
- return (i < code.size() ? code[i].enabled : false);
- }
- void Cheat::enable(unsigned i) {
- if(i >= code.size()) return;
- code[i].enabled = true;
- update(code[i]);
- update_cheat_status();
- }
- void Cheat::disable(unsigned i) {
- if(i >= code.size()) return;
- code[i].enabled = false;
- update(code[i]);
- update_cheat_status();
- }
- //===============================
- //cheat file load / save routines
- //
- //file format:
- //"description", status, nnnn-nnnn[+nnnn-nnnn...]\r\n
- //...
- //===============================
- bool Cheat::load(const char *fn) {
- string data;
- if(!data.readfile(fn)) return false;
- data.replace("\r\n", "\n");
- data.qreplace(" ", "");
- lstring line;
- line.split("\n", data);
- for(unsigned i = 0; i < line.size(); i++) {
- lstring part;
- part.qsplit(",", line[i]);
- if(part.size() != 3) continue;
- trim(part[0], "\"");
- add(part[1] == "enabled", /* code = */ part[2], /* desc = */ part[0]);
- }
- return true;
- }
- bool Cheat::save(const char *fn) const {
- file fp;
- if(!fp.open(fn, file::mode_write)) return false;
- for(unsigned i = 0; i < code.size(); i++) {
- fp.print(string()
- << "\"" << code[i].desc << "\", "
- << (code[i].enabled ? "enabled, " : "disabled, ")
- << code[i].code << "\r\n");
- }
- fp.close();
- return true;
- }
- void Cheat::clear() {
- cheat_enabled_code_exists = false;
- memset(mask, 0, 0x200000);
- code.reset();
- }
- Cheat::Cheat() : cheat_system_enabled(true) {
- clear();
- }
- //==================
- //internal functions
- //==================
- //string <> binary code translation routines
- //decode() "7e123456" -> 0x7e123456
- //encode() 0x7e123456 -> "7e123456"
- bool Cheat::decode(const char *s, unsigned &addr, uint8_t &data, type_t &type) const {
- string t = s;
- strlower(t);
- #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 = 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 = GameGenie;
- strtr(t, "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;
- }
- }
- bool Cheat::encode(string &s, unsigned addr, uint8_t data, type_t type) const {
- char t[16];
- if(type == ProActionReplay) {
- sprintf(t, "%.6x%.2x", addr, data);
- s = t;
- return true;
- } else if(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);
- sprintf(t, "%.2x%.2x-%.4x", data, addr >> 16, addr & 0xffff);
- strtr(t, "0123456789abcdef", "df4709156bc8a23e");
- s = t;
- return true;
- } else {
- return false;
- }
- }
- //speed up S-CPU memory reads by disabling cheat code lookup when either:
- //a) cheat system is disabled by user, or b) no enabled cheat codes exist
- void Cheat::update_cheat_status() {
- for(unsigned i = 0; i < code.size(); i++) {
- if(code[i].enabled) {
- cheat_enabled_code_exists = true;
- cheat_enabled = (cheat_system_enabled && cheat_enabled_code_exists);
- return;
- }
- }
- cheat_enabled_code_exists = false;
- cheat_enabled = false;
- }
- //address lookup table manipulation and mirroring
- //mirror_address() 0x000000 -> 0x7e0000
- //set() enable specified address, mirror accordingly
- //clear() disable specified address, mirror accordingly
- unsigned Cheat::mirror_address(unsigned addr) const {
- if((addr & 0x40e000) != 0x0000) return addr;
- //8k WRAM mirror
- //$[00-3f|80-bf]:[0000-1fff] -> $7e:[0000-1fff]
- return (0x7e0000 + (addr & 0x1fff));
- }
- //updates mask[] table enabled bits;
- //must be called after modifying item.enabled state.
- void Cheat::update(const cheat_t &item) {
- for(unsigned n = 0; n < item.count; n++) {
- (item.enabled) ? set(item.addr[n]) : clear(item.addr[n]);
- }
- }
- void Cheat::set(unsigned addr) {
- addr = mirror_address(addr);
- mask[addr >> 3] |= 1 << (addr & 7);
- if((addr & 0xffe000) == 0x7e0000) {
- //mirror $7e:[0000-1fff] to $[00-3f|80-bf]:[0000-1fff]
- unsigned mirror;
- for(unsigned x = 0; x <= 0x3f; x++) {
- mirror = ((0x00 + x) << 16) + (addr & 0x1fff);
- mask[mirror >> 3] |= 1 << (mirror & 7);
- mirror = ((0x80 + x) << 16) + (addr & 0x1fff);
- mask[mirror >> 3] |= 1 << (mirror & 7);
- }
- }
- }
- void Cheat::clear(unsigned addr) {
- addr = mirror_address(addr);
- //if there is more than one cheat code using the same address,
- //(eg with a different override value) then do not clear code
- //lookup table entry.
- uint8_t r;
- if(read(addr, r) == true) return;
- mask[addr >> 3] &= ~(1 << (addr & 7));
- if((addr & 0xffe000) == 0x7e0000) {
- //mirror $7e:[0000-1fff] to $[00-3f|80-bf]:[0000-1fff]
- unsigned mirror;
- for(unsigned x = 0; x <= 0x3f; x++) {
- mirror = ((0x00 + x) << 16) + (addr & 0x1fff);
- mask[mirror >> 3] &= ~(1 << (mirror & 7));
- mirror = ((0x80 + x) << 16) + (addr & 0x1fff);
- mask[mirror >> 3] &= ~(1 << (mirror & 7));
- }
- }
- }
- //these two functions are used to safely store description text inside .cfg file format.
- string& Cheat::encode_description(string &desc) const {
- desc.replace("\"", "\\q");
- desc.replace("\n", "\\n");
- return desc;
- }
- string& Cheat::decode_description(string &desc) const {
- desc.replace("\\q", "\"");
- desc.replace("\\n", "\n");
- return desc;
- }
|