* added cheat code support to Super Game Boy emulation
* added cheat code search support (not for Game Boy, sorry)
* compilation fixes
This commit is contained in:
byuu 2019-07-22 01:39:32 +09:00
parent 78a6a2e7d7
commit 55799c4230
19 changed files with 213 additions and 10 deletions

View File

@ -32,7 +32,7 @@ using namespace nall;
namespace Emulator {
static const string Name = "bsnes";
static const string Version = "107.10";
static const string Version = "107.11";
static const string Author = "byuu";
static const string License = "GPLv3";
static const string Website = "https://byuu.org/";

View File

@ -84,6 +84,7 @@ struct Interface {
virtual auto unserialize(serializer&) -> bool { return false; }
//cheat functions
virtual auto read(uint24 address) -> uint8 { return 0; }
virtual auto cheats(const vector<string>& = {}) -> void {}
//configuration

View File

@ -421,6 +421,13 @@ static GB_read_function_t * const read_map[] =
read_ram, read_high_memory, /* EXXX FXXX */
};
static GB_read_memory_callback_t GB_read_memory_callback_v = 0;
void GB_set_read_memory_callback(GB_gameboy_t *gb, GB_read_memory_callback_t callback)
{
GB_read_memory_callback_v = callback;
}
uint8_t GB_read_memory(GB_gameboy_t *gb, uint16_t addr)
{
if (gb->n_watchpoints) {
@ -429,6 +436,11 @@ uint8_t GB_read_memory(GB_gameboy_t *gb, uint16_t addr)
if (is_addr_in_dma_use(gb, addr)) {
addr = gb->dma_current_src;
}
if (GB_read_memory_callback_v) {
uint8_t data = read_map[addr >> 12](gb, addr);
data = GB_read_memory_callback_v(gb, addr, data);
return data;
}
return read_map[addr >> 12](gb, addr);
}

View File

@ -3,6 +3,9 @@
#include "gb_struct_def.h"
#include <stdint.h>
typedef uint8_t (*GB_read_memory_callback_t)(GB_gameboy_t *gb, uint16_t addr, uint8_t data);
void GB_set_read_memory_callback(GB_gameboy_t *gb, GB_read_memory_callback_t callback);
uint8_t GB_read_memory(GB_gameboy_t *gb, uint16_t addr);
void GB_write_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value);
#ifdef GB_INTERNAL

View File

@ -27,6 +27,11 @@ namespace SameBoy {
icd.joypWrite(p14, p15);
}
static auto read_memory(GB_gameboy_t*, uint16_t addr, uint8_t data) -> uint8_t {
if(auto replace = icd.cheats.find(addr, data)) return replace();
return data;
}
static auto rgb_encode(GB_gameboy_t*, uint8_t r, uint8_t g, uint8_t b) -> uint32_t {
return r << 16 | g << 8 | b << 0;
}
@ -78,6 +83,7 @@ auto ICD::load() -> bool {
GB_set_icd_vreset_callback(&sameboy, &SameBoy::vreset);
GB_set_icd_pixel_callback(&sameboy, &SameBoy::icd_pixel);
GB_set_joyp_write_callback(&sameboy, &SameBoy::joyp_write);
GB_set_read_memory_callback(&sameboy, &SameBoy::read_memory);
GB_set_rgb_encode_callback(&sameboy, &SameBoy::rgb_encode);
GB_apu_set_sample_callback(&sameboy, &SameBoy::sample);
GB_set_vblank_callback(&sameboy, &SameBoy::vblank);

View File

@ -1,5 +1,6 @@
struct ICD : Emulator::Platform, Thread {
shared_pointer<Emulator::Stream> stream;
Emulator::Cheat cheats;
inline auto pathID() const -> uint { return information.pathID; }

View File

@ -244,8 +244,15 @@ auto Interface::unserialize(serializer& s) -> bool {
return system.unserialize(s);
}
auto Interface::read(uint24 address) -> uint8 {
return cpu.readDisassembler(address);
}
auto Interface::cheats(const vector<string>& list) -> void {
if(cartridge.has.ICD) return; //TODO: SameBoy cheat code support
if(cartridge.has.ICD) {
icd.cheats.assign(list);
return;
}
//make all ROM data writable temporarily
Memory::GlobalWriteEnable = true;

View File

@ -60,6 +60,7 @@ struct Interface : Emulator::Interface {
auto serialize() -> serializer override;
auto unserialize(serializer&) -> bool override;
auto read(uint24 address) -> uint8 override;
auto cheats(const vector<string>&) -> void override;
auto configuration() -> string override;

View File

@ -158,9 +158,10 @@ auto Presentation::create() -> void {
captureScreenshot.setIcon(Icon::Emblem::Image).setText("Capture Screenshot").onActivate([&] {
program.captureScreenshot();
});
cheatEditor.setIcon(Icon::Edit::Replace).setText("Cheat Editor ...").onActivate([&] { toolsWindow.show(0); });
stateManager.setIcon(Icon::Application::FileManager).setText("State Manager ...").onActivate([&] { toolsWindow.show(1); });
manifestViewer.setIcon(Icon::Emblem::Text).setText("Manifest Viewer ...").onActivate([&] { toolsWindow.show(2); });
cheatFinder.setIcon(Icon::Edit::Find).setText("Cheat Finder ...").onActivate([&] { toolsWindow.show(0); });
cheatEditor.setIcon(Icon::Edit::Replace).setText("Cheat Editor ...").onActivate([&] { toolsWindow.show(1); });
stateManager.setIcon(Icon::Application::FileManager).setText("State Manager ...").onActivate([&] { toolsWindow.show(2); });
manifestViewer.setIcon(Icon::Emblem::Text).setText("Manifest Viewer ...").onActivate([&] { toolsWindow.show(3); });
helpMenu.setText(tr("Help"));
documentation.setIcon(Icon::Application::Browser).setText({tr("Documentation"), " ..."}).onActivate([&] {

View File

@ -105,6 +105,7 @@ struct Presentation : Window {
MenuItem frameAdvance{&toolsMenu};
MenuItem captureScreenshot{&toolsMenu};
MenuSeparator toolsSeparatorB{&toolsMenu};
MenuItem cheatFinder{&toolsMenu};
MenuItem cheatEditor{&toolsMenu};
MenuItem stateManager{&toolsMenu};
MenuItem manifestViewer{&toolsMenu};

View File

@ -46,6 +46,7 @@ auto Program::load() -> void {
presentation.pauseEmulation.setChecked(false);
presentation.updateProgramIcon();
presentation.updateStatusIcon();
cheatFinder.restart(); //clear any old cheat search results
cheatEditor.loadCheats();
stateManager.loadStates();
manifestViewer.loadManifest();

View File

@ -31,6 +31,7 @@ auto Program::create() -> void {
driverSettings.create();
toolsWindow.create();
cheatFinder.create();
cheatDatabase.create();
cheatWindow.create();
cheatEditor.create();

View File

@ -34,8 +34,10 @@ auto Program::updateStatus() -> void {
auto Program::captureScreenshot() -> bool {
if(emulator->loaded() && screenshot.data) {
if(auto filename = screenshotPath()) {
image capture;
//RGB555 -> RGB888
image capture{0, 16, 0x8000, 0x7c00, 0x03e0, 0x001f};
capture.copy(screenshot.data, screenshot.pitch, screenshot.width, screenshot.height);
capture.transform(0, 32, 0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff);
//normalize pixel aspect ratio to 1:1
if(capture.width() == 512 && capture.height() == 240) capture.scale(512, 480, false); //hires

View File

@ -16,11 +16,14 @@ auto CheatDatabase::create() -> void {
}
auto CheatDatabase::findCheats() -> void {
auto sha256 = emulator->hashes()[0];
//hack to locate Super Game Boy cheat codes
auto sha256a = emulator->hashes()(0, "none");
auto sha256b = emulator->hashes()(1, "none");
auto document = BML::unserialize(string::read(locate("Database/Cheat Codes.bml")));
for(auto game : document.find("cartridge")) {
if(game["sha256"].text() != sha256) continue;
if(game["sha256"].text() != sha256a
&& game["sha256"].text() != sha256b) continue;
cheatList.reset();
for(auto cheat : game.find("cheat")) {

View File

@ -0,0 +1,124 @@
auto CheatFinder::create() -> void {
setIcon(Icon::Edit::Find);
setText("Cheat Finder");
layout.setPadding(5_sx);
searchList.setHeadered();
searchValue.onActivate([&] { eventScan(); });
searchLabel.setText("Value:");
searchSize.append(ComboButtonItem().setText("Byte"));
searchSize.append(ComboButtonItem().setText("Word"));
searchSize.append(ComboButtonItem().setText("Long"));
searchMode.append(ComboButtonItem().setText("=="));
searchMode.append(ComboButtonItem().setText("!="));
searchMode.append(ComboButtonItem().setText(">="));
searchMode.append(ComboButtonItem().setText("<="));
searchMode.append(ComboButtonItem().setText(">"));
searchMode.append(ComboButtonItem().setText("<"));
searchSpan.append(ComboButtonItem().setText("WRAM"));
searchSpan.append(ComboButtonItem().setText("All"));
searchScan.setText("Scan").onActivate([&] { eventScan(); });
searchClear.setText("Clear").onActivate([&] { eventClear(); });
refresh();
}
auto CheatFinder::restart() -> void {
searchValue.setText("");
searchSize.items().first().setSelected();
searchMode.items().first().setSelected();
searchSpan.items().first().setSelected();
candidates.reset();
refresh();
}
auto CheatFinder::refresh() -> void {
searchList.reset();
searchList.append(TableViewColumn().setText("Address"));
searchList.append(TableViewColumn().setText("Value").setExpandable());
for(auto& candidate : candidates) {
TableViewItem item{&searchList};
item.append(TableViewCell().setText({"0x", hex(candidate.address, 6L)}));
if(candidate.size == 0) {
item.append(TableViewCell().setText({"0x", hex(candidate.data, 2L), " (", candidate.data, ")"}));
}
if(candidate.size == 1) {
item.append(TableViewCell().setText({"0x", hex(candidate.data, 4L), " (", candidate.data, ")"}));
}
if(candidate.size == 2) {
item.append(TableViewCell().setText({"0x", hex(candidate.data, 6L), " (", candidate.data, ")"}));
}
}
for(uint n : range(2)) {
Application::processEvents();
searchList.resizeColumns();
}
}
auto CheatFinder::eventScan() -> void {
uint32_t size = searchSize.selected().offset();
uint32_t mode = searchMode.selected().offset();
uint32_t span = searchSpan.selected().offset();
uint32_t data = searchValue.text().replace("$", "0x").replace("#", "").natural();
if(size == 0) data &= 0xff;
if(size == 1) data &= 0xffff;
if(size == 2) data &= 0xffffff;
if(!candidates) {
for(uint address : range(1 << 24)) {
if((address & 0x40e000) == 0x000000) continue; //00-3f,80-bf:0000-1fff (WRAM mirrors)
if((address & 0x40e000) == 0x002000) continue; //00-3f,80-bf:2000-3fff (I/O)
if((address & 0x40e000) == 0x004000) continue; //00-3f,80-bf:4000-5fff (I/O)
if(span == 0 && (address < 0x7e0000 || address > 0x7fffff)) continue;
auto value = read(size, address);
if(compare(mode, value, data)) {
candidates.append({address, value, size, mode, span});
if(candidates.size() >= 4096) break;
}
}
} else {
vector<CheatCandidate> result;
for(auto& candidate : candidates) {
uint address = candidate.address;
if((address & 0x40e000) == 0x000000) continue; //00-3f,80-bf:0000-1fff (WRAM mirrors)
if((address & 0x40e000) == 0x002000) continue; //00-3f,80-bf:2000-3fff (I/O)
if((address & 0x40e000) == 0x004000) continue; //00-3f,80-bf:4000-5fff (I/O)
if(span == 0 && (address < 0x7e0000 || address > 0x7fffff)) continue;
auto value = read(size, address);
if(compare(mode, value, data)) {
result.append({address, value, size, mode, span});
if(result.size() >= 4096) break;
}
}
candidates = result;
}
refresh();
}
auto CheatFinder::eventClear() -> void {
candidates.reset();
refresh();
}
auto CheatFinder::read(uint32_t size, uint32_t address) -> uint32_t {
uint32_t value = 0;
if(size >= 0) value |= emulator->read(address + 0) << 0;
if(size >= 1) value |= emulator->read(address + 1) << 8;
if(size >= 2) value |= emulator->read(address + 2) << 16;
return value;
}
auto CheatFinder::compare(uint32_t mode, uint32_t x, uint32_t y) -> bool {
if(mode == 0) return x == y;
if(mode == 1) return x != y;
if(mode == 2) return x >= y;
if(mode == 3) return x <= y;
if(mode == 4) return x > y;
if(mode == 5) return x < y;
return false;
}

View File

@ -1,4 +1,5 @@
#include "../bsnes.hpp"
#include "cheat-finder.cpp"
#include "cheat-editor.cpp"
#include "state-manager.cpp"
#include "manifest-viewer.cpp"
@ -7,6 +8,7 @@ CheatDatabase& cheatDatabase = Instances::cheatDatabase();
namespace Instances { Instance<CheatWindow> cheatWindow; }
CheatWindow& cheatWindow = Instances::cheatWindow();
CheatEditor cheatEditor;
CheatFinder cheatFinder;
namespace Instances { Instance<StateWindow> stateWindow; }
StateWindow& stateWindow = Instances::stateWindow();
StateManager stateManager;
@ -16,13 +18,14 @@ ToolsWindow& toolsWindow = Instances::toolsWindow();
auto ToolsWindow::create() -> void {
layout.setPadding(5_sx);
panel.append(cheatFinder);
panel.append(cheatEditor);
panel.append(stateManager);
panel.append(manifestViewer);
panel.onChange([&] {
uint offset = panel.selected().offset();
if(offset != 0) cheatDatabase.setVisible(false), cheatWindow.setVisible(false);
if(offset != 1) stateWindow.setVisible(false);
if(offset != 1) cheatDatabase.setVisible(false), cheatWindow.setVisible(false);
if(offset != 2) stateWindow.setVisible(false);
});
setTitle("Tools");

View File

@ -1,3 +1,35 @@
struct CheatCandidate {
uint32_t address;
uint32_t data;
uint32_t size;
uint32_t mode;
uint32_t span;
};
struct CheatFinder : TabFrameItem {
auto create() -> void;
auto restart() -> void;
auto refresh() -> void;
auto eventScan() -> void;
auto eventClear() -> void;
auto read(uint32_t size, uint32_t address) -> uint32_t;
auto compare(uint32_t mode, uint32_t x, uint32_t y) -> bool;
public:
vector<CheatCandidate> candidates;
VerticalLayout layout{this};
TableView searchList{&layout, Size{~0, ~0}};
HorizontalLayout controlLayout{&layout, Size{~0, 0}};
Label searchLabel{&controlLayout, Size{0, 0}};
LineEdit searchValue{&controlLayout, Size{~0, 0}};
ComboButton searchSize{&controlLayout, Size{0, 0}};
ComboButton searchMode{&controlLayout, Size{0, 0}};
ComboButton searchSpan{&controlLayout, Size{0, 0}};
Button searchScan{&controlLayout, Size{80, 0}};
Button searchClear{&controlLayout, Size{80, 0}};
};
struct Cheat {
auto operator==(const Cheat& compare) const -> bool {
return name == compare.name && code == compare.code && enable == compare.enable;
@ -158,6 +190,7 @@ public:
};
namespace Instances { extern Instance<CheatDatabase> cheatDatabase; }
extern CheatFinder cheatFinder;
extern CheatDatabase& cheatDatabase;
namespace Instances { extern Instance<CheatWindow> cheatWindow; }
extern CheatWindow& cheatWindow;

View File

@ -36,6 +36,7 @@ namespace nall {
#pragma clang diagnostic ignored "-Wswitch-bool"
#pragma clang diagnostic ignored "-Wtautological-compare"
#pragma clang diagnostic ignored "-Wabsolute-value"
#pragma clang diagnostic ignored "-Wunused-result"
//temporary
#pragma clang diagnostic ignored "-Winconsistent-missing-override"
@ -48,6 +49,7 @@ namespace nall {
#pragma GCC diagnostic ignored "-Wunknown-pragmas"
#pragma GCC diagnostic ignored "-Wpragmas"
#pragma GCC diagnostic ignored "-Wswitch-bool"
#pragma GCC diagnostic ignored "-Wunused-result"
#elif defined(_MSC_VER)
#define COMPILER_MICROSOFT
constexpr auto compiler() -> Compiler { return Compiler::Microsoft; }

View File

@ -4,6 +4,7 @@ using namespace ruby;
#undef deprecated
#undef mkdir
#undef noinline
#undef usleep
#if defined(DISPLAY_XORG)