Update to v082r19 release.

byuu says:

This will be the last release with the ui-snes folder (it's broken now
anyway.)
Re-added cheat code database searching + add window. It hashes
NES+SNES+GB images now and will look them up in the database.
Re-added filter support, all filters now output at RGB555. Stacking is
possible, but I don't currently allow it.
Removed mouse capture + test options from tools menu.
Removed smooth video output from settings menu.
There are now two built-in "shaders": "None" (point filtering) and
"Blur" (linear filtering).
OpenGL shaders can now use <shader language="GLSL" filter="point"> (or
"linear") to specify their filtering mode.
Individual emulator versions are gone, and names are hidden from the
GUI: you just see bsnes v082.19 now. A new release bumps all core
versions.

I cannot for the life of me get the video to clear properly when
toggling the shaders. Say you set pixellate2x filter, then turn on
curvature shader, then turn off the filter, you get junk at the bottom
right.
I have tried clearing and flipping the OpenGL surface 64x in a row ...
I don't know where the hell it's getting the data from. If anyone can
make a small patch to fix that, I'd greatly appreciate it.
This commit is contained in:
Tim Allen 2011-09-22 10:00:21 +10:00
parent 101c9507b1
commit 5b4dcbfdfe
25 changed files with 313 additions and 69 deletions

View File

