mirror of
https://github.com/libretro/bsnes-libretro.git
synced 2024-11-27 02:50:32 +00:00
v107.11
* 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:
parent
78a6a2e7d7
commit
55799c4230
@ -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/";
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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; }
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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([&] {
|
||||
|
@ -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};
|
||||
|
@ -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();
|
||||
|
@ -31,6 +31,7 @@ auto Program::create() -> void {
|
||||
driverSettings.create();
|
||||
|
||||
toolsWindow.create();
|
||||
cheatFinder.create();
|
||||
cheatDatabase.create();
|
||||
cheatWindow.create();
|
||||
cheatEditor.create();
|
||||
|
@ -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
|
||||
|
@ -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")) {
|
||||
|
124
bsnes/target-bsnes/tools/cheat-finder.cpp
Normal file
124
bsnes/target-bsnes/tools/cheat-finder.cpp
Normal 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;
|
||||
}
|
@ -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");
|
||||
|
@ -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;
|
||||
|
@ -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; }
|
||||
|
@ -4,6 +4,7 @@ using namespace ruby;
|
||||
|
||||
#undef deprecated
|
||||
#undef mkdir
|
||||
#undef noinline
|
||||
#undef usleep
|
||||
|
||||
#if defined(DISPLAY_XORG)
|
||||
|
Loading…
Reference in New Issue
Block a user