From 69ed35db993eaaf0d1623dbdfc36fb985be96b10 Mon Sep 17 00:00:00 2001 From: Tim Allen Date: Mon, 19 Sep 2011 22:34:18 +1000 Subject: [PATCH] Update to v082r17 release. byuu says: Adds BS-X/Slotted/SufamiTurbo/SGB cartridge loading. Calling it Satellaview as I'm more partial to that at the moment. FileBrowser now remembers your folders per filter type like before, and will keep your place in the list if you don't switch away. I wanted there to be ONE slot loader, so the loader will show a grayed out secondary slot on non-ST loading, but it's more consistent to only have one window instead of two for geometry placement. Removed help menu. Will try and work it in somewhere unobtrusive later on I suppose. Added timed messages and the usual "no cart loaded / paused" messages and such. --- bsnes/nall/file.hpp | 1 + bsnes/nall/string/base.hpp | 3 + bsnes/nall/string/core.hpp | 13 ++ bsnes/nes/nes.hpp | 2 +- bsnes/phoenix/core/core.cpp | 4 + bsnes/phoenix/core/core.hpp | 1 + bsnes/snes/interface/interface.cpp | 39 +++++- bsnes/snes/interface/interface.hpp | 12 +- bsnes/snes/snes.hpp | 2 +- bsnes/ui/base.hpp | 3 +- bsnes/ui/config/config.cpp | 4 +- bsnes/ui/config/config.hpp | 6 +- bsnes/ui/general/file-browser.cpp | 84 ++++++++++--- bsnes/ui/general/file-browser.hpp | 16 ++- bsnes/ui/general/general.cpp | 1 + bsnes/ui/general/general.hpp | 1 + bsnes/ui/general/main-window.cpp | 42 +++---- bsnes/ui/general/main-window.hpp | 8 +- bsnes/ui/general/slot-loader.cpp | 185 +++++++++++++++++++++++++++++ bsnes/ui/general/slot-loader.hpp | 29 +++++ bsnes/ui/input/user-interface.cpp | 11 +- bsnes/ui/input/user-interface.hpp | 1 + bsnes/ui/interface/gameboy.cpp | 5 +- bsnes/ui/interface/interface.cpp | 79 ++++-------- bsnes/ui/interface/interface.hpp | 14 +-- bsnes/ui/interface/nes.cpp | 5 +- bsnes/ui/interface/snes.cpp | 88 +++++++++++++- bsnes/ui/interface/snes.hpp | 4 + bsnes/ui/main.cpp | 12 +- bsnes/ui/tools/cheat-editor.cpp | 2 +- bsnes/ui/tools/state-manager.cpp | 2 +- bsnes/ui/utility/utility.cpp | 37 +++++- bsnes/ui/utility/utility.hpp | 12 +- 33 files changed, 594 insertions(+), 134 deletions(-) create mode 100755 bsnes/ui/general/slot-loader.cpp create mode 100755 bsnes/ui/general/slot-loader.hpp diff --git a/bsnes/nall/file.hpp b/bsnes/nall/file.hpp index 40453448..8447f3ab 100755 --- a/bsnes/nall/file.hpp +++ b/bsnes/nall/file.hpp @@ -23,6 +23,7 @@ namespace nall { enum class time : unsigned { create, modify, access }; static bool read(const string &filename, uint8_t *&data, unsigned &size) { + data = 0; file fp; if(fp.open(filename, mode::read) == false) return false; size = fp.size(); diff --git a/bsnes/nall/string/base.hpp b/bsnes/nall/string/base.hpp index ef331515..86c42c20 100755 --- a/bsnes/nall/string/base.hpp +++ b/bsnes/nall/string/base.hpp @@ -103,6 +103,9 @@ namespace nall { template inline lstring& qsplit(const char*, const char*); template inline lstring& iqsplit(const char*, const char*); + inline bool operator==(const lstring&) const; + inline bool operator!=(const lstring&) const; + lstring(); lstring(std::initializer_list); diff --git a/bsnes/nall/string/core.hpp b/bsnes/nall/string/core.hpp index 9903a6f7..e2af4eea 100755 --- a/bsnes/nall/string/core.hpp +++ b/bsnes/nall/string/core.hpp @@ -135,6 +135,19 @@ optional lstring::find(const char *key) const { return { false, 0 }; } +bool lstring::operator==(const lstring &source) const { + if(this == &source) return true; + if(size() != source.size()) return false; + for(unsigned n = 0; n < size(); n++) { + if(operator[](n) != source[n]) return false; + } + return true; +} + +bool lstring::operator!=(const lstring &source) const { + return !operator==(source); +} + inline lstring::lstring() { } diff --git a/bsnes/nes/nes.hpp b/bsnes/nes/nes.hpp index 8269c317..40280caa 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.12"; + static const char Version[] = "000.13"; } } diff --git a/bsnes/phoenix/core/core.cpp b/bsnes/phoenix/core/core.cpp index 726e0153..204996c3 100755 --- a/bsnes/phoenix/core/core.cpp +++ b/bsnes/phoenix/core/core.cpp @@ -290,6 +290,10 @@ void Window::setWidgetFont(const string &font) { return p.setWidgetFont(font); } +string Window::statusText() { + return state.statusText; +} + void Window::synchronizeLayout() { setGeometry(geometry()); } diff --git a/bsnes/phoenix/core/core.hpp b/bsnes/phoenix/core/core.hpp index 5c824d1c..14087c87 100755 --- a/bsnes/phoenix/core/core.hpp +++ b/bsnes/phoenix/core/core.hpp @@ -153,6 +153,7 @@ struct Window : private nall::base_from_member, Object { void setTitle(const nall::string &text); void setVisible(bool visible = true); void setWidgetFont(const nall::string &font); + nall::string statusText(); void synchronizeLayout(); Window(); diff --git a/bsnes/snes/interface/interface.cpp b/bsnes/snes/interface/interface.cpp index 25404c8d..5f6fba95 100755 --- a/bsnes/snes/interface/interface.cpp +++ b/bsnes/snes/interface/interface.cpp @@ -27,9 +27,38 @@ bool Interface::cartridgeLoaded() { return cartridge.loaded(); } -void Interface::loadCartridge(const string &xml, const uint8_t *data, unsigned size) { - cartridge.rom.copy(data, size); - cartridge.load(Cartridge::Mode::Normal, { xml }); +void Interface::loadCartridge(const CartridgeData &base) { + cartridge.rom.copy(base.data, base.size); + cartridge.load(Cartridge::Mode::Normal, { base.xml }); + system.power(); +} + +void Interface::loadSatellaviewSlottedCartridge(const CartridgeData &base, const CartridgeData &slot) { + cartridge.rom.copy(base.data, base.size); + if(slot.data) bsxflash.memory.copy(slot.data, slot.size); + cartridge.load(Cartridge::Mode::BsxSlotted, { base.xml, slot.xml }); + system.power(); +} + +void Interface::loadSatellaviewCartridge(const CartridgeData &base, const CartridgeData &slot) { + cartridge.rom.copy(base.data, base.size); + if(slot.data) bsxflash.memory.copy(slot.data, slot.size); + cartridge.load(Cartridge::Mode::Bsx, { base.xml, slot.xml }); + system.power(); +} + +void Interface::loadSufamiTurboCartridge(const CartridgeData &base, const CartridgeData &slotA, const CartridgeData &slotB) { + cartridge.rom.copy(base.data, base.size); + if(slotA.data) sufamiturbo.slotA.rom.copy(slotA.data, slotA.size); + if(slotB.data) sufamiturbo.slotB.rom.copy(slotB.data, slotB.size); + cartridge.load(Cartridge::Mode::SufamiTurbo, { base.xml, slotA.xml, slotB.xml }); + system.power(); +} + +void Interface::loadSuperGameBoyCartridge(const CartridgeData &base, const CartridgeData &slot) { + cartridge.rom.copy(base.data, base.size); + GameBoy::cartridge.load(slot.xml, slot.data, slot.size); + cartridge.load(Cartridge::Mode::SuperGameBoy, { base.xml, "" }); system.power(); } @@ -59,6 +88,10 @@ bool Interface::unserialize(serializer &s) { } void Interface::setCheats(const lstring &list) { + if(cartridge.mode() == Cartridge::Mode::SuperGameBoy) { + return icd2.setCheats(list); + } + cheat.reset(); foreach(code, list) { lstring codelist; diff --git a/bsnes/snes/interface/interface.hpp b/bsnes/snes/interface/interface.hpp index e94f75de..c91c98a6 100755 --- a/bsnes/snes/interface/interface.hpp +++ b/bsnes/snes/interface/interface.hpp @@ -8,8 +8,18 @@ public: virtual void connect(bool port, Input::Device device); + struct CartridgeData { + string xml; + const uint8_t *data; + unsigned size; + }; + virtual bool cartridgeLoaded(); - virtual void loadCartridge(const string &xml, const uint8_t *data, unsigned size); + virtual void loadCartridge(const CartridgeData &base); + virtual void loadSatellaviewSlottedCartridge(const CartridgeData &base, const CartridgeData &slot); + virtual void loadSatellaviewCartridge(const CartridgeData &base, const CartridgeData &slot); + virtual void loadSufamiTurboCartridge(const CartridgeData &base, const CartridgeData &slotA, const CartridgeData &slotB); + virtual void loadSuperGameBoyCartridge(const CartridgeData &base, const CartridgeData &slot); virtual void unloadCartridge(); virtual void power(); diff --git a/bsnes/snes/snes.hpp b/bsnes/snes/snes.hpp index abbfe616..31871ed9 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.16"; + static const char Version[] = "082.17"; static const unsigned SerializerVersion = 22; } } diff --git a/bsnes/ui/base.hpp b/bsnes/ui/base.hpp index d2b8326c..8a86ff50 100755 --- a/bsnes/ui/base.hpp +++ b/bsnes/ui/base.hpp @@ -30,9 +30,10 @@ using namespace ruby; struct Application { bool quit; + bool pause; bool autopause; - string realpath; + string basepath; string userpath; string title; diff --git a/bsnes/ui/config/config.cpp b/bsnes/ui/config/config.cpp index 837a94e8..b1b09412 100755 --- a/bsnes/ui/config/config.cpp +++ b/bsnes/ui/config/config.cpp @@ -14,7 +14,9 @@ Config::Config() { attach(input.driver = "", "Input::Driver"); attach(input.focusPolicy = 1, "Input::FocusPolicy"); - attach(path.last = application->realpath, "Path::Recent"); + attach(path.bios.satellaview = "", "Path::BIOS::Satellaview"); + attach(path.bios.sufamiTurbo = "", "Path::BIOS::SufamiTurbo"); + attach(path.bios.superGameBoy = "", "Path::BIOS::SuperGameBoy"); attach(nes.controllerPort1Device = 1, "NES::Controller::Port1"); attach(nes.controllerPort2Device = 0, "NES::Controller::Port2"); diff --git a/bsnes/ui/config/config.hpp b/bsnes/ui/config/config.hpp index 23f1d0eb..3bacf588 100755 --- a/bsnes/ui/config/config.hpp +++ b/bsnes/ui/config/config.hpp @@ -18,7 +18,11 @@ struct Config : public configuration { } input; struct Path { - string last; + struct BIOS { + string satellaview; + string sufamiTurbo; + string superGameBoy; + } bios; } path; struct NES { diff --git a/bsnes/ui/general/file-browser.cpp b/bsnes/ui/general/file-browser.cpp index 01d738a7..69434ddf 100755 --- a/bsnes/ui/general/file-browser.cpp +++ b/bsnes/ui/general/file-browser.cpp @@ -27,13 +27,13 @@ FileBrowser::FileBrowser() { }; pathBrowse.onTick = [&] { - string path = OS::folderSelect(*this, activePath); + string path = OS::folderSelect(*this, mode->path); if(path != "") setPath(path); }; pathUp.onTick = [&] { - if(activePath == "/") return; - string path = activePath; + if(mode->path == "/") return; + string path = mode->path; path.rtrim<1>("/"); path = dir(path); setPath(path); @@ -41,18 +41,36 @@ FileBrowser::FileBrowser() { fileList.onActivate = openButton.onTick = { &FileBrowser::fileListActivate, this }; - setPath(config->path.last); + filterModes[Mode::Default ] = { "Default", "", { "*" } }; + filterModes[Mode::NES ] = { "NES", "", { "*.nes" } }; + filterModes[Mode::SNES ] = { "SNES", "", { "*.sfc" } }; + filterModes[Mode::GameBoy ] = { "GameBoy", "", { "*.gb", "*.gbc" } }; + filterModes[Mode::Satellaview] = { "Satellaview", "", { "*.bs" } }; + filterModes[Mode::SufamiTurbo] = { "SufamiTurbo", "", { "*.st" } }; + mode = &filterModes[Mode::Default]; + + foreach(mode, filterModes) config.attach(mode.path, mode.name); + config.load(string{ application->userpath, "paths.cfg" }); + config.save(string{ application->userpath, "paths.cfg" }); } -void FileBrowser::open(const string &title, const lstring &filterList, function callback) { - this->callback = callback; - this->filterList = filterList; +FileBrowser::~FileBrowser() { + config.save(string{ application->userpath, "paths.cfg" }); +} + +void FileBrowser::open(const string &title, unsigned requestedMode, function requestedCallback) { + callback = requestedCallback; + if(mode == &filterModes[requestedMode]) { + setVisible(); + return; + } + mode = &filterModes[requestedMode]; setTitle(title); - setPath(activePath); + setPath(mode->path); string filterText = "Files of type: "; - foreach(filter, filterList) filterText.append(filter, ", "); + foreach(filter, mode->filter) filterText.append(filter, ", "); filterText.trim<1>(", "); filterLabel.setText(filterText); @@ -60,9 +78,9 @@ void FileBrowser::open(const string &title, const lstring &filterList, function< } void FileBrowser::setPath(const string &path) { - config->path.last = path; - activePath = path; - pathEdit.setText(activePath); + mode->path = path; + if(mode->path == "") mode->path = application->basepath; + pathEdit.setText(mode->path); fileList.reset(); fileNameList.reset(); @@ -71,8 +89,11 @@ void FileBrowser::setPath(const string &path) { foreach(fileName, contentsList) { if(fileName.endswith("/")) { fileNameList.append(fileName); - } else foreach(filter, filterList) { - if(fileName.wildcard(filter)) fileNameList.append(fileName); + } else foreach(filter, mode->filter) { + if(fileName.wildcard(filter)) { + fileNameList.append(fileName); + break; + } } } @@ -84,8 +105,39 @@ void FileBrowser::setPath(const string &path) { void FileBrowser::fileListActivate() { unsigned selection = fileList.selection(); string fileName = fileNameList[selection]; - if(fileName.endswith("/")) return setPath({ activePath, fileName }); + if(fileName.endswith("/")) { + if(loadFolder({ mode->path, fileName })) return; + return setPath({ mode->path, fileName }); + } + loadFile({ mode->path, fileName }); +} - if(callback) callback({ activePath, fileName }); +bool FileBrowser::loadFolder(const string &requestedPath) { + bool accept = false; + string path = requestedPath; + path.rtrim<1>("/"); + foreach(filter, mode->filter) { + if(path.wildcard(filter)) accept = true; + } + if(accept == false) return false; + + lstring contentsList = directory::contents(requestedPath); + lstring fileNameList; + foreach(fileName, contentsList) { + foreach(filter, mode->filter) { + if(fileName.wildcard(filter)) { + fileNameList.append(fileName); + break; + } + } + } + + if(fileNameList.size() != 1) return false; + loadFile({ requestedPath, fileNameList[0] }); + return true; +} + +void FileBrowser::loadFile(const string &filename) { + if(callback) callback(filename); setVisible(false); } diff --git a/bsnes/ui/general/file-browser.hpp b/bsnes/ui/general/file-browser.hpp index 16ef37e3..c261ce7e 100755 --- a/bsnes/ui/general/file-browser.hpp +++ b/bsnes/ui/general/file-browser.hpp @@ -9,18 +9,28 @@ struct FileBrowser : Window { Label filterLabel; Button openButton; - void open(const string &title, const lstring &filterList, function callback); + struct Mode { enum : unsigned { Default, NES, SNES, GameBoy, Satellaview, SufamiTurbo }; }; + void open(const string &title, unsigned mode, function callback); FileBrowser(); + ~FileBrowser(); private: - string activePath; - lstring filterList; + configuration config; + struct FilterMode { + string name; + string path; + lstring filter; + } *mode; + linear_vector filterModes; + lstring fileNameList; function callback; void setPath(const string &path); void fileListActivate(); + bool loadFolder(const string &path); + void loadFile(const string &filename); }; extern FileBrowser *fileBrowser; diff --git a/bsnes/ui/general/general.cpp b/bsnes/ui/general/general.cpp index 1c06a519..50e7f351 100755 --- a/bsnes/ui/general/general.cpp +++ b/bsnes/ui/general/general.cpp @@ -1,3 +1,4 @@ #include "../base.hpp" #include "main-window.cpp" #include "file-browser.cpp" +#include "slot-loader.cpp" diff --git a/bsnes/ui/general/general.hpp b/bsnes/ui/general/general.hpp index 737238df..a0f43444 100755 --- a/bsnes/ui/general/general.hpp +++ b/bsnes/ui/general/general.hpp @@ -1,2 +1,3 @@ #include "main-window.hpp" #include "file-browser.hpp" +#include "slot-loader.hpp" diff --git a/bsnes/ui/general/main-window.cpp b/bsnes/ui/general/main-window.cpp index d93f910e..54010bcb 100755 --- a/bsnes/ui/general/main-window.cpp +++ b/bsnes/ui/general/main-window.cpp @@ -10,6 +10,10 @@ MainWindow::MainWindow() { cartridgeLoadSNES.setText("Load SNES Cartridge ..."); cartridgeLoadNES.setText("Load NES Cartridge ..."); cartridgeLoadGameBoy.setText("Load Game Boy Cartridge ..."); + cartridgeLoadSatellaviewSlotted.setText("Load Satellaview-Slotted Cartridge ..."); + cartridgeLoadSatellaview.setText("Load Satellaview Cartridge ..."); + cartridgeLoadSufamiTurbo.setText("Load Sufami Turbo Cartridge ..."); + cartridgeLoadSuperGameBoy.setText("Load Super Game Boy Cartridge ..."); nesMenu.setText("NES"); nesPower.setText("Power Cycle"); @@ -86,13 +90,15 @@ MainWindow::MainWindow() { toolsStateManager.setText("State Manager ..."); toolsTest.setText("Test"); - helpMenu.setText("Help"); - helpAbout.setText("About ..."); - append(cartridgeMenu); cartridgeMenu.append(cartridgeLoadNES); cartridgeMenu.append(cartridgeLoadSNES); cartridgeMenu.append(cartridgeLoadGameBoy); + cartridgeMenu.append(cartridgeSeparator); + cartridgeMenu.append(cartridgeLoadSatellaviewSlotted); + cartridgeMenu.append(cartridgeLoadSatellaview); + cartridgeMenu.append(cartridgeLoadSufamiTurbo); + cartridgeMenu.append(cartridgeLoadSuperGameBoy); append(nesMenu); nesMenu.append(nesPower); @@ -167,9 +173,6 @@ MainWindow::MainWindow() { toolsMenu.append(toolsSeparator3); toolsMenu.append(toolsTest); - append(helpMenu); - helpMenu.append(helpAbout); - setMenuVisible(); setStatusText("No cartridge loaded"); @@ -182,23 +185,28 @@ MainWindow::MainWindow() { onSize = [&] { utility->resizeMainWindow(); }; cartridgeLoadNES.onTick = [&] { - fileBrowser->open("Load NES Cartridge", { "*.nes" }, [](string filename) { - interface->loadCartridge(filename); + fileBrowser->open("Load Cartridge - NES", FileBrowser::Mode::NES, [](string filename) { + interface->nes.loadCartridge(filename); }); }; cartridgeLoadSNES.onTick = [&] { - fileBrowser->open("Load SNES Cartridge", { "*.sfc" }, [](string filename) { - interface->loadCartridge(filename); + fileBrowser->open("Load Cartridge - SNES", FileBrowser::Mode::SNES, [](string filename) { + interface->snes.loadCartridge(filename); }); }; cartridgeLoadGameBoy.onTick = [&] { - fileBrowser->open("Load Game Boy Cartridge", { "*.gb", "*.gbc" }, [](string filename) { - interface->loadCartridge(filename); + fileBrowser->open("Load Cartridge - Game Boy", FileBrowser::Mode::GameBoy, [](string filename) { + interface->gameBoy.loadCartridge(filename); }); }; + cartridgeLoadSatellaviewSlotted.onTick = [&] { slotLoader->loadSatellaviewSlotted(); }; + cartridgeLoadSatellaview.onTick = [&] { slotLoader->loadSatellaview(); }; + cartridgeLoadSufamiTurbo.onTick = [&] { slotLoader->loadSufamiTurbo(); }; + cartridgeLoadSuperGameBoy.onTick = [&] { slotLoader->loadSuperGameBoy(); }; + nesPower.onTick = { &Interface::power, interface }; nesReset.onTick = { &Interface::reset, interface }; @@ -276,19 +284,11 @@ MainWindow::MainWindow() { NES::cpu.trace = toolsTest.checked(); }; - helpAbout.onTick = [&] { - MessageWindow::information(*this, { - application->title, "\n\n", - "Author: byuu\n", - "Website: http://byuu.org/" - }); - }; - synchronize(); } void MainWindow::synchronize() { - if(interface->loaded()) { + if(interface->cartridgeLoaded()) { toolsStateSave.setEnabled(true); toolsStateLoad.setEnabled(true); } else { diff --git a/bsnes/ui/general/main-window.hpp b/bsnes/ui/general/main-window.hpp index b7a2bedd..5ed765d3 100755 --- a/bsnes/ui/general/main-window.hpp +++ b/bsnes/ui/general/main-window.hpp @@ -6,6 +6,11 @@ struct MainWindow : Window { Item cartridgeLoadSNES; Item cartridgeLoadNES; Item cartridgeLoadGameBoy; + Separator cartridgeSeparator; + Item cartridgeLoadSatellaviewSlotted; + Item cartridgeLoadSatellaview; + Item cartridgeLoadSufamiTurbo; + Item cartridgeLoadSuperGameBoy; Menu nesMenu; Item nesPower; @@ -67,9 +72,6 @@ struct MainWindow : Window { Separator toolsSeparator3; CheckItem toolsTest; - Menu helpMenu; - Item helpAbout; - void synchronize(); MainWindow(); diff --git a/bsnes/ui/general/slot-loader.cpp b/bsnes/ui/general/slot-loader.cpp new file mode 100755 index 00000000..a2a329b6 --- /dev/null +++ b/bsnes/ui/general/slot-loader.cpp @@ -0,0 +1,185 @@ +SlotLoader *slotLoader = 0; + +SlotLoaderPath::SlotLoaderPath() { + browse.setText("Browse ..."); + append(label, 40, 0, 5); + append(path, ~0, 0, 5); + append(browse, 80, 0); +} + +SlotLoader::SlotLoader() { + layout.setMargin(5); + base.label.setText("Base:"); + slot[0].label.setText("Slot:"); + slot[1].label.setText("Slot:"); + loadButton.setText("Load"); + + append(layout); + layout.append(base, ~0, 0, 5); + layout.append(slot[0], ~0, 0, 5); + layout.append(slot[1], ~0, 0, 5); + layout.append(controlLayout, ~0, 0); + controlLayout.append(spacer, ~0, 0); + controlLayout.append(loadButton, 80, 0); + + setGeometry({ 128, 128, 500, layout.minimumGeometry().height }); + windowManager->append(this, "SlotLoader"); +} + +void SlotLoader::synchronize() { + loadButton.setEnabled(base.path.text() != ""); +} + +void SlotLoader::loadSatellaviewSlotted() { + setTitle("Load Cartridge - Satellaview-Slotted"); + + base.path.setText(""); + + slot[0].path.setText(""); + slot[0].path.setEnabled(true); + slot[0].browse.setEnabled(true); + + slot[1].path.setText(""); + slot[1].path.setEnabled(false); + slot[1].browse.setEnabled(false); + + base.browse.onTick = [&] { + fileBrowser->open("Load Cartridge - SNES", FileBrowser::Mode::SNES, [&](const string &filename) { + base.path.setText(filename); + synchronize(); + }); + }; + + slot[0].browse.onTick = [&] { + fileBrowser->open("Load Cartridge - Satellaview", FileBrowser::Mode::Satellaview, [&](const string &filename) { + slot[0].path.setText(filename); + synchronize(); + }); + }; + + loadButton.onTick = [&] { + this->setVisible(false); + interface->snes.loadSatellaviewSlottedCartridge(base.path.text(), slot[0].path.text()); + }; + + synchronize(); + setVisible(); +} + +void SlotLoader::loadSatellaview() { + setTitle("Load Cartridge - Satellaview"); + + base.path.setText(config->path.bios.satellaview); + + slot[0].path.setText(""); + slot[0].path.setEnabled(true); + slot[0].browse.setEnabled(true); + + slot[1].path.setText(""); + slot[1].path.setEnabled(false); + slot[1].browse.setEnabled(false); + + base.browse.onTick = [&] { + fileBrowser->open("Load BIOS - Satellaview", FileBrowser::Mode::SNES, [&](const string &filename) { + config->path.bios.satellaview = filename; + base.path.setText(filename); + synchronize(); + }); + }; + + slot[0].browse.onTick = [&] { + fileBrowser->open("Load Cartridge - Satellaview", FileBrowser::Mode::Satellaview, [&](const string &filename) { + slot[0].path.setText(filename); + synchronize(); + }); + }; + + loadButton.onTick = [&] { + this->setVisible(false); + interface->snes.loadSatellaviewCartridge(base.path.text(), slot[0].path.text()); + }; + + synchronize(); + setVisible(); +} + +void SlotLoader::loadSufamiTurbo() { + setTitle("Load Cartridge - Sufami Turbo"); + + base.path.setText(config->path.bios.sufamiTurbo); + + slot[0].path.setText(""); + slot[0].path.setEnabled(true); + slot[0].browse.setEnabled(true); + + slot[1].path.setText(""); + slot[1].path.setEnabled(true); + slot[1].browse.setEnabled(true); + + base.browse.onTick = [&] { + fileBrowser->open("Load BIOS - Sufami Turbo", FileBrowser::Mode::SNES, [&](const string &filename) { + config->path.bios.sufamiTurbo = filename; + base.path.setText(filename); + synchronize(); + }); + }; + + slot[0].browse.onTick = [&] { + fileBrowser->open("Load Cartridge - Sufami Turbo", FileBrowser::Mode::SufamiTurbo, [&](const string &filename) { + slot[0].path.setText(filename); + synchronize(); + }); + }; + + slot[1].browse.onTick = [&] { + fileBrowser->open("Load Cartridge - Sufami Turbo", FileBrowser::Mode::SufamiTurbo, [&](const string &filename) { + slot[1].path.setText(filename); + synchronize(); + }); + }; + + loadButton.onTick = [&] { + this->setVisible(false); + interface->snes.loadSufamiTurboCartridge(base.path.text(), slot[0].path.text(), slot[1].path.text()); + }; + + synchronize(); + setVisible(); +} + +void SlotLoader::loadSuperGameBoy() { + setTitle("Load Cartridge - Super Game Boy"); + + base.path.setText(config->path.bios.superGameBoy); + + slot[0].path.setText(""); + slot[0].path.setEnabled(true); + slot[0].browse.setEnabled(true); + + slot[1].path.setText(""); + slot[1].path.setEnabled(false); + slot[1].browse.setEnabled(false); + + base.browse.onTick = [&] { + fileBrowser->open("Load BIOS - Super Game Boy", FileBrowser::Mode::SNES, [&](const string &filename) { + config->path.bios.superGameBoy = filename; + base.path.setText(filename); + synchronize(); + }); + }; + + slot[0].browse.onTick = [&] { + fileBrowser->open("Load Cartridge - Game Boy", FileBrowser::Mode::GameBoy, [&](const string &filename) { + slot[0].path.setText(filename); + synchronize(); + }); + }; + + loadButton.onTick = [&] { + this->setVisible(false); + interface->snes.loadSuperGameBoyCartridge(base.path.text(), slot[0].path.text()); + }; + + synchronize(); + setVisible(); +} diff --git a/bsnes/ui/general/slot-loader.hpp b/bsnes/ui/general/slot-loader.hpp new file mode 100755 index 00000000..d86cb0e5 --- /dev/null +++ b/bsnes/ui/general/slot-loader.hpp @@ -0,0 +1,29 @@ +struct SlotLoaderPath : HorizontalLayout { + Label label; + LineEdit path; + Button browse; + + string name; + lstring filter; + + SlotLoaderPath(); +}; + +struct SlotLoader : Window { + VerticalLayout layout; + SlotLoaderPath base; + SlotLoaderPath slot[2]; + HorizontalLayout controlLayout; + Widget spacer; + Button loadButton; + + void synchronize(); + void loadSatellaviewSlotted(); + void loadSatellaview(); + void loadSufamiTurbo(); + void loadSuperGameBoy(); + + SlotLoader(); +}; + +extern SlotLoader *slotLoader; diff --git a/bsnes/ui/input/user-interface.cpp b/bsnes/ui/input/user-interface.cpp index bd865a05..02cc6796 100755 --- a/bsnes/ui/input/user-interface.cpp +++ b/bsnes/ui/input/user-interface.cpp @@ -3,6 +3,10 @@ void HotkeyGeneral::inputEvent(int16_t scancode, int16_t value) { utility->toggleFullScreen(); } + if(scancode == pause.scancode && value) { + application->pause = !application->pause; + } + if(scancode == turboMode.scancode) { static bool Vsync, Async; if(value) { @@ -20,13 +24,16 @@ void HotkeyGeneral::inputEvent(int16_t scancode, int16_t value) { HotkeyGeneral::HotkeyGeneral() { name = "General"; - toggleFullScreen.name = "Toggle Fullscreen"; - turboMode.name = "Turbo Mode"; + toggleFullScreen.name = "Toggle fullscreen"; + pause.name = "Pause emulation"; + turboMode.name = "Turbo mode"; toggleFullScreen.mapping = "KB0::F11"; + pause.mapping = "KB0::P"; turboMode.mapping = "KB0::Tilde"; append(toggleFullScreen); + append(pause); append(turboMode); } diff --git a/bsnes/ui/input/user-interface.hpp b/bsnes/ui/input/user-interface.hpp index 24e60b3c..328b26b9 100755 --- a/bsnes/ui/input/user-interface.hpp +++ b/bsnes/ui/input/user-interface.hpp @@ -1,5 +1,6 @@ struct HotkeyGeneral : TertiaryInput { DigitalInput toggleFullScreen; + DigitalInput pause; DigitalInput turboMode; void inputEvent(int16_t scancode, int16_t value); diff --git a/bsnes/ui/interface/gameboy.cpp b/bsnes/ui/interface/gameboy.cpp index 1442448b..c65377ce 100755 --- a/bsnes/ui/interface/gameboy.cpp +++ b/bsnes/ui/interface/gameboy.cpp @@ -3,11 +3,14 @@ bool InterfaceGameBoy::loadCartridge(const string &filename) { unsigned size; if(file::read(filename, data, size) == false) return false; + interface->unloadCartridge(); interface->baseName = nall::basename(filename); + GameBoyCartridge info(data, size); GameBoy::Interface::loadCartridge(info.xml, data, size); - delete[] data; + + interface->loadCartridge(::Interface::Mode::GameBoy); return true; } diff --git a/bsnes/ui/interface/interface.cpp b/bsnes/ui/interface/interface.cpp index de107953..b36bfee4 100755 --- a/bsnes/ui/interface/interface.cpp +++ b/bsnes/ui/interface/interface.cpp @@ -25,7 +25,7 @@ void Interface::setController(unsigned port, unsigned device) { } } -bool Interface::loaded() { +bool Interface::cartridgeLoaded() { switch(mode()) { case Mode::NES: return nes.cartridgeLoaded(); case Mode::SNES: return snes.cartridgeLoaded(); @@ -34,32 +34,28 @@ bool Interface::loaded() { return false; } +void Interface::loadCartridge(Mode mode) { + utility->setMode(this->mode = mode); + bindControllers(); + cheatEditor->load({ baseName, ".cht" }); + stateManager->load({ baseName, ".bsa" }, 0u); + utility->showMessage({ "Loaded ", notdir(baseName) }); +} + bool Interface::loadCartridge(const string &filename) { bool result = false; - setCheatCodes(); - 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) { - bindControllers(); - cheatEditor->load({ baseName, ".cht" }); - stateManager->load({ baseName, ".bsa" }, 0u); - } + if(filename.endswith(".nes")) result = nes.loadCartridge(filename); + if(filename.endswith(".sfc")) result = snes.loadCartridge(filename); + if(filename.endswith(".gb" )) result = gameBoy.loadCartridge(filename); + if(filename.endswith(".gbc")) result = gameBoy.loadCartridge(filename); return result; } -bool Interface::loadCartridgeNES(const string &filename) { - if(nes.loadCartridge(filename) == false) return false; - utility->setMode(mode = Mode::NES); - return true; -} - void Interface::unloadCartridge() { - if(loaded() == false) return; + if(cartridgeLoaded() == false) return; cheatEditor->save({ baseName, ".cht" }); stateManager->save({ baseName, ".bsa" }, 0u); + setCheatCodes(); switch(mode()) { case Mode::NES: nes.unloadCartridge(); break; @@ -70,33 +66,6 @@ void Interface::unloadCartridge() { utility->setMode(mode = Mode::None); } -void Interface::unloadCartridgeNES() { - nes.unloadCartridge(); - utility->setMode(mode = Mode::None); -} - -bool Interface::loadCartridgeSNES(const string &filename) { - if(snes.loadCartridge(filename) == false) return false; - utility->setMode(mode = Mode::SNES); - return true; -} - -void Interface::unloadCartridgeSNES() { - snes.unloadCartridge(); - utility->setMode(mode = Mode::None); -} - -bool Interface::loadCartridgeGameBoy(const string &filename) { - if(gameBoy.loadCartridge(filename) == false) return false; - utility->setMode(mode = Mode::GameBoy); - return true; -} - -void Interface::unloadCartridgeGameBoy() { - gameBoy.unloadCartridge(); - utility->setMode(mode = Mode::None); -} - void Interface::power() { switch(mode()) { case Mode::NES: return nes.power(); @@ -138,19 +107,23 @@ bool Interface::unserialize(serializer &s) { } bool Interface::saveState(const string &filename) { + bool result = false; switch(mode()) { - case Mode::SNES: return snes.saveState(filename); - case Mode::GameBoy: return gameBoy.saveState(filename); + case Mode::SNES: result = snes.saveState(filename); break; + case Mode::GameBoy: result = gameBoy.saveState(filename); break; } - return false; + utility->showMessage(result == true ? "Saved state" : "Failed to save state"); + return result; } bool Interface::loadState(const string &filename) { + bool result = false; switch(mode()) { - case Mode::SNES: return snes.loadState(filename); - case Mode::GameBoy: return gameBoy.loadState(filename); + case Mode::SNES: result = snes.loadState(filename); break; + case Mode::GameBoy: result = gameBoy.loadState(filename); break; } - return false; + utility->showMessage(result == true ? "Loaded state" : "Failed to load state"); + return result; } void Interface::setCheatCodes(const lstring &list) { @@ -178,7 +151,7 @@ void Interface::videoRefresh() { time(¤t); if(current != previous) { previous = current; - mainWindow->setStatusText({ "FPS: ", frameCounter }); + utility->setStatusText({ "FPS: ", frameCounter }); frameCounter = 0; } } diff --git a/bsnes/ui/interface/interface.hpp b/bsnes/ui/interface/interface.hpp index 5cb51fc7..95c1b258 100755 --- a/bsnes/ui/interface/interface.hpp +++ b/bsnes/ui/interface/interface.hpp @@ -9,17 +9,10 @@ struct Interface : property { void bindControllers(); void setController(unsigned port, unsigned device); - bool loaded(); - - bool loadCartridge(const string &filename); - bool loadCartridgeNES(const string &filename); - bool loadCartridgeSNES(const string &filename); - bool loadCartridgeGameBoy(const string &filename); - + bool cartridgeLoaded(); + void loadCartridge(Mode mode); + bool loadCartridge(const string &filename); //auto-detect system-type based on file extension void unloadCartridge(); - void unloadCartridgeNES(); - void unloadCartridgeSNES(); - void unloadCartridgeGameBoy(); void power(); void reset(); @@ -38,7 +31,6 @@ struct Interface : property { string baseName; // = "/path/to/cartridge" (no extension) -private: InterfaceNES nes; InterfaceSNES snes; InterfaceGameBoy gameBoy; diff --git a/bsnes/ui/interface/nes.cpp b/bsnes/ui/interface/nes.cpp index 8d7d357b..6487fd4e 100755 --- a/bsnes/ui/interface/nes.cpp +++ b/bsnes/ui/interface/nes.cpp @@ -17,10 +17,13 @@ bool InterfaceNES::loadCartridge(const string &filename) { filemap fp; if(fp.open(filename, filemap::mode::read) == false) return false; + interface->unloadCartridge(); interface->baseName = nall::basename(filename); - NES::Interface::loadCartridge("", fp.data(), fp.size()); + NES::Interface::loadCartridge("", fp.data(), fp.size()); fp.close(); + + interface->loadCartridge(::Interface::Mode::NES); return true; } diff --git a/bsnes/ui/interface/snes.cpp b/bsnes/ui/interface/snes.cpp index 2d2161bd..e554b71d 100755 --- a/bsnes/ui/interface/snes.cpp +++ b/bsnes/ui/interface/snes.cpp @@ -26,11 +26,95 @@ bool InterfaceSNES::loadCartridge(const string &filename) { unsigned size; if(file::read(filename, data, size) == false) return false; + interface->unloadCartridge(); interface->baseName = nall::basename(filename); - string xml = SNESCartridge(data, size).xmlMemoryMap; - SNES::Interface::loadCartridge(xml, data, size); + string xml = SNESCartridge(data, size).xmlMemoryMap; + SNES::Interface::loadCartridge({ xml, data, size }); delete[] data; + + interface->loadCartridge(::Interface::Mode::SNES); + return true; +} + +bool InterfaceSNES::loadSatellaviewSlottedCartridge(const string &basename, const string &slotname) { + uint8_t *data[2]; + unsigned size[2]; + if(file::read(basename, data[0], size[0]) == false) return false; + file::read(slotname, data[1], size[1]); + + interface->unloadCartridge(); + interface->baseName = nall::basename(basename); + if(data[1]) interface->baseName.append("+", nall::basename(notdir(slotname))); + + string xml = SNESCartridge(data[0], size[0]).xmlMemoryMap; + SNES::Interface::loadSatellaviewSlottedCartridge({ xml, data[0], size[0] }, { "", data[1], size[1] }); + delete[] data[0]; + if(data[1]) delete[] data[1]; + + interface->loadCartridge(::Interface::Mode::SNES); + return true; +} + +bool InterfaceSNES::loadSatellaviewCartridge(const string &basename, const string &slotname) { + uint8_t *data[2]; + unsigned size[2]; + if(file::read(basename, data[0], size[0]) == false) return false; + file::read(slotname, data[1], size[1]); + + interface->unloadCartridge(); + interface->baseName = nall::basename(basename); + if(data[1]) interface->baseName.append("+", nall::basename(notdir(slotname))); + + string xml = SNESCartridge(data[0], size[0]).xmlMemoryMap; + SNES::Interface::loadSatellaviewCartridge({ xml, data[0], size[0] }, { "", data[1], size[1] }); + delete[] data[0]; + if(data[1]) delete[] data[1]; + + interface->loadCartridge(::Interface::Mode::SNES); + return true; +} + +bool InterfaceSNES::loadSufamiTurboCartridge(const string &basename, const string &slotAname, const string &slotBname) { + uint8_t *data[3]; + unsigned size[3]; + if(file::read(basename, data[0], size[0]) == false) return false; + file::read(slotAname, data[1], size[1]); + file::read(slotBname, data[2], size[2]); + + interface->unloadCartridge(); + interface->baseName = nall::basename(basename); + if(data[1] && data[2]) interface->baseName = { nall::basename(slotAname), "+", nall::basename(notdir(slotBname)) }; + else if(data[1]) interface->baseName = nall::basename(slotAname); + else if(data[2]) interface->baseName = nall::basename(slotBname); + + string xml = SNESCartridge(data[0], size[0]).xmlMemoryMap; + SNES::Interface::loadSufamiTurboCartridge({ xml, data[0], size[0] }, { "", data[1], size[1] }, { "", data[2], size[2] }); + delete[] data[0]; + if(data[1]) delete[] data[1]; + if(data[2]) delete[] data[2]; + + interface->loadCartridge(::Interface::Mode::SNES); + return true; +} + +bool InterfaceSNES::loadSuperGameBoyCartridge(const string &basename, const string &slotname) { + uint8_t *data[2]; + unsigned size[2]; + if(file::read(basename, data[0], size[0]) == false) return false; + file::read(slotname, data[1], size[1]); + + interface->unloadCartridge(); + interface->baseName = nall::basename(basename); + if(data[1]) interface->baseName = nall::basename(slotname); + + string xml = SNESCartridge(data[0], size[0]).xmlMemoryMap; + string gbXml = GameBoyCartridge(data[1], size[1]).xml; + SNES::Interface::loadSuperGameBoyCartridge({ xml, data[0], size[0] }, { gbXml, data[1], size[1] }); + delete[] data[0]; + if(data[1]) delete[] data[1]; + + interface->loadCartridge(::Interface::Mode::SNES); return true; } diff --git a/bsnes/ui/interface/snes.hpp b/bsnes/ui/interface/snes.hpp index cb55f23d..429e2af3 100755 --- a/bsnes/ui/interface/snes.hpp +++ b/bsnes/ui/interface/snes.hpp @@ -2,6 +2,10 @@ struct InterfaceSNES : SNES::Interface { void setController(bool port, unsigned device); bool loadCartridge(const string &filename); + bool loadSatellaviewSlottedCartridge(const string &basename, const string &slotname); + bool loadSatellaviewCartridge(const string &basename, const string &slotname); + bool loadSufamiTurboCartridge(const string &basename, const string &slotAname, const string &slotBname); + bool loadSuperGameBoyCartridge(const string &basename, const string &slotname); void unloadCartridge(); bool saveState(const string &filename); diff --git a/bsnes/ui/main.cpp b/bsnes/ui/main.cpp index 5c46d0c8..09bcd265 100755 --- a/bsnes/ui/main.cpp +++ b/bsnes/ui/main.cpp @@ -7,8 +7,9 @@ void Application::run() { inputManager->scan(); autopause = (mainWindow->focused() == false && config->input.focusPolicy == 2); + utility->updateStatus(); - if(interface->loaded() == false || autopause) { + if(interface->cartridgeLoaded() == false || pause || autopause) { audio.clear(); usleep(20 * 1000); return; @@ -17,12 +18,15 @@ void Application::run() { interface->run(); } -Application::Application(int argc, char **argv) : quit(false) { +Application::Application(int argc, char **argv) { application = this; + quit = false; + pause = false; + autopause = false; { char path[PATH_MAX]; auto unused = ::realpath(argv[0], path); - realpath = path; + basepath = path; unused = ::userpath(path); userpath = path; #if defined(PLATFORM_WIN) @@ -52,6 +56,7 @@ Application::Application(int argc, char **argv) : quit(false) { windowManager = new WindowManager; mainWindow = new MainWindow; fileBrowser = new FileBrowser; + slotLoader = new SlotLoader; settingsWindow = new SettingsWindow; cheatEditor = new CheatEditor; stateManager = new StateManager; @@ -99,6 +104,7 @@ Application::~Application() { delete stateManager; delete cheatEditor; delete settingsWindow; + delete slotLoader; delete fileBrowser; delete mainWindow; delete windowManager; diff --git a/bsnes/ui/tools/cheat-editor.cpp b/bsnes/ui/tools/cheat-editor.cpp index 51b58fe8..0d71301d 100755 --- a/bsnes/ui/tools/cheat-editor.cpp +++ b/bsnes/ui/tools/cheat-editor.cpp @@ -43,7 +43,7 @@ CheatEditor::CheatEditor() { } void CheatEditor::synchronize() { - layout.setEnabled(interface->loaded()); + layout.setEnabled(interface->cartridgeLoaded()); if(cheatList.selected()) { unsigned n = cheatList.selection(); diff --git a/bsnes/ui/tools/state-manager.cpp b/bsnes/ui/tools/state-manager.cpp index b1a1fb4b..704090df 100755 --- a/bsnes/ui/tools/state-manager.cpp +++ b/bsnes/ui/tools/state-manager.cpp @@ -38,7 +38,7 @@ StateManager::StateManager() { } void StateManager::synchronize() { - layout.setEnabled(interface->loaded()); + layout.setEnabled(interface->cartridgeLoaded()); descEdit.setText(""); descEdit.setEnabled(false); diff --git a/bsnes/ui/utility/utility.cpp b/bsnes/ui/utility/utility.cpp index 219e8b8a..130828fd 100755 --- a/bsnes/ui/utility/utility.cpp +++ b/bsnes/ui/utility/utility.cpp @@ -71,14 +71,10 @@ void Utility::resizeMainWindow(bool shrink) { } } -void Utility::shrinkMainWindow() { -} - void Utility::toggleFullScreen() { - static bool fullScreen = false; static Geometry geometry; - if(fullScreen == false) { + if(mainWindow->fullScreen() == false) { geometry = mainWindow->geometry(); mainWindow->setMenuVisible(false); mainWindow->setStatusVisible(false); @@ -92,7 +88,6 @@ void Utility::toggleFullScreen() { mainWindow->setGeometry(geometry); } - fullScreen ^= 1; resizeMainWindow(); } @@ -101,3 +96,33 @@ void Utility::bindVideoShader() { data.readfile(config->video.shader); video.set(Video::Shader, (const char*)data); } + +void Utility::updateStatus() { + time_t currentTime = time(0); + string text; + if((currentTime - statusTime) <= 2) { + text = statusMessage; + } else if(interface->cartridgeLoaded() == false) { + text = "No cartridge loaded"; + } else if(application->pause || application->autopause) { + text = "Paused"; + } else { + text = statusText; + } + if(text != mainWindow->statusText()) { + mainWindow->setStatusText(text); + } +} + +void Utility::setStatusText(const string &text) { + statusText = text; +} + +void Utility::showMessage(const string &message) { + statusTime = time(0); + statusMessage = message; +} + +Utility::Utility() { + statusTime = 0; +} diff --git a/bsnes/ui/utility/utility.hpp b/bsnes/ui/utility/utility.hpp index 9f1f0989..27c2c005 100755 --- a/bsnes/ui/utility/utility.hpp +++ b/bsnes/ui/utility/utility.hpp @@ -1,9 +1,19 @@ struct Utility { void setMode(Interface::Mode mode); void resizeMainWindow(bool shrink = false); - void shrinkMainWindow(); void toggleFullScreen(); void bindVideoShader(); + + void updateStatus(); + void setStatusText(const string &text); + void showMessage(const string &text); + + Utility(); + +private: + time_t statusTime; + string statusText; + string statusMessage; }; extern Utility *utility;