@ -83,7 +83,9 @@ void Cartridge::load(const string &xml, const uint8_t *data, unsigned size) {
ramdata = new uint8_t[ramsize = info.ramsize](); ramdata = new uint8_t[ramsize = info.ramsize]();
system.load(); system.load();
loaded = true; loaded = true;
sha256 = nall::sha256(romdata, romsize);
} }
void Cartridge::unload() { void Cartridge::unload() {

View File

@ -34,6 +34,7 @@ struct Cartridge : MMIO, property<Cartridge> {
} info; } info;
readonly<bool> loaded; readonly<bool> loaded;
readonly<string> sha256;
uint8_t *romdata; uint8_t *romdata;
unsigned romsize; unsigned romsize;

View File

@ -4,7 +4,6 @@
namespace GameBoy { namespace GameBoy {
namespace Info { namespace Info {
static const char Name[] = "bgameboy"; static const char Name[] = "bgameboy";
static const char Version[] = "000.23";
static unsigned SerializerVersion = 2; static unsigned SerializerVersion = 2;
} }
} }

View File

@ -37,6 +37,7 @@ void Cartridge::load(const string &xml, const uint8_t *data, unsigned size) {
} }
loaded = true; loaded = true;
sha256 = nall::sha256(rom_data, rom_size);
} }
void Cartridge::unload() { void Cartridge::unload() {

View File

@ -9,6 +9,7 @@ struct Cartridge : property<Cartridge> {
void reset(); void reset();
readonly<bool> loaded; readonly<bool> loaded;
readonly<string> sha256;
Cartridge(); Cartridge();

View File

@ -4,7 +4,6 @@
namespace NES { namespace NES {
namespace Info { namespace Info {
static const char Name[] = "bnes"; static const char Name[] = "bnes";
static const char Version[] = "000.14";
} }
} }

View File

@ -96,6 +96,7 @@ public:
if(name == Video::Shader) { if(name == Video::Shader) {
OpenGL::set_shader(any_cast<const char*>(value)); OpenGL::set_shader(any_cast<const char*>(value));
settings.filter = any_cast<unsigned>(OpenGL::shaderfilter);
return true; return true;
} }

View File

@ -28,6 +28,7 @@ class OpenGL {
public: public:
GLuint gltexture; GLuint gltexture;
GLuint glprogram; GLuint glprogram;
unsigned shaderfilter;
GLuint fragmentshader; GLuint fragmentshader;
GLuint vertexshader; GLuint vertexshader;
bool shader_support; bool shader_support;
@ -139,6 +140,7 @@ public:
if(source) { if(source) {
bool is_glsl = false; bool is_glsl = false;
shaderfilter = 0;
string fragment_source; string fragment_source;
string vertex_source; string vertex_source;
@ -147,6 +149,7 @@ public:
if(head.name == "shader") { if(head.name == "shader") {
foreach(attribute, head.attribute) { foreach(attribute, head.attribute) {
if(attribute.name == "language" && attribute.content == "GLSL") is_glsl = true; if(attribute.name == "language" && attribute.content == "GLSL") is_glsl = true;
if(attribute.name == "filter") shaderfilter = attribute.content == "linear" ? 1 : 0;
} }
foreach(element, head.element) { foreach(element, head.element) {
if(element.name == "fragment") { if(element.name == "fragment") {
@ -236,6 +239,7 @@ public:
OpenGL() { OpenGL() {
gltexture = 0; gltexture = 0;
glprogram = 0; glprogram = 0;
shaderfilter = 0;
fragmentshader = 0; fragmentshader = 0;
vertexshader = 0; vertexshader = 0;

View File

@ -65,6 +65,7 @@ public:
if(name == Video::Shader) { if(name == Video::Shader) {
settings.shader = any_cast<const char*>(value); settings.shader = any_cast<const char*>(value);
OpenGL::set_shader(settings.shader); OpenGL::set_shader(settings.shader);
settings.filter = any_cast<unsigned>(OpenGL::shaderfilter);
return true; return true;
} }

View File

@ -4,7 +4,6 @@
namespace SNES { namespace SNES {
namespace Info { namespace Info {
static const char Name[] = "bsnes"; static const char Name[] = "bsnes";
static const char Version[] = "082.18";
static const unsigned SerializerVersion = 22; static const unsigned SerializerVersion = 22;
} }
} }

View File

@ -3,11 +3,11 @@ Config *config = 0;
Config::Config() { Config::Config() {
attach(video.driver = "", "Video::Driver"); attach(video.driver = "", "Video::Driver");
attach(video.shader = "", "Video::Shader"); attach(video.filter = "None", "Video::Filter");
attach(video.shader = "None", "Video::Shader");
attach(video.synchronize = true, "Video::Synchronize"); attach(video.synchronize = true, "Video::Synchronize");
attach(video.enableOverscan = false, "Video::EnableOverscan"); attach(video.enableOverscan = false, "Video::EnableOverscan");
attach(video.correctAspectRatio = true, "Video::CorrectAspectRatio"); attach(video.correctAspectRatio = true, "Video::CorrectAspectRatio");
attach(video.smooth = true, "Video::Smooth");
attach(video.brightness = 100, "Video::Brightness"); attach(video.brightness = 100, "Video::Brightness");
attach(video.contrast = 100, "Video::Contrast"); attach(video.contrast = 100, "Video::Contrast");

View File

@ -1,11 +1,11 @@
struct Config : public configuration { struct Config : public configuration {
struct Video { struct Video {
string driver; string driver;
string filter;
string shader; string shader;
bool synchronize; bool synchronize;
bool enableOverscan; bool enableOverscan;
bool correctAspectRatio; bool correctAspectRatio;
bool smooth;
unsigned brightness; unsigned brightness;
unsigned contrast; unsigned contrast;

View File

@ -59,8 +59,13 @@ MainWindow::MainWindow() {
gameBoyCartridgeUnload.setText("Unload Cartridge"); gameBoyCartridgeUnload.setText("Unload Cartridge");
settingsMenu.setText("Settings"); settingsMenu.setText("Settings");
settingsVideoShaders.setText("Video Shader"); settingsVideoFilter.setText("Video Filter");
setupVideoShaders(); settingsVideoFilterNone.setText("None");
setupVideoFilters();
settingsVideoShader.setText("Video Shader");
settingsVideoShaderNone.setText("None");
settingsVideoShaderBlur.setText("Blur");
setupVideoShaders();
settingsSynchronizeVideo.setText("Synchronize Video"); settingsSynchronizeVideo.setText("Synchronize Video");
settingsSynchronizeVideo.setChecked(config->video.synchronize); settingsSynchronizeVideo.setChecked(config->video.synchronize);
settingsSynchronizeAudio.setText("Synchronize Audio"); settingsSynchronizeAudio.setText("Synchronize Audio");
@ -69,8 +74,6 @@ MainWindow::MainWindow() {
settingsEnableOverscan.setChecked(config->video.enableOverscan); settingsEnableOverscan.setChecked(config->video.enableOverscan);
settingsCorrectAspectRatio.setText("Correct Aspect Ratio"); settingsCorrectAspectRatio.setText("Correct Aspect Ratio");
settingsCorrectAspectRatio.setChecked(config->video.correctAspectRatio); settingsCorrectAspectRatio.setChecked(config->video.correctAspectRatio);
settingsSmoothVideo.setText("Smooth Video Output");
settingsSmoothVideo.setChecked(config->video.smooth);
settingsMuteAudio.setText("Mute Audio"); settingsMuteAudio.setText("Mute Audio");
settingsMuteAudio.setChecked(config->audio.mute); settingsMuteAudio.setChecked(config->audio.mute);
settingsConfiguration.setText("Configuration ..."); settingsConfiguration.setText("Configuration ...");
@ -88,11 +91,9 @@ MainWindow::MainWindow() {
toolsStateLoad3.setText("Slot 3"); toolsStateLoad3.setText("Slot 3");
toolsStateLoad4.setText("Slot 4"); toolsStateLoad4.setText("Slot 4");
toolsStateLoad5.setText("Slot 5"); toolsStateLoad5.setText("Slot 5");
toolsCaptureMouse.setText("Capture Mouse");
toolsShrinkWindow.setText("Shrink Window"); toolsShrinkWindow.setText("Shrink Window");
toolsCheatEditor.setText("Cheat Editor ..."); toolsCheatEditor.setText("Cheat Editor ...");
toolsStateManager.setText("State Manager ..."); toolsStateManager.setText("State Manager ...");
toolsTest.setText("Test");
append(cartridgeMenu); append(cartridgeMenu);
cartridgeMenu.append(cartridgeLoadNES); cartridgeMenu.append(cartridgeLoadNES);
@ -144,16 +145,25 @@ MainWindow::MainWindow() {
gameBoyMenu.append(gameBoyCartridgeUnload); gameBoyMenu.append(gameBoyCartridgeUnload);
append(settingsMenu); append(settingsMenu);
settingsMenu.append(settingsVideoShaders); settingsMenu.append(settingsVideoFilter);
for(unsigned n = 0; n < videoShaderCount; n++) settingsVideoFilter.append(settingsVideoFilterNone);
settingsVideoShaders.append(settingsVideoShader[n]); if(videoFilterName.size())
settingsVideoFilter.append(settingsVideoFilterSeparator);
for(unsigned n = 0; n < videoFilterName.size(); n++)
settingsVideoFilter.append(settingsVideoFilterList[n]);
settingsMenu.append(settingsVideoShader);
settingsVideoShader.append(settingsVideoShaderNone);
settingsVideoShader.append(settingsVideoShaderBlur);
if(videoShaderName.size())
settingsVideoShader.append(settingsVideoShaderSeparator);
for(unsigned n = 0; n < videoShaderName.size(); n++)
settingsVideoShader.append(settingsVideoShaderList[n]);
settingsMenu.append(settingsSeparator1); settingsMenu.append(settingsSeparator1);
settingsMenu.append(settingsSynchronizeVideo); settingsMenu.append(settingsSynchronizeVideo);
settingsMenu.append(settingsSynchronizeAudio); settingsMenu.append(settingsSynchronizeAudio);
settingsMenu.append(settingsSeparator2); settingsMenu.append(settingsSeparator2);
settingsMenu.append(settingsEnableOverscan); settingsMenu.append(settingsEnableOverscan);
settingsMenu.append(settingsCorrectAspectRatio); settingsMenu.append(settingsCorrectAspectRatio);
settingsMenu.append(settingsSmoothVideo);
settingsMenu.append(settingsMuteAudio); settingsMenu.append(settingsMuteAudio);
settingsMenu.append(settingsSeparator3); settingsMenu.append(settingsSeparator3);
settingsMenu.append(settingsConfiguration); settingsMenu.append(settingsConfiguration);
@ -171,14 +181,10 @@ MainWindow::MainWindow() {
toolsStateLoad.append(toolsStateLoad3); toolsStateLoad.append(toolsStateLoad3);
toolsStateLoad.append(toolsStateLoad4); toolsStateLoad.append(toolsStateLoad4);
toolsStateLoad.append(toolsStateLoad5); toolsStateLoad.append(toolsStateLoad5);
toolsMenu.append(toolsSeparator1); toolsMenu.append(toolsSeparator);
toolsMenu.append(toolsCaptureMouse);
toolsMenu.append(toolsShrinkWindow); toolsMenu.append(toolsShrinkWindow);
toolsMenu.append(toolsSeparator2);
toolsMenu.append(toolsCheatEditor); toolsMenu.append(toolsCheatEditor);
toolsMenu.append(toolsStateManager); toolsMenu.append(toolsStateManager);
toolsMenu.append(toolsSeparator3);
toolsMenu.append(toolsTest);
setMenuVisible(); setMenuVisible();
@ -247,6 +253,21 @@ MainWindow::MainWindow() {
gameBoyPower.onTick = { &Interface::power, interface }; gameBoyPower.onTick = { &Interface::power, interface };
gameBoyCartridgeUnload.onTick = { &Interface::unloadCartridge, interface }; gameBoyCartridgeUnload.onTick = { &Interface::unloadCartridge, interface };
settingsVideoFilterNone.onTick = [&] {
config->video.filter = "None";
utility->bindVideoFilter();
};
settingsVideoShaderNone.onTick = [&] {
config->video.shader = "None";
utility->bindVideoShader();
};
settingsVideoShaderBlur.onTick = [&] {
config->video.shader = "Blur";
utility->bindVideoShader();
};
settingsSynchronizeVideo.onTick = [&] { settingsSynchronizeVideo.onTick = [&] {
config->video.synchronize = settingsSynchronizeVideo.checked(); config->video.synchronize = settingsSynchronizeVideo.checked();
video.set(Video::Synchronize, config->video.synchronize); video.set(Video::Synchronize, config->video.synchronize);
@ -267,11 +288,6 @@ MainWindow::MainWindow() {
utility->resizeMainWindow(); utility->resizeMainWindow();
}; };
settingsSmoothVideo.onTick = [&] {
config->video.smooth = settingsSmoothVideo.checked();
video.set(Video::Filter, config->video.smooth == false ? 0u : 1u);
};
settingsMuteAudio.onTick = [&] { settingsMuteAudio.onTick = [&] {
config->audio.mute = settingsMuteAudio.checked(); config->audio.mute = settingsMuteAudio.checked();
dspaudio.setVolume(config->audio.mute == false ? 1.0 : 0.0); dspaudio.setVolume(config->audio.mute == false ? 1.0 : 0.0);
@ -291,16 +307,10 @@ MainWindow::MainWindow() {
toolsStateLoad4.onTick = [&] { interface->loadState(4); }; toolsStateLoad4.onTick = [&] { interface->loadState(4); };
toolsStateLoad5.onTick = [&] { interface->loadState(5); }; toolsStateLoad5.onTick = [&] { interface->loadState(5); };
toolsCaptureMouse.onTick = [&] { input.acquire(); };
toolsShrinkWindow.onTick = [&] { utility->resizeMainWindow(true); }; toolsShrinkWindow.onTick = [&] { utility->resizeMainWindow(true); };
toolsCheatEditor.onTick = [&] { cheatEditor->setVisible(); }; toolsCheatEditor.onTick = [&] { cheatEditor->setVisible(); };
toolsStateManager.onTick = [&] { stateManager->setVisible(); }; toolsStateManager.onTick = [&] { stateManager->setVisible(); };
toolsTest.onTick = [&] {
NES::cpu.trace = toolsTest.checked();
};
synchronize(); synchronize();
} }
@ -314,33 +324,56 @@ void MainWindow::synchronize() {
} }
} }
void MainWindow::setupVideoFilters() {
lstring files = directory::files({ application->userpath, "filters/" }, "*.filter");
reference_array<RadioItem&> group;
settingsVideoFilterList = new RadioItem[files.size()];
for(unsigned n = 0; n < files.size(); n++) {
string name = files[n];
videoFilterName.append({ application->userpath, "filters/", name });
if(auto position = name.position(".filter")) name[position()] = 0;
settingsVideoFilterList[n].setText(name);
settingsVideoFilterList[n].onTick = [&, n] {
config->video.filter = videoFilterName[n];
utility->bindVideoFilter();
};
}
group.append(settingsVideoFilterNone);
for(unsigned n = 0; n < files.size(); n++) group.append(settingsVideoFilterList[n]);
RadioItem::group(group);
if(config->video.filter == "None") settingsVideoFilterNone.setChecked();
for(unsigned n = 0; n < files.size(); n++)
if(config->video.filter == videoFilterName[n]) settingsVideoFilterList[n].setChecked();
}
void MainWindow::setupVideoShaders() { void MainWindow::setupVideoShaders() {
lstring files = directory::files({ application->userpath, "shaders/" }, { "*.", config->video.driver, ".shader" }); lstring files = directory::files({ application->userpath, "shaders/" }, { "*.", config->video.driver, ".shader" });
videoShaderCount = 1 + files.size();
reference_array<RadioItem&> group; reference_array<RadioItem&> group;
unsigned active = 0;
settingsVideoShader = new RadioItem[videoShaderCount]; settingsVideoShaderList = new RadioItem[files.size()];
for(unsigned n = 0; n < videoShaderCount; n++) { for(unsigned n = 0; n < files.size(); n++) {
string name; string name = files[n];
if(n == 0) { videoShaderName.append({ application->userpath, "shaders/", name });
name = "None"; if(auto position = name.position(string{ ".", config->video.driver, ".shader" })) name[position()] = 0;
videoShaderName.append("");
} else { settingsVideoShaderList[n].setText(name);
name = files[n - 1]; settingsVideoShaderList[n].onTick = [&, n] {
videoShaderName.append({ application->userpath, "shaders/", name });
if(auto position = name.position(string{ ".", config->video.driver, ".shader" })) name[position()] = 0;
}
if(config->video.shader == videoShaderName[n]) active = n;
settingsVideoShader[n].setText(name);
settingsVideoShader[n].onTick = [&, n] {
config->video.shader = videoShaderName[n]; config->video.shader = videoShaderName[n];
utility->bindVideoShader(); utility->bindVideoShader();
}; };
} }
for(unsigned n = 0; n < videoShaderCount; n++) group.append(settingsVideoShader[n]); group.append(settingsVideoShaderNone);
group.append(settingsVideoShaderBlur);
for(unsigned n = 0; n < files.size(); n++) group.append(settingsVideoShaderList[n]);
RadioItem::group(group); RadioItem::group(group);
settingsVideoShader[active].setChecked();
if(config->video.shader == "None") settingsVideoShaderNone.setChecked();
if(config->video.shader == "Blur") settingsVideoShaderBlur.setChecked();
for(unsigned n = 0; n < files.size(); n++)
if(config->video.shader == videoShaderName[n]) settingsVideoShaderList[n].setChecked();
} }

View File

@ -40,15 +40,21 @@ struct MainWindow : Window {
Item gameBoyCartridgeUnload; Item gameBoyCartridgeUnload;
Menu settingsMenu; Menu settingsMenu;
Menu settingsVideoShaders; Menu settingsVideoFilter;
RadioItem *settingsVideoShader; RadioItem settingsVideoFilterNone;
Separator settingsVideoFilterSeparator;
RadioItem *settingsVideoFilterList;
Menu settingsVideoShader;
RadioItem settingsVideoShaderNone;
RadioItem settingsVideoShaderBlur;
Separator settingsVideoShaderSeparator;
RadioItem *settingsVideoShaderList;
Separator settingsSeparator1; Separator settingsSeparator1;
CheckItem settingsSynchronizeVideo; CheckItem settingsSynchronizeVideo;
CheckItem settingsSynchronizeAudio; CheckItem settingsSynchronizeAudio;
Separator settingsSeparator2; Separator settingsSeparator2;
CheckItem settingsEnableOverscan; CheckItem settingsEnableOverscan;
CheckItem settingsCorrectAspectRatio; CheckItem settingsCorrectAspectRatio;
CheckItem settingsSmoothVideo;
CheckItem settingsMuteAudio; CheckItem settingsMuteAudio;
Separator settingsSeparator3; Separator settingsSeparator3;
Item settingsConfiguration; Item settingsConfiguration;
@ -66,22 +72,19 @@ struct MainWindow : Window {
Item toolsStateLoad3; Item toolsStateLoad3;
Item toolsStateLoad4; Item toolsStateLoad4;
Item toolsStateLoad5; Item toolsStateLoad5;
Separator toolsSeparator1; Separator toolsSeparator;
Item toolsCaptureMouse;
Item toolsShrinkWindow; Item toolsShrinkWindow;
Separator toolsSeparator2;
Item toolsCheatEditor; Item toolsCheatEditor;
Item toolsStateManager; Item toolsStateManager;
Separator toolsSeparator3;
CheckItem toolsTest;
void synchronize(); void synchronize();
MainWindow(); MainWindow();
private: private:
unsigned videoShaderCount; lstring videoFilterName;
lstring videoShaderName; lstring videoShaderName;
void setupVideoFilters();
void setupVideoShaders(); void setupVideoShaders();
}; };

View File

@ -5,6 +5,23 @@
#include "gameboy.cpp" #include "gameboy.cpp"
Interface *interface = 0; Interface *interface = 0;
Filter filter;
void Filter::render(const uint16_t *input, unsigned inputPitch, unsigned inputWidth, unsigned inputHeight) {
width = inputWidth, height = inputHeight;
dl_size(width, height);
dl_render(data, pitch, input, inputPitch, inputWidth, inputHeight);
}
Filter::Filter() {
data = new uint16_t[1024 * 1024];
pitch = 1024 * sizeof(uint16_t);
}
Filter::~Filter() {
delete[] data;
}
void Interface::bindControllers() { void Interface::bindControllers() {
switch(mode()) { switch(mode()) {
case Mode::NES: case Mode::NES:
@ -64,6 +81,7 @@ bool Interface::loadCartridge(const string &filename) {
void Interface::unloadCartridge() { void Interface::unloadCartridge() {
if(cartridgeLoaded() == false) return; if(cartridgeLoaded() == false) return;
cheatDatabase->setVisible(false);
cheatEditor->save({ baseName, ".cht" }); cheatEditor->save({ baseName, ".cht" });
stateManager->save({ baseName, ".bsa" }, 0u); stateManager->save({ baseName, ".bsa" }, 0u);
setCheatCodes(); setCheatCodes();
@ -151,6 +169,15 @@ void Interface::setCheatCodes(const lstring &list) {
} }
} }
string Interface::sha256() {
switch(mode()) {
case Mode::NES: return NES::cartridge.sha256();
case Mode::SNES: return SNES::cartridge.sha256();
case Mode::GameBoy: return GameBoy::cartridge.sha256();
}
return "{None}";
}
Interface::Interface() { Interface::Interface() {
mode = Mode::None; mode = Mode::None;
palette.update(); palette.update();
@ -166,6 +193,14 @@ void Interface::videoRefresh(const uint16_t *input, unsigned inputPitch, unsigne
uint32_t *output; uint32_t *output;
unsigned outputPitch; unsigned outputPitch;
if(filter.opened()) {
filter.render(input, inputPitch, width, height);
input = filter.data;
inputPitch = filter.pitch;
width = filter.width;
height = filter.height;
}
if(video.lock(output, outputPitch, width, height)) { if(video.lock(output, outputPitch, width, height)) {
inputPitch >>= 1, outputPitch >>= 2; inputPitch >>= 1, outputPitch >>= 2;

View File

@ -4,6 +4,21 @@
#include "snes.hpp" #include "snes.hpp"
#include "gameboy.hpp" #include "gameboy.hpp"
struct Filter : public library {
function<void (unsigned&, unsigned&)> dl_size;
function<void (uint16_t*, unsigned, const uint16_t*, unsigned, unsigned, unsigned)> dl_render;
void render(const uint16_t*, unsigned, unsigned, unsigned);
Filter();
~Filter();
uint16_t *data;
unsigned pitch;
unsigned width;
unsigned height;
};
extern Filter filter;
struct Interface : property<Interface> { struct Interface : property<Interface> {
enum class Mode : unsigned { None, SNES, NES, GameBoy }; enum class Mode : unsigned { None, SNES, NES, GameBoy };
readonly<Mode> mode; readonly<Mode> mode;
@ -27,6 +42,7 @@ struct Interface : property<Interface> {
bool saveState(unsigned slot); bool saveState(unsigned slot);
bool loadState(unsigned slot); bool loadState(unsigned slot);
void setCheatCodes(const lstring &list = lstring{}); void setCheatCodes(const lstring &list = lstring{});
string sha256();
Interface(); Interface();

View File

@ -41,7 +41,7 @@ Application::Application(int argc, char **argv) {
inputManager = new InputManager; inputManager = new InputManager;
utility = new Utility; utility = new Utility;
title = "bsnes"; title = "bsnes v082.19";
#if defined(PLATFORM_WIN) #if defined(PLATFORM_WIN)
normalFont = "Tahoma, 8"; normalFont = "Tahoma, 8";
@ -59,6 +59,7 @@ Application::Application(int argc, char **argv) {
slotLoader = new SlotLoader; slotLoader = new SlotLoader;
dipSwitches = new DipSwitches; dipSwitches = new DipSwitches;
settingsWindow = new SettingsWindow; settingsWindow = new SettingsWindow;
cheatDatabase = new CheatDatabase;
cheatEditor = new CheatEditor; cheatEditor = new CheatEditor;
stateManager = new StateManager; stateManager = new StateManager;
windowManager->loadGeometry(); windowManager->loadGeometry();
@ -69,8 +70,8 @@ Application::Application(int argc, char **argv) {
video.driver(config->video.driver); video.driver(config->video.driver);
video.set(Video::Handle, mainWindow->viewport.handle()); video.set(Video::Handle, mainWindow->viewport.handle());
video.set(Video::Synchronize, config->video.synchronize); video.set(Video::Synchronize, config->video.synchronize);
video.set(Video::Filter, config->video.smooth == false ? 0u : 1u);
video.init(); video.init();
utility->bindVideoFilter();
utility->bindVideoShader(); utility->bindVideoShader();
audio.driver(config->audio.driver); audio.driver(config->audio.driver);
@ -104,6 +105,7 @@ Application::Application(int argc, char **argv) {
Application::~Application() { Application::~Application() {
delete stateManager; delete stateManager;
delete cheatEditor; delete cheatEditor;
delete cheatDatabase;
delete settingsWindow; delete settingsWindow;
delete dipSwitches; delete dipSwitches;
delete slotLoader; delete slotLoader;

View File

@ -0,0 +1,95 @@
CheatDatabase *cheatDatabase = 0;
CheatDatabase::CheatDatabase() {
setGeometry({ 128, 128, 640, 400 });
windowManager->append(this, "CheatDatabase");
layout.setMargin(5);
cheatList.setCheckable();
selectAllButton.setText("Select All");
unselectAllButton.setText("Unselect All");
acceptButton.setText("Add Codes");
append(layout);
layout.append(cheatList, ~0, ~0, 5);
layout.append(controlLayout, ~0, 0);
controlLayout.append(selectAllButton, 100, 0, 5);
controlLayout.append(unselectAllButton, 100, 0);
controlLayout.append(spacer, ~0, 0);
controlLayout.append(acceptButton, 80, 0);
selectAllButton.onTick = [&] {
foreach(item, cheatCode, n) cheatList.setChecked(n, true);
};
unselectAllButton.onTick = [&] {
foreach(item, cheatCode, n) cheatList.setChecked(n, false);
};
acceptButton.onTick = { &CheatDatabase::addCodes, this };
}
void CheatDatabase::findCodes() {
string data;
data.readfile({ application->userpath, "cheats.xml" });
if(auto position = data.position(interface->sha256())) {
auto startPosition = strpos((const char*)data + position(), ">");
auto endPosition = strpos((const char*)data + position(), "</cartridge>");
string xmlData = {
"<cartridge>\n",
substr((const char*)data + position() + 1, startPosition(), endPosition() - startPosition() - 1),
"</cartridge>\n"
};
setTitle("");
cheatList.reset();
cheatCode.reset();
xml_element document = xml_parse(xmlData);
foreach(root, document.element) {
if(root.name == "cartridge") {
foreach(node, root.element) {
if(node.name == "name") {
setTitle(node.parse());
} else if(node.name == "cheat") {
string description, code;
foreach(element, node.element) {
if(element.name == "description") {
description = element.parse();
} else if(element.name == "code") {
code.append(element.parse(), "+");
}
}
code.rtrim<1>("+");
code.append("\t");
code.append(description);
cheatList.append(description);
cheatCode.append(code);
}
}
}
}
setVisible();
} else {
MessageWindow::information(*cheatEditor, "Sorry, no cheat codes were found for this cartridge.");
}
}
void CheatDatabase::addCodes() {
foreach(code, cheatCode, n) {
if(cheatList.checked(n)) {
lstring part;
part.split<1>("\t", code);
if(cheatEditor->addCode(part[0], part[1]) == false) {
MessageWindow::warning(*this, "Ran out of empty slots for cheat codes.\nNot all cheat codes were added.");
break;
}
}
}
setVisible(false);
cheatEditor->updateUI();
cheatEditor->updateInterface();
cheatEditor->synchronize();
}

View File

@ -0,0 +1,18 @@
struct CheatDatabase : Window {
VerticalLayout layout;
ListView cheatList;
HorizontalLayout controlLayout;
Button selectAllButton;
Button unselectAllButton;
Widget spacer;
Button acceptButton;
void findCodes();
void addCodes();
CheatDatabase();
private:
lstring cheatCode;
};
extern CheatDatabase *cheatDatabase;

View File

@ -11,7 +11,6 @@ CheatEditor::CheatEditor() {
codeLabel.setText("Code(s):"); codeLabel.setText("Code(s):");
descLabel.setText("Description:"); descLabel.setText("Description:");
findButton.setText("Find Codes ..."); findButton.setText("Find Codes ...");
findButton.setEnabled(false);
clearAllButton.setText("Clear All"); clearAllButton.setText("Clear All");
clearButton.setText("Clear"); clearButton.setText("Clear");
@ -38,6 +37,7 @@ CheatEditor::CheatEditor() {
cheatList.onTick = [&](unsigned) { updateInterface(); }; cheatList.onTick = [&](unsigned) { updateInterface(); };
codeEdit.onChange = { &CheatEditor::updateCode, this }; codeEdit.onChange = { &CheatEditor::updateCode, this };
descEdit.onChange = { &CheatEditor::updateDesc, this }; descEdit.onChange = { &CheatEditor::updateDesc, this };
findButton.onTick = { &CheatDatabase::findCodes, cheatDatabase };
clearAllButton.onTick = { &CheatEditor::clearAll, this }; clearAllButton.onTick = { &CheatEditor::clearAll, this };
clearButton.onTick = { &CheatEditor::clearSelected, this }; clearButton.onTick = { &CheatEditor::clearSelected, this };
} }
@ -196,3 +196,15 @@ bool CheatEditor::save(const string &filename) {
return true; return true;
} }
bool CheatEditor::addCode(const string &code, const string &description) {
for(unsigned n = 0; n < 128; n++) {
if(cheatText[n][Code] == "" && cheatText[n][Desc] == "") {
cheatList.setChecked(n, false);
cheatText[n][Code] = code;
cheatText[n][Desc] = description;
return true;
}
}
return false;
}

View File

@ -24,6 +24,7 @@ struct CheatEditor : Window {
void reset(); void reset();
bool load(const string &filename); bool load(const string &filename);
bool save(const string &filename); bool save(const string &filename);
bool addCode(const string &code, const string &description);
CheatEditor(); CheatEditor();

View File

@ -1,3 +1,4 @@
#include "../base.hpp" #include "../base.hpp"
#include "cheat-database.cpp"
#include "cheat-editor.cpp" #include "cheat-editor.cpp"
#include "state-manager.cpp" #include "state-manager.cpp"

View File

@ -1,2 +1,3 @@
#include "cheat-database.hpp"
#include "cheat-editor.hpp" #include "cheat-editor.hpp"
#include "state-manager.hpp" #include "state-manager.hpp"

View File

@ -16,19 +16,19 @@ void Utility::setMode(Interface::Mode mode) {
} }
else if(mode == Interface::Mode::NES) { else if(mode == Interface::Mode::NES) {
mainWindow->setTitle({ notdir(interface->baseName), " - ", NES::Info::Name, " v", NES::Info::Version }); mainWindow->setTitle(notdir(interface->baseName));
mainWindow->nesMenu.setVisible(true); mainWindow->nesMenu.setVisible(true);
dspaudio.setChannels(1); dspaudio.setChannels(1);
} }
else if(mode == Interface::Mode::SNES) { else if(mode == Interface::Mode::SNES) {
mainWindow->setTitle({ notdir(interface->baseName), " - ", SNES::Info::Name, " v", SNES::Info::Version }); mainWindow->setTitle(notdir(interface->baseName));
mainWindow->snesMenu.setVisible(true); mainWindow->snesMenu.setVisible(true);
dspaudio.setChannels(2); dspaudio.setChannels(2);
} }
else if(mode == Interface::Mode::GameBoy) { else if(mode == Interface::Mode::GameBoy) {
mainWindow->setTitle({ notdir(interface->baseName), " - ", GameBoy::Info::Name, " v", GameBoy::Info::Version }); mainWindow->setTitle(notdir(interface->baseName));
mainWindow->gameBoyMenu.setVisible(true); mainWindow->gameBoyMenu.setVisible(true);
dspaudio.setChannels(2); dspaudio.setChannels(2);
} }
@ -107,10 +107,28 @@ void Utility::toggleFullScreen() {
resizeMainWindow(); resizeMainWindow();
} }
void Utility::bindVideoFilter() {
if(filter.opened()) filter.close();
if(config->video.filter == "None") return;
if(filter.open_absolute(config->video.filter)) {
filter.dl_size = filter.sym("filter_size");
filter.dl_render = filter.sym("filter_render");
if(!filter.dl_size || !filter.dl_render) filter.close();
}
}
void Utility::bindVideoShader() { void Utility::bindVideoShader() {
string data; if(config->video.shader == "None") {
data.readfile(config->video.shader); video.set(Video::Filter, 0u);
video.set(Video::Shader, (const char*)data); video.set(Video::Shader, (const char*)"");
} else if(config->video.shader == "Blur") {
video.set(Video::Filter, 1u);
video.set(Video::Shader, (const char*)"");
} else {
string data;
data.readfile(config->video.shader);
video.set(Video::Shader, (const char*)data);
}
} }
void Utility::updateStatus() { void Utility::updateStatus() {

View File

@ -2,6 +2,7 @@ struct Utility {
void setMode(Interface::Mode mode); void setMode(Interface::Mode mode);
void resizeMainWindow(bool shrink = false); void resizeMainWindow(bool shrink = false);
void toggleFullScreen(); void toggleFullScreen();
void bindVideoFilter();
void bindVideoShader(); void bindVideoShader();
void updateStatus(); void updateStatus();