diff --git a/bsnes/gameboy/Makefile b/bsnes/gameboy/Makefile index 82d356114..273620249 100755 --- a/bsnes/gameboy/Makefile +++ b/bsnes/gameboy/Makefile @@ -1,6 +1,7 @@ gameboy_objects := gameboy-system gameboy-scheduler gameboy_objects += gameboy-memory gameboy-cartridge gameboy_objects += gameboy-cpu gameboy-apu gameboy-lcd +gameboy_objects += gameboy-cheat objects += $(gameboy_objects) obj/gameboy-system.o: $(gameboy)/system/system.cpp $(call rwildcard,$(gameboy)/system/) @@ -10,3 +11,4 @@ obj/gameboy-memory.o: $(gameboy)/memory/memory.cpp $(call rwildcard,$(gameboy)/m obj/gameboy-cpu.o: $(gameboy)/cpu/cpu.cpp $(call rwildcard,$(gameboy)/cpu/) obj/gameboy-apu.o: $(gameboy)/apu/apu.cpp $(call rwildcard,$(gameboy)/apu/) obj/gameboy-lcd.o: $(gameboy)/lcd/lcd.cpp $(call rwildcard,$(gameboy)/lcd/) +obj/gameboy-cheat.o: $(gameboy)/cheat/cheat.cpp $(call rwildcard,$(gameboy)/cheat/) diff --git a/bsnes/gameboy/cheat/cheat.cpp b/bsnes/gameboy/cheat/cheat.cpp new file mode 100755 index 000000000..e43fa0a7a --- /dev/null +++ b/bsnes/gameboy/cheat/cheat.cpp @@ -0,0 +1,91 @@ +#include + +namespace GameBoy { + +Cheat cheat; + +bool Cheat::decode(const string &code, unsigned &addr, unsigned &data, unsigned &comp) { + static bool initialize = false; + static uint8 asciiMap[256]; + + if(initialize == false) { + initialize = true; + foreach(n, asciiMap) n = ~0; + asciiMap['0'] = 0; asciiMap['1'] = 1; asciiMap['2'] = 2; asciiMap['3'] = 3; + asciiMap['4'] = 4; asciiMap['5'] = 5; asciiMap['6'] = 6; asciiMap['7'] = 7; + asciiMap['8'] = 8; asciiMap['9'] = 9; asciiMap['A'] = 10; asciiMap['B'] = 11; + asciiMap['C'] = 12; asciiMap['D'] = 13; asciiMap['E'] = 14; asciiMap['F'] = 15; + } + + unsigned length = code.length(), bits = 0; + for(unsigned n = 0; n < length; n++) if(asciiMap[code[n]] > 15 && code[n] != '-') return false; + + if(code.wildcard("???" "-" "???")) { + string text = string(code).replace("-", ""); + for(unsigned n = 0; n < 6; n++) bits |= asciiMap[text[n]] << (20 - n * 4); + + addr = (bits >> 0) & 0xffff; + data = (bits >> 16) & 0xff; + comp = ~0; + + addr = (((addr >> 4) | (addr << 12)) & 0xffff) ^ 0xf000; + + return true; + } + + if(code.wildcard("???" "-" "???" "-" "???")) { + string text = string(code).replace("-", ""); + for(unsigned n = 0; n < 8; n++) bits |= asciiMap[text[n == 7 ? 8 : n]] << (28 - n * 4); + + addr = (bits >> 8) & 0xffff; + data = (bits >> 24) & 0xff; + comp = (bits >> 0) & 0xff; + + addr = (((addr >> 4) | (addr << 12)) & 0xffff) ^ 0xf000; + comp = (((comp >> 2) | (comp << 6)) & 0xff) ^ 0xba; + + return true; + } + + return false; +} + +void Cheat::synchronize() { + foreach(n, override) n = false; + + for(unsigned n = 0; n < size(); n++) { + const CheatCode &code = operator[](n); + + for(unsigned n = 0; n < code.addr.size(); n++) { + override[code.addr[n]] = true; + } + } +} + +bool CheatCode::operator=(const string &s) { + addr.reset(), data.reset(), comp.reset(); + + lstring list; + list.split("+", s); + + for(unsigned n = 0; n < list.size(); n++) { + unsigned codeaddr, codedata, codecomp; + if(Cheat::decode(list[n], codeaddr, codedata, codecomp) == false) { + addr.reset(), data.reset(), comp.reset(); + return false; + } + addr.append(codeaddr), data.append(codedata), comp.append(codecomp); + } + + return true; +} + +CheatCode::CheatCode(const string &s) { + operator=(s); +} + +CheatCode::CheatCode() { + enable = false; +} + +} diff --git a/bsnes/gameboy/cheat/cheat.hpp b/bsnes/gameboy/cheat/cheat.hpp new file mode 100755 index 000000000..2899cc46d --- /dev/null +++ b/bsnes/gameboy/cheat/cheat.hpp @@ -0,0 +1,19 @@ +struct CheatCode { + bool enable; + array addr; + array data; + array comp; + + bool operator=(const string&); + CheatCode(const string&); + CheatCode(); +}; + +struct Cheat : public linear_vector { + static bool decode(const string &code, unsigned &addr, unsigned &data, unsigned &comp); + + void synchronize(); + bool override[65536]; +}; + +extern Cheat cheat; diff --git a/bsnes/gameboy/gameboy.hpp b/bsnes/gameboy/gameboy.hpp index b0de9e028..df2d742b0 100755 --- a/bsnes/gameboy/gameboy.hpp +++ b/bsnes/gameboy/gameboy.hpp @@ -99,6 +99,7 @@ namespace GameBoy { #include #include #include + #include }; #endif diff --git a/bsnes/gameboy/memory/memory.cpp b/bsnes/gameboy/memory/memory.cpp index 0f88290f9..c9d148267 100755 --- a/bsnes/gameboy/memory/memory.cpp +++ b/bsnes/gameboy/memory/memory.cpp @@ -42,7 +42,22 @@ Memory::~Memory() { // uint8 Bus::read(uint16 addr) { - return mmio[addr]->mmio_read(addr); + uint8 data = mmio[addr]->mmio_read(addr); + + if(cheat.override[addr]) { + for(unsigned x = 0; x < cheat.size(); x++) { + const CheatCode &code = cheat[x]; + for(unsigned y = 0; y < code.addr.size(); y++) { + if(code.addr[y] == addr) { + if(code.comp[y] > 255 || code.comp[y] == data) { + return code.data[y]; + } + } + } + } + } + + return data; } void Bus::write(uint16 addr, uint8 data) { diff --git a/bsnes/gameboy/system/system.cpp b/bsnes/gameboy/system/system.cpp index d78a16927..0e0864108 100755 --- a/bsnes/gameboy/system/system.cpp +++ b/bsnes/gameboy/system/system.cpp @@ -49,6 +49,11 @@ void System::power() { lcd.power(); scheduler.init(); +// cheat.reset(); +// cheat.append(CheatCode("3EB-81B-4CA")); +// cheat[0].enable = true; +// cheat.synchronize(); + clocks_executed = 0; } diff --git a/bsnes/nes/Makefile b/bsnes/nes/Makefile index 84b9fc7e4..6216d0671 100755 --- a/bsnes/nes/Makefile +++ b/bsnes/nes/Makefile @@ -1,6 +1,7 @@ nes_objects := nes-system nes-scheduler nes_objects += nes-mapper nes-cartridge nes-memory nes_objects += nes-cpu nes-apu nes-ppu +nes_objects += nes-cheat objects += $(nes_objects) obj/nes-system.o: $(nes)/system/system.cpp $(call rwildcard,$(nes)/system/) @@ -11,3 +12,4 @@ obj/nes-memory.o: $(nes)/memory/memory.cpp $(call rwildcard,$(nes)/memory/) obj/nes-cpu.o: $(nes)/cpu/cpu.cpp $(call rwildcard,$(nes)/cpu/) obj/nes-apu.o: $(nes)/apu/apu.cpp $(call rwildcard,$(nes)/apu/) obj/nes-ppu.o: $(nes)/ppu/ppu.cpp $(call rwildcard,$(nes)/ppu/) +obj/nes-cheat.o: $(nes)/cheat/cheat.cpp $(call rwildcard,$(nes)/cheat/) diff --git a/bsnes/nes/cartridge/cartridge.cpp b/bsnes/nes/cartridge/cartridge.cpp index 77792733f..256af6b0f 100755 --- a/bsnes/nes/cartridge/cartridge.cpp +++ b/bsnes/nes/cartridge/cartridge.cpp @@ -31,8 +31,9 @@ void Cartridge::load(const string &xml, const uint8_t *data, unsigned size) { switch(mapperNumber) { default : mapper = &Mapper::none; break; case 1: mapper = &Mapper::mmc1; break; - case 7: mapper = &Mapper::sn74hc161n; break; - case 16: mapper = &Mapper::lz93d50; break; + case 3: mapper = &Mapper::cnrom; break; + case 7: mapper = &Mapper::aorom; break; + case 16: mapper = &Mapper::bandaiFCG; break; } loaded = true; diff --git a/bsnes/nes/cheat/cheat.cpp b/bsnes/nes/cheat/cheat.cpp new file mode 100755 index 000000000..84f6e5c31 --- /dev/null +++ b/bsnes/nes/cheat/cheat.cpp @@ -0,0 +1,88 @@ +#include + +namespace NES { + +Cheat cheat; + +bool Cheat::decode(const string &code, unsigned &addr, unsigned &data, unsigned &comp) { + static bool initialize = false; + static uint8 asciiMap[256]; + + if(initialize == false) { + initialize = true; + foreach(n, asciiMap) n = ~0; + asciiMap['A'] = 0; asciiMap['P'] = 1; asciiMap['Z'] = 2; asciiMap['L'] = 3; + asciiMap['G'] = 4; asciiMap['I'] = 5; asciiMap['T'] = 6; asciiMap['Y'] = 7; + asciiMap['E'] = 8; asciiMap['O'] = 9; asciiMap['X'] = 10; asciiMap['U'] = 11; + asciiMap['K'] = 12; asciiMap['S'] = 13; asciiMap['V'] = 14; asciiMap['N'] = 15; + } + + unsigned length = code.length(), bits = 0; + for(unsigned n = 0; n < length; n++) if(asciiMap[code[n]] > 15) return false; + + if(code.length() == 6) { + for(unsigned n = 0; n < 6; n++) bits |= asciiMap[code[n]] << (20 - n * 4); + unsigned addrTable[] = { 10, 9, 8, 7, 2, 1, 0, 19, 14, 13, 12, 11, 6, 5, 4 }; + unsigned dataTable[] = { 23, 18, 17, 16, 3, 22, 21, 20 }; + + addr = 0x8000, data = 0x00, comp = ~0; + for(unsigned n = 0; n < 15; n++) addr |= bits & (1 << addrTable[n]) ? 0x4000 >> n : 0; + for(unsigned n = 0; n < 8; n++) data |= bits & (1 << dataTable[n]) ? 0x80 >> n : 0; + return true; + } + + if(code.length() == 8) { + for(unsigned n = 0; n < 8; n++) bits |= asciiMap[code[n]] << (28 - n * 4); + unsigned addrTable[] = { 18, 17, 16, 15, 10, 9, 8, 27, 22, 21, 20, 19, 14, 13, 12 }; + unsigned dataTable[] = { 31, 26, 25, 24, 3, 30, 29, 28 }; + unsigned compTable[] = { 7, 2, 1, 0, 11, 6, 5,4 }; + + addr = 0x8000, data = 0x00, comp = 0x00; + for(unsigned n = 0; n < 15; n++) addr |= bits & (1 << addrTable[n]) ? 0x4000 >> n : 0; + for(unsigned n = 0; n < 8; n++) data |= bits & (1 << dataTable[n]) ? 0x80 >> n : 0; + for(unsigned n = 0; n < 8; n++) comp |= bits & (1 << compTable[n]) ? 0x80 >> n : 0; + return true; + } + + return false; +} + +void Cheat::synchronize() { + foreach(n, override) n = false; + + for(unsigned n = 0; n < size(); n++) { + const CheatCode &code = operator[](n); + + for(unsigned n = 0; n < code.addr.size(); n++) { + override[code.addr[n]] = true; + } + } +} + +bool CheatCode::operator=(const string &s) { + addr.reset(), data.reset(), comp.reset(); + + lstring list; + list.split("+", s); + + for(unsigned n = 0; n < list.size(); n++) { + unsigned codeaddr, codedata, codecomp; + if(Cheat::decode(list[n], codeaddr, codedata, codecomp) == false) { + addr.reset(), data.reset(), comp.reset(); + return false; + } + addr.append(codeaddr), data.append(codedata), comp.append(codecomp); + } + + return true; +} + +CheatCode::CheatCode(const string &s) { + operator=(s); +} + +CheatCode::CheatCode() { + enable = false; +} + +} diff --git a/bsnes/nes/cheat/cheat.hpp b/bsnes/nes/cheat/cheat.hpp new file mode 100755 index 000000000..2899cc46d --- /dev/null +++ b/bsnes/nes/cheat/cheat.hpp @@ -0,0 +1,19 @@ +struct CheatCode { + bool enable; + array addr; + array data; + array comp; + + bool operator=(const string&); + CheatCode(const string&); + CheatCode(); +}; + +struct Cheat : public linear_vector { + static bool decode(const string &code, unsigned &addr, unsigned &data, unsigned &comp); + + void synchronize(); + bool override[65536]; +}; + +extern Cheat cheat; diff --git a/bsnes/nes/mapper/sn74hc161n/sn74hc161n.cpp b/bsnes/nes/mapper/aorom/aorom.cpp similarity index 55% rename from bsnes/nes/mapper/sn74hc161n/sn74hc161n.cpp rename to bsnes/nes/mapper/aorom/aorom.cpp index 70b20cf94..8c0564d77 100755 --- a/bsnes/nes/mapper/sn74hc161n/sn74hc161n.cpp +++ b/bsnes/nes/mapper/aorom/aorom.cpp @@ -1,10 +1,6 @@ -//TODO: this naming nomenclature does not work. -//AOROM and CNROM use this same chip in different pin configurations. -//this emulates AOROM currently ... +AOROM aorom; -SN74HC161N sn74hc161n; - -uint8 SN74HC161N::prg_read(uint16 addr) { +uint8 AOROM::prg_read(uint16 addr) { if(addr & 0x8000) { unsigned rom_addr = (prg_bank << 15) | (addr & 0x7fff); return cartridge.prg_data[mirror(rom_addr, cartridge.prg_size)]; @@ -13,35 +9,35 @@ uint8 SN74HC161N::prg_read(uint16 addr) { return cpu.mdr(); } -void SN74HC161N::prg_write(uint16 addr, uint8 data) { +void AOROM::prg_write(uint16 addr, uint8 data) { if(addr & 0x8000) { prg_bank = data & 0x0f; mirror_select = data & 0x10; } } -uint8 SN74HC161N::chr_read(uint16 addr) { +uint8 AOROM::chr_read(uint16 addr) { return cartridge.chr_data[mirror(addr, cartridge.chr_size)]; } -void SN74HC161N::chr_write(uint16 addr, uint8 data) { +void AOROM::chr_write(uint16 addr, uint8 data) { if(cartridge.chr_ram == false) return; cartridge.chr_data[mirror(addr, cartridge.chr_size)] = data; } -uint8 SN74HC161N::ciram_read(uint13 addr) { +uint8 AOROM::ciram_read(uint13 addr) { return ppu.ciram_read((mirror_select << 10) | (addr & 0x03ff)); } -void SN74HC161N::ciram_write(uint13 addr, uint8 data) { +void AOROM::ciram_write(uint13 addr, uint8 data) { return ppu.ciram_write((mirror_select << 10) | (addr & 0x03ff), data); } -void SN74HC161N::power() { +void AOROM::power() { reset(); } -void SN74HC161N::reset() { +void AOROM::reset() { prg_bank = 0x0f; mirror_select = 0; } diff --git a/bsnes/nes/mapper/sn74hc161n/sn74hc161n.hpp b/bsnes/nes/mapper/aorom/aorom.hpp similarity index 84% rename from bsnes/nes/mapper/sn74hc161n/sn74hc161n.hpp rename to bsnes/nes/mapper/aorom/aorom.hpp index 49b9cdaa7..86837148a 100755 --- a/bsnes/nes/mapper/sn74hc161n/sn74hc161n.hpp +++ b/bsnes/nes/mapper/aorom/aorom.hpp @@ -1,4 +1,4 @@ -struct SN74HC161N : Mapper { +struct AOROM : Mapper { uint8 prg_read(uint16 addr); void prg_write(uint16 addr, uint8 data); @@ -16,4 +16,4 @@ private: bool mirror_select; }; -extern SN74HC161N sn74hc161n; +extern AOROM aorom; diff --git a/bsnes/nes/mapper/lz93d50/lz93d50.cpp b/bsnes/nes/mapper/bandai-fcg/bandai-fcg.cpp similarity index 83% rename from bsnes/nes/mapper/lz93d50/lz93d50.cpp rename to bsnes/nes/mapper/bandai-fcg/bandai-fcg.cpp index c56366c5d..ae3fe4a90 100755 --- a/bsnes/nes/mapper/lz93d50/lz93d50.cpp +++ b/bsnes/nes/mapper/bandai-fcg/bandai-fcg.cpp @@ -1,6 +1,6 @@ -LZ93D50 lz93d50; +BandaiFCG bandaiFCG; -uint8 LZ93D50::prg_read(uint16 addr) { +uint8 BandaiFCG::prg_read(uint16 addr) { clock(); if(addr >= 0x8000 && addr <= 0xbfff) { @@ -16,7 +16,7 @@ uint8 LZ93D50::prg_read(uint16 addr) { return cpu.mdr(); } -void LZ93D50::prg_write(uint16 addr, uint8 data) { +void BandaiFCG::prg_write(uint16 addr, uint8 data) { clock(); if(addr >= 0x6000) { @@ -49,32 +49,32 @@ void LZ93D50::prg_write(uint16 addr, uint8 data) { } } -uint8 LZ93D50::chr_read(uint16 addr) { +uint8 BandaiFCG::chr_read(uint16 addr) { unsigned rom_addr = (chr_bank[addr >> 10] << 10) | (addr & 0x03ff); return cartridge.chr_data[mirror(rom_addr, cartridge.chr_size)]; } -void LZ93D50::chr_write(uint16 addr, uint8 data) { +void BandaiFCG::chr_write(uint16 addr, uint8 data) { if(cartridge.chr_ram == false) return; unsigned rom_addr = (chr_bank[addr >> 10] << 10) | (addr & 0x03ff); cartridge.chr_data[mirror(rom_addr, cartridge.chr_size)] = data; } -uint8 LZ93D50::ciram_read(uint13 addr) { +uint8 BandaiFCG::ciram_read(uint13 addr) { addr = ciram_addr(addr); return ppu.ciram_read(addr); } -void LZ93D50::ciram_write(uint13 addr, uint8 data) { +void BandaiFCG::ciram_write(uint13 addr, uint8 data) { addr = ciram_addr(addr); return ppu.ciram_write(addr, data); } -void LZ93D50::power() { +void BandaiFCG::power() { reset(); } -void LZ93D50::reset() { +void BandaiFCG::reset() { for(unsigned n = 0; n < 8; n++) chr_bank[n] = 0x00; prg_bank = 0x00; mirror_select = 0; @@ -85,7 +85,7 @@ void LZ93D50::reset() { // -unsigned LZ93D50::ciram_addr(unsigned addr) const { +unsigned BandaiFCG::ciram_addr(unsigned addr) const { switch(mirror_select & 0x03) { case 0: return ((addr & 0x0400) >> 0) | (addr & 0x03ff); //vertical mirroring case 1: return ((addr & 0x0800) >> 1) | (addr & 0x03ff); //horizontal mirroring @@ -95,7 +95,7 @@ unsigned LZ93D50::ciram_addr(unsigned addr) const { throw; } -void LZ93D50::clock() { +void BandaiFCG::clock() { if(irq_counter_enable) { if(--irq_counter == 0xffff) { cpu.set_irq_line(1); diff --git a/bsnes/nes/mapper/lz93d50/lz93d50.hpp b/bsnes/nes/mapper/bandai-fcg/bandai-fcg.hpp similarity index 89% rename from bsnes/nes/mapper/lz93d50/lz93d50.hpp rename to bsnes/nes/mapper/bandai-fcg/bandai-fcg.hpp index 243701148..6b436cea4 100755 --- a/bsnes/nes/mapper/lz93d50/lz93d50.hpp +++ b/bsnes/nes/mapper/bandai-fcg/bandai-fcg.hpp @@ -1,4 +1,4 @@ -struct LZ93D50 : Mapper { +struct BandaiFCG : Mapper { uint8 prg_read(uint16 addr); void prg_write(uint16 addr, uint8 data); @@ -23,4 +23,4 @@ private: uint16 irq_latch; }; -extern LZ93D50 lz93d50; +extern BandaiFCG bandaiFCG; diff --git a/bsnes/nes/mapper/cnrom/cnrom.cpp b/bsnes/nes/mapper/cnrom/cnrom.cpp new file mode 100755 index 000000000..4ad7bed5e --- /dev/null +++ b/bsnes/nes/mapper/cnrom/cnrom.cpp @@ -0,0 +1,42 @@ +CNROM cnrom; + +uint8 CNROM::prg_read(uint16 addr) { + if(addr & 0x8000) { + return cartridge.prg_data[mirror(addr & 0x7fff, cartridge.prg_size)]; + } + + return cpu.mdr(); +} + +void CNROM::prg_write(uint16 addr, uint8 data) { + if(addr & 0x8000) chr_bank = data & 0x03; +} + +uint8 CNROM::chr_read(uint16 addr) { + unsigned chr_addr = (chr_bank * 0x2000) + (addr & 0x1fff); + return cartridge.chr_data[mirror(chr_addr, cartridge.chr_size)]; +} + +void CNROM::chr_write(uint16 addr, uint8 data) { + if(cartridge.chr_ram == false) return; + unsigned chr_addr = (chr_bank * 0x2000) + (addr & 0x1fff); + cartridge.chr_data[mirror(chr_addr, cartridge.chr_size)] = data; +} + +uint8 CNROM::ciram_read(uint13 addr) { + if(cartridge.mirroring == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff); + return ppu.ciram_read(addr & 0x07ff); +} + +void CNROM::ciram_write(uint13 addr, uint8 data) { + if(cartridge.mirroring == 0) addr = ((addr & 0x0800) >> 1) | (addr & 0x03ff); + return ppu.ciram_write(addr & 0x07ff, data); +} + +void CNROM::power() { + reset(); +} + +void CNROM::reset() { + chr_bank = 0; +} diff --git a/bsnes/nes/mapper/cnrom/cnrom.hpp b/bsnes/nes/mapper/cnrom/cnrom.hpp new file mode 100755 index 000000000..47f095270 --- /dev/null +++ b/bsnes/nes/mapper/cnrom/cnrom.hpp @@ -0,0 +1,18 @@ +struct CNROM : Mapper { + uint8 prg_read(uint16 addr); + void prg_write(uint16 addr, uint8 data); + + uint8 chr_read(uint16 addr); + void chr_write(uint16 addr, uint8 data); + + uint8 ciram_read(uint13 addr); + void ciram_write(uint13 addr, uint8 data); + + void power(); + void reset(); + +private: + uint2 chr_bank; +}; + +extern CNROM cnrom; diff --git a/bsnes/nes/mapper/mapper.cpp b/bsnes/nes/mapper/mapper.cpp index d86aecef7..fe41b5d93 100755 --- a/bsnes/nes/mapper/mapper.cpp +++ b/bsnes/nes/mapper/mapper.cpp @@ -22,9 +22,10 @@ namespace Mapper { } #include "none/none.cpp" - #include "lz93d50/lz93d50.cpp" + #include "aorom/aorom.cpp" + #include "bandai-fcg/bandai-fcg.cpp" + #include "cnrom/cnrom.cpp" #include "mmc1/mmc1.cpp" - #include "sn74hc161n/sn74hc161n.cpp" } } diff --git a/bsnes/nes/mapper/mapper.hpp b/bsnes/nes/mapper/mapper.hpp index 92474745f..be3ff05fd 100755 --- a/bsnes/nes/mapper/mapper.hpp +++ b/bsnes/nes/mapper/mapper.hpp @@ -16,7 +16,8 @@ namespace Mapper { }; #include "none/none.hpp" - #include "lz93d50/lz93d50.hpp" + #include "aorom/aorom.hpp" + #include "bandai-fcg/bandai-fcg.hpp" + #include "cnrom/cnrom.hpp" #include "mmc1/mmc1.hpp" - #include "sn74hc161n/sn74hc161n.hpp" } diff --git a/bsnes/nes/memory/memory.cpp b/bsnes/nes/memory/memory.cpp index 9dfe3113b..09e7c9907 100755 --- a/bsnes/nes/memory/memory.cpp +++ b/bsnes/nes/memory/memory.cpp @@ -16,6 +16,20 @@ uint8 Bus::read(uint16 addr) { if(addr <= 0x1fff) return cpu.ram_read(addr); if(addr <= 0x3fff) return ppu.read(addr); if(addr <= 0x4017) return cpu.read(addr); + + if(cheat.override[addr]) { + for(unsigned x = 0; x < cheat.size(); x++) { + const CheatCode &code = cheat[x]; + for(unsigned y = 0; y < code.addr.size(); y++) { + if(code.addr[y] == addr) { + if(code.comp[y] > 255 || code.comp[y] == data) { + return code.data[y]; + } + } + } + } + } + return data; } diff --git a/bsnes/nes/nes.hpp b/bsnes/nes/nes.hpp index ed9c9c7cb..727476f17 100755 --- a/bsnes/nes/nes.hpp +++ b/bsnes/nes/nes.hpp @@ -4,7 +4,7 @@ namespace NES { namespace Info { static const char Name[] = "bnes"; - static const char Version[] = "000.05"; + static const char Version[] = "000.06"; } } @@ -107,6 +107,7 @@ namespace NES { #include #include #include + #include } #endif diff --git a/bsnes/nes/system/system.cpp b/bsnes/nes/system/system.cpp index d91372753..2b5893bae 100755 --- a/bsnes/nes/system/system.cpp +++ b/bsnes/nes/system/system.cpp @@ -14,6 +14,11 @@ void System::power() { apu.power(); ppu.power(); scheduler.power(); + +// cheat.reset(); +// cheat.append(CheatCode("GXXZZLVI")); +// cheat[0].enable = true; +// cheat.synchronize(); } void System::reset() { diff --git a/bsnes/snes/snes.hpp b/bsnes/snes/snes.hpp index 417bd1c3b..84bee8610 100755 --- a/bsnes/snes/snes.hpp +++ b/bsnes/snes/snes.hpp @@ -4,7 +4,7 @@ namespace SNES { namespace Info { static const char Name[] = "bsnes"; - static const char Version[] = "082.09"; + static const char Version[] = "082.10"; static const unsigned SerializerVersion = 21; } } diff --git a/bsnes/ui/Makefile b/bsnes/ui/Makefile index 4693c895f..cbaa3face 100755 --- a/bsnes/ui/Makefile +++ b/bsnes/ui/Makefile @@ -3,7 +3,8 @@ include $(snes)/Makefile include $(gameboy)/Makefile name := batch -ui_objects := ui-main ui-config ui-interface ui-utility ui-general +ui_objects := ui-main ui-config ui-interface ui-utility +ui_objects += ui-general ui-tools ui_objects += phoenix ruby ui_objects += $(if $(call streq,$(platform),win),resource) @@ -71,6 +72,7 @@ obj/ui-config.o: $(ui)/config/config.cpp $(call rwildcard,$(ui)/) obj/ui-interface.o: $(ui)/interface/interface.cpp $(call rwildcard,$(ui)/) obj/ui-utility.o: $(ui)/utility/utility.cpp $(call rwildcard,$(ui)/) obj/ui-general.o: $(ui)/general/general.cpp $(call rwildcard,$(ui)/) +obj/ui-tools.o: $(ui)/tools/tools.cpp $(call rwildcard,$(ui)/) obj/ruby.o: ruby/ruby.cpp $(call rwildcard,ruby/*) $(call compile,$(rubydef) $(rubyflags)) diff --git a/bsnes/ui/base.hpp b/bsnes/ui/base.hpp index 2473005bf..a0a7e6527 100755 --- a/bsnes/ui/base.hpp +++ b/bsnes/ui/base.hpp @@ -23,6 +23,7 @@ using namespace ruby; #include "interface/interface.hpp" #include "utility/utility.hpp" #include "general/general.hpp" +#include "tools/tools.hpp" struct Application { bool quit; diff --git a/bsnes/ui/general/main-window.cpp b/bsnes/ui/general/main-window.cpp index df280700b..2ef25ffef 100755 --- a/bsnes/ui/general/main-window.cpp +++ b/bsnes/ui/general/main-window.cpp @@ -32,7 +32,20 @@ MainWindow::MainWindow() { settingsMuteAudio.setText("Mute Audio"); toolsMenu.setText("Tools"); + toolsStateSave.setText("Save State"); + toolsStateSave1.setText("Slot 1"); + toolsStateSave2.setText("Slot 2"); + toolsStateSave3.setText("Slot 3"); + toolsStateSave4.setText("Slot 4"); + toolsStateSave5.setText("Slot 5"); + toolsStateLoad.setText("Load State"); + toolsStateLoad1.setText("Slot 1"); + toolsStateLoad2.setText("Slot 2"); + toolsStateLoad3.setText("Slot 3"); + toolsStateLoad4.setText("Slot 4"); + toolsStateLoad5.setText("Slot 5"); toolsShrinkWindow.setText("Shrink Window"); + toolsCheatEditor.setText("Cheat Editor ..."); toolsTest.setText("Test"); helpMenu.setText("Help"); @@ -66,7 +79,21 @@ MainWindow::MainWindow() { settingsMenu.append(settingsMuteAudio); append(toolsMenu); + toolsMenu.append(toolsStateSave); + toolsStateSave.append(toolsStateSave1); + toolsStateSave.append(toolsStateSave2); + toolsStateSave.append(toolsStateSave3); + toolsStateSave.append(toolsStateSave4); + toolsStateSave.append(toolsStateSave5); + toolsMenu.append(toolsStateLoad); + toolsStateLoad.append(toolsStateLoad1); + toolsStateLoad.append(toolsStateLoad2); + toolsStateLoad.append(toolsStateLoad3); + toolsStateLoad.append(toolsStateLoad4); + toolsStateLoad.append(toolsStateLoad5); + toolsMenu.append(toolsSeparator); toolsMenu.append(toolsShrinkWindow); + toolsMenu.append(toolsCheatEditor); toolsMenu.append(toolsTest); append(helpMenu); @@ -87,32 +114,32 @@ MainWindow::MainWindow() { cartridgeLoadNES.onTick = [&] { fileBrowser->open("Load NES Cartridge", { "*.nes" }, [](string filename) { - interface->loadCartridgeNES(filename); + interface->loadCartridge(filename); }); }; cartridgeLoadSNES.onTick = [&] { fileBrowser->open("Load SNES Cartridge", { "*.sfc" }, [](string filename) { - interface->loadCartridgeSNES(filename); + interface->loadCartridge(filename); }); }; cartridgeLoadGameBoy.onTick = [&] { fileBrowser->open("Load Game Boy Cartridge", { "*.gb", "*.gbc" }, [](string filename) { - interface->loadCartridgeGameBoy(filename); + interface->loadCartridge(filename); }); }; nesPower.onTick = { &Interface::power, interface }; nesReset.onTick = { &Interface::reset, interface }; - nesCartridgeUnload.onTick = { &Interface::unloadCartridgeNES, interface }; + nesCartridgeUnload.onTick = { &Interface::unloadCartridge, interface }; snesPower.onTick = { &Interface::power, interface }; snesReset.onTick = { &Interface::reset, interface }; - snesCartridgeUnload.onTick = { &Interface::unloadCartridgeSNES, interface }; + snesCartridgeUnload.onTick = { &Interface::unloadCartridge, interface }; gameBoyPower.onTick = { &Interface::power, interface }; - gameBoyCartridgeUnload.onTick = { &Interface::unloadCartridgeGameBoy, interface }; + gameBoyCartridgeUnload.onTick = { &Interface::unloadCartridge, interface }; settingsSynchronizeVideo.onTick = [&] { video.set(Video::Synchronize, settingsSynchronizeVideo.checked()); @@ -126,8 +153,22 @@ MainWindow::MainWindow() { dspaudio.setVolume(settingsMuteAudio.checked() ? 0.0 : 1.0); }; + toolsStateSave1.onTick = [&] { interface->saveState({ interface->baseName, "-1.bst" }); }; + toolsStateSave2.onTick = [&] { interface->saveState({ interface->baseName, "-2.bst" }); }; + toolsStateSave3.onTick = [&] { interface->saveState({ interface->baseName, "-3.bst" }); }; + toolsStateSave4.onTick = [&] { interface->saveState({ interface->baseName, "-4.bst" }); }; + toolsStateSave5.onTick = [&] { interface->saveState({ interface->baseName, "-5.bst" }); }; + + toolsStateLoad1.onTick = [&] { interface->loadState({ interface->baseName, "-1.bst" }); }; + toolsStateLoad2.onTick = [&] { interface->loadState({ interface->baseName, "-2.bst" }); }; + toolsStateLoad3.onTick = [&] { interface->loadState({ interface->baseName, "-3.bst" }); }; + toolsStateLoad4.onTick = [&] { interface->loadState({ interface->baseName, "-4.bst" }); }; + toolsStateLoad5.onTick = [&] { interface->loadState({ interface->baseName, "-5.bst" }); }; + toolsShrinkWindow.onTick = [&] { utility->resizeMainWindow(true); }; + toolsCheatEditor.onTick = [&] { cheatEditor->setVisible(); }; + toolsTest.onTick = [&] { NES::cpu.trace = toolsTest.checked(); }; @@ -139,4 +180,16 @@ MainWindow::MainWindow() { "Website: http://byuu.org/" }); }; + + synchronize(); +} + +void MainWindow::synchronize() { + if(interface->loaded()) { + toolsStateSave.setEnabled(true); + toolsStateLoad.setEnabled(true); + } else { + toolsStateSave.setEnabled(false); + toolsStateLoad.setEnabled(false); + } } diff --git a/bsnes/ui/general/main-window.hpp b/bsnes/ui/general/main-window.hpp index 28e97b470..f66ce42b3 100755 --- a/bsnes/ui/general/main-window.hpp +++ b/bsnes/ui/general/main-window.hpp @@ -30,12 +30,27 @@ struct MainWindow : Window { CheckItem settingsMuteAudio; Menu toolsMenu; + Menu toolsStateSave; + Item toolsStateSave1; + Item toolsStateSave2; + Item toolsStateSave3; + Item toolsStateSave4; + Item toolsStateSave5; + Menu toolsStateLoad; + Item toolsStateLoad1; + Item toolsStateLoad2; + Item toolsStateLoad3; + Item toolsStateLoad4; + Item toolsStateLoad5; + Separator toolsSeparator; Item toolsShrinkWindow; + Item toolsCheatEditor; CheckItem toolsTest; Menu helpMenu; Item helpAbout; + void synchronize(); MainWindow(); }; diff --git a/bsnes/ui/interface/gameboy.cpp b/bsnes/ui/interface/gameboy.cpp index 0bc869fd3..88f608c0b 100755 --- a/bsnes/ui/interface/gameboy.cpp +++ b/bsnes/ui/interface/gameboy.cpp @@ -17,6 +17,30 @@ void InterfaceGameBoy::unloadCartridge() { interface->baseName = ""; } +bool InterfaceGameBoy::saveState(const string &filename) { + GameBoy::system.runtosave(); + serializer s = GameBoy::system.serialize(); + return file::write(filename, s.data(), s.size()); +} + +bool InterfaceGameBoy::loadState(const string &filename) { + uint8_t *data; + unsigned size; + if(file::read(filename, data, size) == false) return false; + serializer s(data, size); + delete[] data; + return GameBoy::system.unserialize(s); +} + +void InterfaceGameBoy::setCheatCodes(const lstring &list) { + GameBoy::cheat.reset(); + for(unsigned n = 0; n < list.size(); n++) { + GameBoy::cheat[n] = list[n]; + GameBoy::cheat[n].enable = true; + } + GameBoy::cheat.synchronize(); +} + // void InterfaceGameBoy::video_refresh(const uint8_t *data) { diff --git a/bsnes/ui/interface/gameboy.hpp b/bsnes/ui/interface/gameboy.hpp index b9c1c97a1..41891cb5f 100755 --- a/bsnes/ui/interface/gameboy.hpp +++ b/bsnes/ui/interface/gameboy.hpp @@ -2,6 +2,10 @@ struct InterfaceGameBoy : GameBoy::Interface { bool loadCartridge(const string &filename); void unloadCartridge(); + bool saveState(const string &filename); + bool loadState(const string &filename); + void setCheatCodes(const lstring &list); + void video_refresh(const uint8_t *data); void audio_sample(int16_t csample, int16_t lsample, int16_t rsample); bool input_poll(unsigned id); diff --git a/bsnes/ui/interface/interface.cpp b/bsnes/ui/interface/interface.cpp index 53f2602fc..6bfed70c2 100755 --- a/bsnes/ui/interface/interface.cpp +++ b/bsnes/ui/interface/interface.cpp @@ -14,11 +14,14 @@ bool Interface::loaded() { } bool Interface::loadCartridge(const string &filename) { - if(filename.endswith(".nes")) return loadCartridgeNES(filename); - if(filename.endswith(".sfc")) return loadCartridgeSNES(filename); - if(filename.endswith(".gb" )) return loadCartridgeGameBoy(filename); - if(filename.endswith(".gbc")) return loadCartridgeGameBoy(filename); - return true; + bool result = false; + unloadCartridge(); + if(filename.endswith(".nes")) result = loadCartridgeNES(filename); + if(filename.endswith(".sfc")) result = loadCartridgeSNES(filename); + if(filename.endswith(".gb" )) result = loadCartridgeGameBoy(filename); + if(filename.endswith(".gbc")) result = loadCartridgeGameBoy(filename); + if(result == true) cheatEditor->load({ baseName, ".cht" }); + return result; } bool Interface::loadCartridgeNES(const string &filename) { @@ -27,6 +30,19 @@ bool Interface::loadCartridgeNES(const string &filename) { return true; } +void Interface::unloadCartridge() { + if(loaded() == false) return; + cheatEditor->save({ baseName, ".cht" }); + + switch(mode()) { + case Mode::NES: nes.unloadCartridge(); break; + case Mode::SNES: snes.unloadCartridge(); break; + case Mode::GameBoy: gameBoy.unloadCartridge(); break; + } + + utility->setMode(mode = Mode::None); +} + void Interface::unloadCartridgeNES() { nes.unloadCartridge(); utility->setMode(mode = Mode::None); @@ -86,6 +102,30 @@ void Interface::run() { } } +bool Interface::saveState(const string &filename) { + switch(mode()) { + case Mode::SNES: return snes.saveState(filename); + case Mode::GameBoy: return gameBoy.saveState(filename); + } + return false; +} + +bool Interface::loadState(const string &filename) { + switch(mode()) { + case Mode::SNES: return snes.loadState(filename); + case Mode::GameBoy: return gameBoy.loadState(filename); + } + return false; +} + +void Interface::setCheatCodes(const lstring &list) { + switch(mode()) { + case Mode::NES: return nes.setCheatCodes(list); + case Mode::SNES: return snes.setCheatCodes(list); + case Mode::GameBoy: return gameBoy.setCheatCodes(list); + } +} + Interface::Interface() { mode = Mode::None; NES::system.init(&nes); diff --git a/bsnes/ui/interface/interface.hpp b/bsnes/ui/interface/interface.hpp index 57baa9cd3..cb51639cb 100755 --- a/bsnes/ui/interface/interface.hpp +++ b/bsnes/ui/interface/interface.hpp @@ -13,6 +13,7 @@ struct Interface : property { bool loadCartridgeSNES(const string &filename); bool loadCartridgeGameBoy(const string &filename); + void unloadCartridge(); void unloadCartridgeNES(); void unloadCartridgeSNES(); void unloadCartridgeGameBoy(); @@ -21,6 +22,10 @@ struct Interface : property { void reset(); void run(); + bool saveState(const string &filename); + bool loadState(const string &filename); + void setCheatCodes(const lstring &list); + Interface(); int16_t inputState[Scancode::Limit]; diff --git a/bsnes/ui/interface/nes.cpp b/bsnes/ui/interface/nes.cpp index cdb263bcd..7f073a78b 100755 --- a/bsnes/ui/interface/nes.cpp +++ b/bsnes/ui/interface/nes.cpp @@ -15,6 +15,15 @@ void InterfaceNES::unloadCartridge() { interface->baseName = ""; } +void InterfaceNES::setCheatCodes(const lstring &list) { + NES::cheat.reset(); + for(unsigned n = 0; n < list.size(); n++) { + NES::cheat[n] = list[n]; + NES::cheat[n].enable = true; + } + NES::cheat.synchronize(); +} + // void InterfaceNES::video_refresh(const uint32_t *data) { diff --git a/bsnes/ui/interface/nes.hpp b/bsnes/ui/interface/nes.hpp index a91060245..72e1e9809 100755 --- a/bsnes/ui/interface/nes.hpp +++ b/bsnes/ui/interface/nes.hpp @@ -2,6 +2,8 @@ struct InterfaceNES : NES::Interface { bool loadCartridge(const string &filename); void unloadCartridge(); + void setCheatCodes(const lstring &list); + void video_refresh(const uint32_t *data); void audio_sample(int16_t sample); int16_t input_poll(bool port, unsigned device, unsigned id); diff --git a/bsnes/ui/interface/snes.cpp b/bsnes/ui/interface/snes.cpp index daf12bd73..8d0a34569 100755 --- a/bsnes/ui/interface/snes.cpp +++ b/bsnes/ui/interface/snes.cpp @@ -18,6 +18,30 @@ void InterfaceSNES::unloadCartridge() { interface->baseName = ""; } +bool InterfaceSNES::saveState(const string &filename) { + SNES::system.runtosave(); + serializer s = SNES::system.serialize(); + return file::write(filename, s.data(), s.size()); +} + +bool InterfaceSNES::loadState(const string &filename) { + uint8_t *data; + unsigned size; + if(file::read(filename, data, size) == false) return false; + serializer s(data, size); + delete[] data; + return SNES::system.unserialize(s); +} + +void InterfaceSNES::setCheatCodes(const lstring &list) { + SNES::cheat.reset(); + for(unsigned n = 0; n < list.size(); n++) { + SNES::cheat[n] = list[n]; + SNES::cheat[n].enabled = true; + } + SNES::cheat.synchronize(); +} + // void InterfaceSNES::video_refresh(const uint16_t *data, bool hires, bool interlace, bool overscan) { @@ -87,5 +111,5 @@ int16_t InterfaceSNES::input_poll(bool port, SNES::Input::Device device, unsigne } string InterfaceSNES::path(SNES::Cartridge::Slot slot, const string &hint) { - return "/home/byuu/Desktop/test"; + return dir(interface->baseName); } diff --git a/bsnes/ui/interface/snes.hpp b/bsnes/ui/interface/snes.hpp index 5eb26356e..f0e17dc63 100755 --- a/bsnes/ui/interface/snes.hpp +++ b/bsnes/ui/interface/snes.hpp @@ -2,6 +2,10 @@ struct InterfaceSNES : SNES::Interface { bool loadCartridge(const string &filename); void unloadCartridge(); + bool saveState(const string &filename); + bool loadState(const string &filename); + void setCheatCodes(const lstring &list); + void video_refresh(const uint16_t *data, bool hires, bool interlace, bool overscan); void audio_sample(int16_t lsample, int16_t rsample); int16_t input_poll(bool port, SNES::Input::Device device, unsigned index, unsigned id); diff --git a/bsnes/ui/main.cpp b/bsnes/ui/main.cpp index 6141a517a..1a77a2916 100755 --- a/bsnes/ui/main.cpp +++ b/bsnes/ui/main.cpp @@ -41,6 +41,7 @@ Application::Application(int argc, char **argv) : quit(false) { mainWindow = new MainWindow; fileBrowser = new FileBrowser; + cheatEditor = new CheatEditor; utility->setMode(Interface::Mode::None); mainWindow->setVisible(); @@ -73,9 +74,12 @@ Application::Application(int argc, char **argv) : quit(false) { OS::processEvents(); Application::run(); } + + interface->unloadCartridge(); } Application::~Application() { + delete cheatEditor; delete fileBrowser; delete mainWindow; delete utility; diff --git a/bsnes/ui/tools/cheat-editor.cpp b/bsnes/ui/tools/cheat-editor.cpp new file mode 100755 index 000000000..e4db4b0ee --- /dev/null +++ b/bsnes/ui/tools/cheat-editor.cpp @@ -0,0 +1,197 @@ +CheatEditor *cheatEditor = 0; + +CheatEditor::CheatEditor() { + setTitle("Cheat Editor"); + setGeometry({ 128, 128, 600, 360 }); + + cheatList.setHeaderText("Slot", "Code", "Description"); + cheatList.setHeaderVisible(); + cheatList.setCheckable(); + codeLabel.setText("Code(s):"); + descLabel.setText("Description:"); + findButton.setText("Find Codes ..."); + findButton.setEnabled(false); + clearAllButton.setText("Clear All"); + clearButton.setText("Clear"); + + append(layout); + layout.setMargin(5); + layout.append(cheatList, ~0, ~0, 5); + layout.append(codeLayout, ~0, 0, 5); + codeLayout.append(codeLabel, 80, 0); + codeLayout.append(codeEdit, ~0, 0); + layout.append(descLayout, ~0, 0, 5); + descLayout.append(descLabel, 80, 0); + descLayout.append(descEdit, ~0, 0); + layout.append(controlLayout, ~0, 0); + controlLayout.append(findButton, 100, 0); + controlLayout.append(spacer, ~0, 0); + controlLayout.append(clearAllButton, 80, 0, 5); + controlLayout.append(clearButton, 80, 0); + + for(unsigned n = 0; n < 128; n++) cheatList.append("", "", ""); + updateUI(); + synchronize(); + + cheatList.onChange = { &CheatEditor::synchronize, this }; + cheatList.onTick = [&](unsigned) { updateInterface(); }; + codeEdit.onChange = { &CheatEditor::updateCode, this }; + descEdit.onChange = { &CheatEditor::updateDesc, this }; + clearAllButton.onTick = { &CheatEditor::clearAll, this }; + clearButton.onTick = { &CheatEditor::clearSelected, this }; +} + +void CheatEditor::synchronize() { + layout.setEnabled(interface->loaded()); + + if(cheatList.selected()) { + unsigned n = cheatList.selection(); + codeEdit.setText(cheatText[n][Code]); + descEdit.setText(cheatText[n][Desc]); + codeEdit.setEnabled(true); + descEdit.setEnabled(true); + clearButton.setEnabled(true); + } else { + codeEdit.setText(""); + codeEdit.setEnabled(false); + descEdit.setText(""); + descEdit.setEnabled(false); + clearButton.setEnabled(false); + } +} + +void CheatEditor::updateUI() { + for(unsigned n = 0; n < 128; n++) { + string code = cheatText[n][Code]; + string description = cheatText[n][Code] == "" && cheatText[n][Desc] == "" ? "(empty)" : cheatText[n][Desc]; + lstring codes; + codes.split("+", code); + cheatList.modify(n, decimal<3>(n + 1), codes.size() == 1 ? code : string{ codes[0], "+..." }, description); + } + cheatList.autoSizeColumns(); +} + +void CheatEditor::updateInterface() { + lstring cheatCodes; + for(unsigned n = 0; n < 128; n++) { + string code = cheatText[n][Code]; + if(cheatList.checked(n) && code != "") cheatCodes.append(code.replace(" ", "")); + } + interface->setCheatCodes(cheatCodes); +} + +void CheatEditor::updateCode() { + unsigned n = cheatList.selection(); + cheatText[n][Code] = codeEdit.text(); + updateUI(), updateInterface(); +} + +void CheatEditor::updateDesc() { + unsigned n = cheatList.selection(); + cheatText[n][Desc] = descEdit.text(); + updateUI(), updateInterface(); +} + +void CheatEditor::clearAll() { + if(MessageWindow::question(*this, "Warning: all cheat codes will be permanently erased!\nAre you sure?") + == MessageWindow::Response::Yes) { + reset(); + } +} + +void CheatEditor::clearSelected() { + unsigned n = cheatList.selection(); + cheatList.setChecked(n, false); + cheatText[n][Code] = ""; + cheatText[n][Desc] = ""; + codeEdit.setText(""); + descEdit.setText(""); + updateUI(), updateInterface(); +} + +void CheatEditor::reset() { + synchronize(); + for(unsigned n = 0; n < 128; n++) { + cheatList.setChecked(n, false); + cheatText[n][Code] = ""; + cheatText[n][Desc] = ""; + } + codeEdit.setText(""); + descEdit.setText(""); + updateUI(), updateInterface(); +} + +bool CheatEditor::load(const string &filename) { + synchronize(); + + string data; + if(data.readfile(filename) == false) return false; + + unsigned n = 0; + xml_element document = xml_parse(data); + foreach(head, document.element) { + if(head.name == "cartridge") { + foreach(node, head.element) { + if(node.name == "cheat") { + bool enable = false; + string description; + string code; + foreach(attribute, node.attribute) { + if(attribute.name == "enabled") enable = (attribute.parse() == "true"); + } + foreach(element, node.element) { + if(element.name == "description") description = element.parse(); + else if(element.name == "code") code.append(element.parse(), "+"); + } + code.rtrim<1>("+"); + + cheatList.setChecked(n, enable); + cheatText[n][Code] = code; + cheatText[n][Desc] = description; + + if(++n >= 128) break; + } + } + } + } + + updateUI(), updateInterface(); + return true; +} + +bool CheatEditor::save(const string &filename) { + synchronize(); + + signed lastSave = -1; + for(signed n = 127; n >= 0; n--) { + if(cheatText[n][Code] != "" || cheatText[n][Desc] != "") { + lastSave = n; + break; + } + } + + if(lastSave == -1) { + unlink(filename); + return true; + } + + file fp; + if(fp.open(filename, file::mode::write) == false) return false; + + fp.print("\n"); + fp.print("\n"); + for(unsigned n = 0; n <= lastSave; n++) { + fp.print(" \n"); + fp.print(" ", cheatText[n][Desc], "\n"); + lstring list; + list.split("+", cheatText[n][Code]); + foreach(code, list) { + fp.print(" ", code, "\n"); + } + fp.print(" \n"); + } + fp.print("\n"); + fp.close(); + + return true; +} diff --git a/bsnes/ui/tools/cheat-editor.hpp b/bsnes/ui/tools/cheat-editor.hpp new file mode 100755 index 000000000..ad25abc1e --- /dev/null +++ b/bsnes/ui/tools/cheat-editor.hpp @@ -0,0 +1,35 @@ +struct CheatEditor : Window { + VerticalLayout layout; + ListView cheatList; + HorizontalLayout codeLayout; + Label codeLabel; + LineEdit codeEdit; + HorizontalLayout descLayout; + Label descLabel; + LineEdit descEdit; + HorizontalLayout controlLayout; + Button findButton; + Widget spacer; + Button clearAllButton; + Button clearButton; + + void synchronize(); + void updateUI(); + void updateInterface(); + void updateCode(); + void updateDesc(); + void clearAll(); + void clearSelected(); + + void reset(); + bool load(const string &filename); + bool save(const string &filename); + + CheatEditor(); + +private: + enum : unsigned { Code = 0, Desc = 1 }; + string cheatText[128][2]; +}; + +extern CheatEditor *cheatEditor; diff --git a/bsnes/ui/tools/tools.cpp b/bsnes/ui/tools/tools.cpp new file mode 100755 index 000000000..5e37026e1 --- /dev/null +++ b/bsnes/ui/tools/tools.cpp @@ -0,0 +1,2 @@ +#include "../base.hpp" +#include "cheat-editor.cpp" diff --git a/bsnes/ui/tools/tools.hpp b/bsnes/ui/tools/tools.hpp new file mode 100755 index 000000000..97db5ccfc --- /dev/null +++ b/bsnes/ui/tools/tools.hpp @@ -0,0 +1 @@ +#include "cheat-editor.hpp" diff --git a/bsnes/ui/utility/utility.cpp b/bsnes/ui/utility/utility.cpp index c6de8908b..402860d29 100755 --- a/bsnes/ui/utility/utility.cpp +++ b/bsnes/ui/utility/utility.cpp @@ -12,6 +12,7 @@ void Utility::setMode(Interface::Mode mode) { if(mode == Interface::Mode::None) { mainWindow->setTitle(application->title); mainWindow->setStatusText("No cartridge loaded"); + cheatEditor->reset(); } else if(mode == Interface::Mode::NES) { @@ -32,6 +33,7 @@ void Utility::setMode(Interface::Mode mode) { dspaudio.setFrequency(4194304.0); } + mainWindow->synchronize(); resizeMainWindow(); }