From 76553756a2c2e8b6f1b9f12d168fb7d1ec927bde Mon Sep 17 00:00:00 2001 From: Tim Allen Date: Mon, 30 Apr 2012 09:58:41 +1000 Subject: [PATCH] Update to v088r09 release. byuu says: Lots of work on ethos, nothing more. Settings window is in, InputManager pulls all the inputs from all cores and binds them to ruby inputs, main window adds menu and dynamically maps in all systems and cartridge slots and options and such, file browser's back in, RAM is loaded and saved, etc. It's barely usable, but you have to set up your inputs from the config file by hand for now. --- bsnes/emulator/emulator.hpp | 2 +- bsnes/emulator/interface.hpp | 44 ++++++-- bsnes/gba/cartridge/cartridge.cpp | 6 ++ bsnes/gba/interface/interface.cpp | 86 ++++++++++------ bsnes/nall/map.hpp | 1 + bsnes/target-ethos/Makefile | 5 +- bsnes/target-ethos/bootstrap.cpp | 34 +++++++ bsnes/target-ethos/ethos.cpp | 61 ++++++----- bsnes/target-ethos/ethos.hpp | 13 ++- bsnes/target-ethos/general/browser.cpp | 106 ++++++++++++++++++++ bsnes/target-ethos/general/browser.hpp | 28 ++++++ bsnes/target-ethos/general/general.cpp | 3 +- bsnes/target-ethos/general/general.hpp | 3 +- bsnes/target-ethos/general/presentation.cpp | 78 ++++++++++++++ bsnes/target-ethos/general/presentation.hpp | 30 ++++++ bsnes/target-ethos/general/video-window.cpp | 12 --- bsnes/target-ethos/general/video-window.hpp | 8 -- bsnes/target-ethos/input/input.cpp | 69 +++++++++++++ bsnes/target-ethos/input/input.hpp | 25 +++++ bsnes/target-ethos/interface/interface.cpp | 33 +++--- bsnes/target-ethos/interface/interface.hpp | 2 +- bsnes/target-ethos/settings/audio.cpp | 8 ++ bsnes/target-ethos/settings/audio.hpp | 7 ++ bsnes/target-ethos/settings/input.cpp | 74 ++++++++++++++ bsnes/target-ethos/settings/input.hpp | 25 +++++ bsnes/target-ethos/settings/settings.cpp | 52 ++++++++++ bsnes/target-ethos/settings/settings.hpp | 22 ++++ bsnes/target-ethos/settings/video.cpp | 8 ++ bsnes/target-ethos/settings/video.hpp | 7 ++ bsnes/target-ethos/utility/utility.cpp | 51 ++++++++++ bsnes/target-ethos/utility/utility.hpp | 15 +++ 31 files changed, 808 insertions(+), 110 deletions(-) create mode 100755 bsnes/target-ethos/bootstrap.cpp create mode 100755 bsnes/target-ethos/general/browser.cpp create mode 100755 bsnes/target-ethos/general/browser.hpp create mode 100755 bsnes/target-ethos/general/presentation.cpp create mode 100755 bsnes/target-ethos/general/presentation.hpp delete mode 100755 bsnes/target-ethos/general/video-window.cpp delete mode 100755 bsnes/target-ethos/general/video-window.hpp create mode 100755 bsnes/target-ethos/input/input.cpp create mode 100755 bsnes/target-ethos/input/input.hpp create mode 100755 bsnes/target-ethos/settings/audio.cpp create mode 100755 bsnes/target-ethos/settings/audio.hpp create mode 100755 bsnes/target-ethos/settings/input.cpp create mode 100755 bsnes/target-ethos/settings/input.hpp create mode 100755 bsnes/target-ethos/settings/settings.cpp create mode 100755 bsnes/target-ethos/settings/settings.hpp create mode 100755 bsnes/target-ethos/settings/video.cpp create mode 100755 bsnes/target-ethos/settings/video.hpp create mode 100755 bsnes/target-ethos/utility/utility.cpp create mode 100755 bsnes/target-ethos/utility/utility.hpp diff --git a/bsnes/emulator/emulator.hpp b/bsnes/emulator/emulator.hpp index bea54726..193d5ebb 100755 --- a/bsnes/emulator/emulator.hpp +++ b/bsnes/emulator/emulator.hpp @@ -1,7 +1,7 @@ #ifndef EMULATOR_HPP #define EMULATOR_HPP -static const char Version[] = "088.08"; +static const char Version[] = "088.09"; #include #include diff --git a/bsnes/emulator/interface.hpp b/bsnes/emulator/interface.hpp index e7fc7e01..f864227e 100755 --- a/bsnes/emulator/interface.hpp +++ b/bsnes/emulator/interface.hpp @@ -10,26 +10,52 @@ struct Interface { unsigned height; unsigned frequency; unsigned ports; + bool resettable; } information; - struct Media { + struct Firmware { + string name; + unsigned id; + }; + vector firmware; + + struct MediaObject { + string displayname; string name; string filter; unsigned id; }; + + struct Media : MediaObject { + vector slot; + }; vector media; - struct Controller { + struct Memory { string name; - string port; - string device; - struct Input { + unsigned id; + uint8_t *data; + unsigned size; + }; + vector memory; + + struct Port { + string name; + unsigned id; + struct Device { string name; unsigned id; + struct Input { + string name; + unsigned id; + unsigned guid; + }; + vector input; + vector displayinput; }; - vector inputs; + vector device; }; - vector controllers; + vector port; struct Callback { function videoColor; @@ -52,8 +78,8 @@ struct Interface { if(callback.audioSample) return callback.audioSample(lsample, rsample); } - virtual int16_t inputPoll(unsigned port, unsigned device, unsigned id) { - if(callback.inputPoll) return callback.inputPoll(port, device, id); + virtual int16_t inputPoll(unsigned port, unsigned device, unsigned input) { + if(callback.inputPoll) return callback.inputPoll(port, device, input); return 0; } diff --git a/bsnes/gba/cartridge/cartridge.cpp b/bsnes/gba/cartridge/cartridge.cpp index cf8e253e..777b3056 100755 --- a/bsnes/gba/cartridge/cartridge.cpp +++ b/bsnes/gba/cartridge/cartridge.cpp @@ -29,6 +29,8 @@ bool Cartridge::load(const string &markup, const stream &memory) { ram.size = numeral(info["size"].data); ram.mask = ram.size - 1; for(unsigned n = 0; n < ram.size; n++) ram.data[n] = 0xff; + + interface->memory.append({"save.ram", 2, ram.data, ram.size}); } if(info["type"].data == "EEPROM") { @@ -39,6 +41,8 @@ bool Cartridge::load(const string &markup, const stream &memory) { eeprom.mask = size > 16 * 1024 * 1024 ? 0x0fffff00 : 0x0f000000; eeprom.test = size > 16 * 1024 * 1024 ? 0x0dffff00 : 0x0d000000; for(unsigned n = 0; n < eeprom.size; n++) eeprom.data[n] = 0xff; + + interface->memory.append({"save.ram", 3, eeprom.data, eeprom.size}); } if(info["type"].data == "FlashROM") { @@ -46,6 +50,8 @@ bool Cartridge::load(const string &markup, const stream &memory) { flashrom.id = numeral(info["id"].data); flashrom.size = numeral(info["size"].data); for(unsigned n = 0; n < flashrom.size; n++) flashrom.data[n] = 0xff; + + interface->memory.append({"save.ram", 4, flashrom.data, flashrom.size}); } } diff --git a/bsnes/gba/interface/interface.cpp b/bsnes/gba/interface/interface.cpp index 39146cf2..cbd0df62 100755 --- a/bsnes/gba/interface/interface.cpp +++ b/bsnes/gba/interface/interface.cpp @@ -8,9 +8,24 @@ bool Interface::loaded() { return cartridge.loaded(); } -void Interface::load(unsigned id, const stream &memory, const string &markup) { - if(id == 0) cartridge.load(markup, memory); - if(id == 1) memory.read(bios.data, min(bios.size, memory.size())); +void Interface::load(unsigned id, const stream &stream, const string &markup) { + if(id == 0) { + memory.reset(); + cartridge.load(markup, stream); + system.power(); + } + if(id == 1) { + stream.read(bios.data, min(bios.size, stream.size())); + } + if(id == 2) { + stream.read(cartridge.ram.data, min(cartridge.ram.size, stream.size())); + } + if(id == 3) { + stream.read(cartridge.eeprom.data, min(cartridge.eeprom.size, stream.size())); + } + if(id == 4) { + stream.read(cartridge.flashrom.data, min(cartridge.flashrom.size, stream.size())); + } } void Interface::unload() { @@ -36,36 +51,51 @@ void Interface::updatePalette() { Interface::Interface() { interface = this; - information.name = "Game Boy Advance"; - information.width = 240; - information.height = 160; - information.frequency = 32768; - information.ports = 1; + information.name = "Game Boy Advance"; + information.width = 240; + information.height = 160; + information.frequency = 32768; + information.ports = 1; + information.resettable = false; { - Media medium; - medium.name = "Game Boy Advance"; - medium.filter = "*.sfc"; - medium.id = 0; - media.append(medium); + Firmware firmware; + firmware.name = "BIOS"; + firmware.id = 1; + this->firmware.append(firmware); } { - Controller controller; - controller.name = "Controller"; - controller.port = 0; - controller.device = 0; - controller.inputs.append({"Up", 6}); - controller.inputs.append({"Down", 7}); - controller.inputs.append({"Left", 5}); - controller.inputs.append({"Right", 4}); - controller.inputs.append({"B", 1}); - controller.inputs.append({"A", 0}); - controller.inputs.append({"L", 9}); - controller.inputs.append({"R", 8}); - controller.inputs.append({"Select", 2}); - controller.inputs.append({"Start", 3}); - controllers.append(controller); + Media media; + media.displayname = "Game Boy Advance"; + media.name = "program.rom"; + media.filter = "*.gba"; + media.id = 0; + this->media.append(media); + } + + { + Port port; + port.name = "Device"; + port.id = 0; + { + Port::Device device; + device.name = "Controller"; + device.id = 0; + device.input.append({"A", 0}); + device.input.append({"B", 1}); + device.input.append({"Select", 2}); + device.input.append({"Start", 3}); + device.input.append({"Right", 4}); + device.input.append({"Left", 5}); + device.input.append({"Up", 6}); + device.input.append({"Down", 7}); + device.input.append({"R", 8}); + device.input.append({"L", 9}); + device.displayinput = { 6, 7, 5, 4, 1, 0, 9, 8, 2, 3 }; + port.device.append(device); + } + this->port.append(port); } } diff --git a/bsnes/nall/map.hpp b/bsnes/nall/map.hpp index 537aa9ac..938f0c2d 100755 --- a/bsnes/nall/map.hpp +++ b/bsnes/nall/map.hpp @@ -68,6 +68,7 @@ struct map { } inline RHS& operator()(const LHS &name) { + if(auto position = find(name)) return list[position()].data; return insert(name, RHS()); } diff --git a/bsnes/target-ethos/Makefile b/bsnes/target-ethos/Makefile index 78565f87..52efb36f 100755 --- a/bsnes/target-ethos/Makefile +++ b/bsnes/target-ethos/Makefile @@ -7,7 +7,7 @@ include gb/Makefile include gba/Makefile name := ethos -ui_objects := ui-ethos ui-interface ui-general +ui_objects := ui-ethos ui-interface ui-utility ui-input ui-general ui-settings ui_objects += phoenix ruby ui_objects += $(if $(call streq,$(platform),win),resource) @@ -40,7 +40,10 @@ objects := $(patsubst %,obj/%.o,$(objects)) obj/ui-ethos.o: $(ui)/ethos.cpp $(call rwildcard,$(ui)/) obj/ui-interface.o: $(ui)/interface/interface.cpp $(call rwildcard,$(ui)/) +obj/ui-utility.o: $(ui)/utility/utility.cpp $(call rwildcard,$(ui)/) +obj/ui-input.o: $(ui)/input/input.cpp $(call rwildcard,$(ui)/) obj/ui-general.o: $(ui)/general/general.cpp $(call rwildcard,$(ui)/) +obj/ui-settings.o: $(ui)/settings/settings.cpp $(call rwildcard,$(ui)/) obj/ruby.o: ruby/ruby.cpp $(call rwildcard,ruby/*) $(call compile,$(rubyflags)) diff --git a/bsnes/target-ethos/bootstrap.cpp b/bsnes/target-ethos/bootstrap.cpp new file mode 100755 index 00000000..9d078d7c --- /dev/null +++ b/bsnes/target-ethos/bootstrap.cpp @@ -0,0 +1,34 @@ +#include + +void Application::bootstrap() { + interface = new Interface; + + emulator.append(new GameBoyAdvance::Interface); + + for(auto &system : emulator) { + system->callback.videoColor = {&Interface::videoColor, interface}; + system->callback.videoRefresh = {&Interface::videoRefresh, interface}; + system->callback.audioSample = {&Interface::audioSample, interface}; + system->callback.inputPoll = {&Interface::inputPoll, interface}; + system->updatePalette(); + + string basepath = path({system->information.name, ".sys/"}); + + string manifest; + manifest.readfile({basepath, "manifest.xml"}); + XML::Document document(manifest); + + for(auto &firmware : system->firmware) { + string path = firmware.name; + path.lower(); + for(auto &root : document) { + for(auto &node : root) { + if(node.name == path) { + filestream fs({basepath, node["firmware"].data}); + system->load(firmware.id, fs); + } + } + } + } + } +} diff --git a/bsnes/target-ethos/ethos.cpp b/bsnes/target-ethos/ethos.cpp index 9eebaf9d..dbd016f9 100755 --- a/bsnes/target-ethos/ethos.cpp +++ b/bsnes/target-ethos/ethos.cpp @@ -1,14 +1,30 @@ #include "ethos.hpp" +#include "bootstrap.cpp" Application *application = nullptr; +Emulator::Interface& system() { + struct application_interface_null{}; + if(application->active == nullptr) throw application_interface_null(); + return *application->active; +} + string Application::path(const string &filename) { string path = {basepath, filename}; if(file::exists(path)) return path; + if(directory::exists(path)) return path; return {userpath, filename}; } void Application::run() { + inputManager->poll(); + + if(active == nullptr || system().loaded() == false) { + usleep(20 * 1000); + return; + } + + system().run(); } Application::Application(int argc, char **argv) { @@ -23,21 +39,14 @@ Application::Application(int argc, char **argv) { unused = ::userpath(path); userpath = path; if(Intrinsics::platform() == Intrinsics::Platform::Windows) { - userpath.append("bsnes/"); + userpath.append("ethos/"); } else { - userpath.append(".config/bsnes/"); + userpath.append(".config/ethos/"); } mkdir(userpath, 0755); - interface = new Interface; - - auto gba = new GameBoyAdvance::Interface; - gba->callback.videoColor = {&Interface::videoColor, interface}; - gba->callback.videoRefresh = {&Interface::videoRefresh, interface}; - gba->callback.audioSample = {&Interface::audioSample, interface}; - gba->callback.inputPoll = {&Interface::inputPoll, interface}; - gba->updatePalette(); - emulators.append(gba); + bootstrap(); + active = nullptr; if(Intrinsics::platform() == Intrinsics::Platform::Windows) { normalFont = "Tahoma, 8"; @@ -51,39 +60,37 @@ Application::Application(int argc, char **argv) { monospaceFont = "Liberation Mono, 8"; } - filestream bios{"/home/byuu/.config/bsnes/Game Boy Advance.sys/bios.rom"}; - gba->load(1, bios); + utility = new Utility; + inputManager = new InputManager; + browser = new Browser; + presentation = new Presentation; + videoSettings = new VideoSettings; + audioSettings = new AudioSettings; + inputSettings = new InputSettings; + settings = new Settings; - string manifest; - manifest.readfile("/media/sdb1/root/cartridges/Game Boy Advance/Super Mario Advance (US).gba/manifest.xml"); - filestream fs{"/media/sdb1/root/cartridges/Game Boy Advance/Super Mario Advance (US).gba/program.rom"}; - gba->load(0, fs, manifest); - gba->power(); - - videoWindow = new VideoWindow; - - videoWindow->setVisible(); + presentation->setVisible(); video.driver("OpenGL"); - video.set(Video::Handle, videoWindow->viewport.handle()); + video.set(Video::Handle, presentation->viewport.handle()); video.set(Video::Synchronize, false); video.set(Video::Depth, 24u); video.init(); audio.driver("ALSA"); - audio.set(Audio::Handle, videoWindow->viewport.handle()); - audio.set(Audio::Synchronize, true); + audio.set(Audio::Handle, presentation->viewport.handle()); + audio.set(Audio::Synchronize, false); audio.set(Audio::Latency, 80u); audio.set(Audio::Frequency, 32768u); audio.init(); input.driver("SDL"); - input.set(Input::Handle, videoWindow->viewport.handle()); + input.set(Input::Handle, presentation->viewport.handle()); input.init(); while(quit == false) { OS::processEvents(); - gba->run(); + run(); } } diff --git a/bsnes/target-ethos/ethos.hpp b/bsnes/target-ethos/ethos.hpp index 0fbcab95..f66fd8ed 100755 --- a/bsnes/target-ethos/ethos.hpp +++ b/bsnes/target-ethos/ethos.hpp @@ -1,8 +1,9 @@ #include -#include #include #include +#include +#include #include #include #include @@ -15,10 +16,17 @@ using namespace phoenix; using namespace ruby; #include "interface/interface.hpp" +#include "utility/utility.hpp" +#include "input/input.hpp" #include "general/general.hpp" +#include "settings/settings.hpp" + +Emulator::Interface& system(); struct Application { - vector emulators; + vector emulator; + Emulator::Interface *active; + bool quit; bool pause; bool autopause; @@ -34,6 +42,7 @@ struct Application { string path(const string &filename); void run(); + void bootstrap(); Application(int argc, char **argv); ~Application(); }; diff --git a/bsnes/target-ethos/general/browser.cpp b/bsnes/target-ethos/general/browser.cpp new file mode 100755 index 00000000..4fa872f4 --- /dev/null +++ b/bsnes/target-ethos/general/browser.cpp @@ -0,0 +1,106 @@ +Browser *browser = nullptr; + +Browser::Browser() { + setGeometry({128, 128, 640, 400}); + + layout.setMargin(5); + pathBrowse.setText("Browse ..."); + pathUp.setText(".."); + openButton.setText("Open"); + + append(layout); + layout.append(pathLayout, {~0, 0}, 5); + pathLayout.append(pathEdit, {~0, 0}, 5); + pathLayout.append(pathBrowse, {0, 0}, 5); + pathLayout.append(pathUp, {0, 0}); + layout.append(fileList, {~0, ~0}, 5); + layout.append(controlLayout, {~0, 0}); + controlLayout.append(filterLabel, {~0, 0}, 5); + controlLayout.append(openButton, {80, 0}); + + pathEdit.onActivate = [&] { + string path = pathEdit.text(); + path.transform("\\", "/"); + if(path.endswith("/") == false) path.append("/"); + setPath(path); + }; + + pathBrowse.onActivate = [&] { + string path = DialogWindow::folderSelect(*this, this->path); + if(!path.empty()) setPath(path); + }; + + pathUp.onActivate = [&] { + if(this->path == "/") return; + string path = this->path; + path.rtrim<1>("/"); + path = dir(path); + setPath(path); + }; + + fileList.onChange = {&Browser::synchronize, this}; + fileList.onActivate = openButton.onActivate = {&Browser::fileListActivate, this}; + + synchronize(); +} + +void Browser::open(Emulator::Interface::Media &media, function callback) { + this->media = media; + this->callback = callback; + + setTitle({"Load ", media.displayname}); + setPath("/media/sdb1/root/cartridges/Game Boy Advance/"); + + filterLabel.setText({"Files of type: ", media.filter}); + + setVisible(); +} + +void Browser::synchronize() { + openButton.setEnabled(fileList.selected()); +} + +void Browser::setPath(const string &path) { + this->path = path; + pathEdit.setText(path); + + fileList.reset(); + filenameList.reset(); + + lstring contents = directory::contents(path); + for(auto &filename : contents) { + if(filename.endswith("/")) { + filenameList.append(filename); + } else if(filename.wildcard(media.filter)) { + filenameList.append(filename); + } + } + + for(auto &filename : filenameList) fileList.append(filename); + fileList.setSelection(0); + fileList.setFocused(); + synchronize(); +} + +void Browser::fileListActivate() { + unsigned selection = fileList.selection(); + string filename = filenameList[selection]; + if(filename.endswith("/")) { + if(loadFolder({path, filename})) return; + return setPath({path, filename}); + } + loadFile({path, filename}); +} + +bool Browser::loadFolder(const string &path) { + string requested = path; + requested.rtrim<1>("/"); + if(requested.wildcard(media.filter) == false) return false; + loadFile(path); + return true; +} + +void Browser::loadFile(const string &filename) { + setVisible(false); + if(callback) callback(filename); +} diff --git a/bsnes/target-ethos/general/browser.hpp b/bsnes/target-ethos/general/browser.hpp new file mode 100755 index 00000000..325de616 --- /dev/null +++ b/bsnes/target-ethos/general/browser.hpp @@ -0,0 +1,28 @@ +struct Browser : Window { + VerticalLayout layout; + HorizontalLayout pathLayout; + LineEdit pathEdit; + Button pathBrowse; + Button pathUp; + ListView fileList; + HorizontalLayout controlLayout; + Label filterLabel; + Button openButton; + + void open(Emulator::Interface::Media &media, function callback); + Browser(); + +public: + Emulator::Interface::Media media; + function callback; + string path; + lstring filenameList; + + void synchronize(); + void setPath(const string &path); + void fileListActivate(); + bool loadFolder(const string &path); + void loadFile(const string &filename); +}; + +extern Browser *browser; diff --git a/bsnes/target-ethos/general/general.cpp b/bsnes/target-ethos/general/general.cpp index 8b7e26c4..cfd89a30 100755 --- a/bsnes/target-ethos/general/general.cpp +++ b/bsnes/target-ethos/general/general.cpp @@ -1,2 +1,3 @@ #include "../ethos.hpp" -#include "video-window.cpp" +#include "browser.cpp" +#include "presentation.cpp" diff --git a/bsnes/target-ethos/general/general.hpp b/bsnes/target-ethos/general/general.hpp index 55b25cf1..5f65c61c 100755 --- a/bsnes/target-ethos/general/general.hpp +++ b/bsnes/target-ethos/general/general.hpp @@ -1 +1,2 @@ -#include "video-window.hpp" +#include "browser.hpp" +#include "presentation.hpp" diff --git a/bsnes/target-ethos/general/presentation.cpp b/bsnes/target-ethos/general/presentation.cpp new file mode 100755 index 00000000..7a5d6fa2 --- /dev/null +++ b/bsnes/target-ethos/general/presentation.cpp @@ -0,0 +1,78 @@ +Presentation *presentation = nullptr; + +void Presentation::synchronize() { + for(auto &system : emulatorList) { + system->menu.setVisible(system->interface == application->active); + } +} + +Presentation::Presentation() { + bootstrap(); + + setTitle("ethos"); + setGeometry({1024, 600, 720, 480}); + setBackgroundColor({0, 0, 0}); + setMenuFont(application->normalFont); + setMenuVisible(); + setStatusFont(application->boldFont); + setStatusVisible(); + + loadMenu.setText("Load"); + settingsMenu.setText("Settings"); + configurationSettings.setText("Configuration ..."); + toolsMenu.setText("Tools"); + + for(auto &system : emulatorList) { + loadMenu.append(system->load); + } + append(loadMenu); + for(auto &system : emulatorList) { + append(system->menu); + } + append(settingsMenu); + settingsMenu.append(configurationSettings); + append(toolsMenu); + + append(layout); + layout.append(viewport, {0, 0, 720, 480}); + + onClose = [&] { application->quit = true; }; + + configurationSettings.onActivate = [&] { settings->setVisible(); }; + + synchronize(); +} + +void Presentation::bootstrap() { + for(auto &emulator : application->emulator) { + System *system = new System; + system->interface = emulator; + + system->name = emulator->information.name; + system->filter = "*.gba"; + + system->load.setText(system->name); + system->load.onActivate = [=] { + browser->open(system->interface->media[0], [=](string filename) { + utility->loadMedia(system->interface, system->interface->media[0], filename); + }); + }; + + system->menu.setText(system->name); + system->power.setText("Power"); + system->reset.setText("Reset"); + system->unload.setText("Unload"); + + system->menu.append(system->power); + if(emulator->information.resettable) + system->menu.append(system->reset); + system->menu.append(system->separator); + system->menu.append(system->unload); + + system->power.onActivate = {&Utility::power, utility}; + system->reset.onActivate = {&Utility::reset, utility}; + system->unload.onActivate = {&Utility::unload, utility}; + + emulatorList.append(system); + } +} diff --git a/bsnes/target-ethos/general/presentation.hpp b/bsnes/target-ethos/general/presentation.hpp new file mode 100755 index 00000000..a8bf66f3 --- /dev/null +++ b/bsnes/target-ethos/general/presentation.hpp @@ -0,0 +1,30 @@ +struct Presentation : Window { + FixedLayout layout; + Viewport viewport; + + struct System { + Emulator::Interface *interface; + + string name; + string filter; + Item load; + Menu menu; + Item power; + Item reset; + Separator separator; + Item unload; + function callback; + }; + vector emulatorList; + + Menu loadMenu; + Menu settingsMenu; + Item configurationSettings; + Menu toolsMenu; + + void synchronize(); + void bootstrap(); + Presentation(); +}; + +extern Presentation *presentation; diff --git a/bsnes/target-ethos/general/video-window.cpp b/bsnes/target-ethos/general/video-window.cpp deleted file mode 100755 index 08451da5..00000000 --- a/bsnes/target-ethos/general/video-window.cpp +++ /dev/null @@ -1,12 +0,0 @@ -VideoWindow *videoWindow = nullptr; - -VideoWindow::VideoWindow() { - setTitle("ethos"); - setGeometry({1024, 600, 720, 480}); - setBackgroundColor({0, 0, 0}); - - append(layout); - layout.append(viewport, {0, 0, 720, 480}); - - onClose = [&] { application->quit = true; }; -} diff --git a/bsnes/target-ethos/general/video-window.hpp b/bsnes/target-ethos/general/video-window.hpp deleted file mode 100755 index 25aefb4d..00000000 --- a/bsnes/target-ethos/general/video-window.hpp +++ /dev/null @@ -1,8 +0,0 @@ -struct VideoWindow : Window { - FixedLayout layout; - Viewport viewport; - - VideoWindow(); -}; - -extern VideoWindow *videoWindow; diff --git a/bsnes/target-ethos/input/input.cpp b/bsnes/target-ethos/input/input.cpp new file mode 100755 index 00000000..3edb0908 --- /dev/null +++ b/bsnes/target-ethos/input/input.cpp @@ -0,0 +1,69 @@ +#include "../ethos.hpp" +InputManager *inputManager = nullptr; + +void AbstractInput::bind() { + if(mapping.empty()) type = Type::Button, mapping = "None"; + + if(mapping.endswith(".Up")) type = Type::HatUp; + else if(mapping.endswith(".Down")) type = Type::HatDown; + else if(mapping.endswith(".Left")) type = Type::HatLeft; + else if(mapping.endswith(".Right")) type = Type::HatRight; + else if(mapping.endswith(".Lo")) type = Type::AxisLo; + else if(mapping.endswith(".Hi")) type = Type::AxisHi; + else if(mapping.beginswith("JP") && mapping.position("Axis")) type = Type::Axis; + else if(mapping.beginswith("MS") && mapping.endswith("axis")) type = Type::MouseAxis; + else if(mapping.beginswith("MS")) type = Type::MouseButton; + else type = Type::Button; + + string decode = mapping; + if(auto position = decode.position(".")) decode[position()] = 0; + scancode = Scancode::decode(decode); +} + +void InputManager::bind() { + for(auto &input : inputMap) input.data.bind(); +} + +void InputManager::poll() { + input.poll(table); +} + +int16_t InputManager::poll(unsigned guid) { + return table[inputMap[guid].scancode]; +} + +InputManager::InputManager() { + bootstrap(); +} + +void InputManager::bootstrap() { + unsigned guid = 0; + for(auto &emulator : application->emulator) { + for(auto &port : emulator->port) { + for(auto &device : port.device) { + for(auto &number : device.displayinput) { + auto &input = device.input[number]; + + AbstractInput abstract; + abstract.type = AbstractInput::Type::Button; + abstract.name = {emulator->information.name, "::", port.name, "::", device.name, "::", input.name}; + abstract.mapping = "None"; + abstract.scancode = 0; + abstract.name.replace(" ", ""); + + input.guid = guid++; + inputMap(input.guid) = abstract; + } + } + } + } + + for(auto &input : inputMap) { + config.append(input.data.mapping, input.data.name); + } + + config.load(application->path("input.cfg")); + config.save(application->path("input.cfg")); + + bind(); +} diff --git a/bsnes/target-ethos/input/input.hpp b/bsnes/target-ethos/input/input.hpp new file mode 100755 index 00000000..f68a1746 --- /dev/null +++ b/bsnes/target-ethos/input/input.hpp @@ -0,0 +1,25 @@ +struct AbstractInput { + enum class Type : unsigned { Button, MouseButton, MouseAxis, HatUp, HatDown, HatLeft, HatRight, Axis, AxisLo, AxisHi } type; + string name; + string mapping; + unsigned scancode; + + void bind(); +}; + +struct InputManager { + int16_t table[Scancode::Limit]; + + map inputMap; + + void bind(); + void poll(); + int16_t poll(unsigned guid); + void bootstrap(); + InputManager(); + +private: + configuration config; +}; + +extern InputManager *inputManager; diff --git a/bsnes/target-ethos/interface/interface.cpp b/bsnes/target-ethos/interface/interface.cpp index 7f001df3..9ff86d0e 100755 --- a/bsnes/target-ethos/interface/interface.cpp +++ b/bsnes/target-ethos/interface/interface.cpp @@ -20,29 +20,24 @@ void Interface::videoRefresh(const uint32_t *data, unsigned pitch, unsigned widt video.unlock(); video.refresh(); } + + static unsigned frameCounter = 0; + static time_t previous, current; + frameCounter++; + + time(¤t); + if(current != previous) { + previous = current; + utility->setStatusText({"FPS: ", frameCounter}); + frameCounter = 0; + } } void Interface::audioSample(int16_t lsample, int16_t rsample) { audio.sample(lsample, rsample); } -int16_t Interface::inputPoll(unsigned port, unsigned device, unsigned id) { - using nall::Keyboard; - static int16_t table[Scancode::Limit]; - if(id == 0) input.poll(table); - - switch(id) { - case 0: return table[keyboard(0)[Keyboard::X]]; //A - case 1: return table[keyboard(0)[Keyboard::Z]]; //B - case 2: return table[keyboard(0)[Keyboard::Apostrophe]]; //Select - case 3: return table[keyboard(0)[Keyboard::Return]]; //Start - case 4: return table[keyboard(0)[Keyboard::Right]]; //Right - case 5: return table[keyboard(0)[Keyboard::Left]]; //Left - case 6: return table[keyboard(0)[Keyboard::Up]]; //Up - case 7: return table[keyboard(0)[Keyboard::Down]]; //Down - case 8: return table[keyboard(0)[Keyboard::R]]; //R - case 9: return table[keyboard(0)[Keyboard::L]]; //L - } - - return 0; +int16_t Interface::inputPoll(unsigned port, unsigned device, unsigned input) { + unsigned guid = system().port[port].device[device].input[input].guid; + return inputManager->poll(guid); } diff --git a/bsnes/target-ethos/interface/interface.hpp b/bsnes/target-ethos/interface/interface.hpp index 1b8cdc9e..eeae179f 100755 --- a/bsnes/target-ethos/interface/interface.hpp +++ b/bsnes/target-ethos/interface/interface.hpp @@ -2,7 +2,7 @@ struct Interface { uint32_t videoColor(unsigned source, uint16_t red, uint16_t green, uint16_t blue); void videoRefresh(const uint32_t *data, unsigned pitch, unsigned width, unsigned height); void audioSample(int16_t lsample, int16_t rsample); - int16_t inputPoll(unsigned port, unsigned device, unsigned id); + int16_t inputPoll(unsigned port, unsigned device, unsigned input); }; extern Interface *interface; diff --git a/bsnes/target-ethos/settings/audio.cpp b/bsnes/target-ethos/settings/audio.cpp new file mode 100755 index 00000000..34e99b7c --- /dev/null +++ b/bsnes/target-ethos/settings/audio.cpp @@ -0,0 +1,8 @@ +AudioSettings *audioSettings = nullptr; + +AudioSettings::AudioSettings() { + title.setFont(application->titleFont); + title.setText("Audio Settings"); + + append(title, {~0, 0}, 5); +} diff --git a/bsnes/target-ethos/settings/audio.hpp b/bsnes/target-ethos/settings/audio.hpp new file mode 100755 index 00000000..91ecc5d8 --- /dev/null +++ b/bsnes/target-ethos/settings/audio.hpp @@ -0,0 +1,7 @@ +struct AudioSettings : SettingsLayout { + Label title; + + AudioSettings(); +}; + +extern AudioSettings *audioSettings; diff --git a/bsnes/target-ethos/settings/input.cpp b/bsnes/target-ethos/settings/input.cpp new file mode 100755 index 00000000..4e533a07 --- /dev/null +++ b/bsnes/target-ethos/settings/input.cpp @@ -0,0 +1,74 @@ +InputSettings *inputSettings = nullptr; + +InputSettings::InputSettings() { + title.setFont(application->titleFont); + title.setText("Input Settings"); + inputList.setHeaderText("Name", "Mapping"); + inputList.setHeaderVisible(); + clearButton.setText("Clear"); + + append(title, {~0, 0}, 5); + append(selectionLayout, {~0, 0}, 5); + selectionLayout.append(systemList, {~0, 0}, 5); + selectionLayout.append(portList, {~0, 0}, 5); + selectionLayout.append(deviceList, {~0, 0}); + append(inputList, {~0, ~0}, 5); + append(controlLayout, {~0, 0}); + controlLayout.append(assign[0], {100, 0}, 5); + controlLayout.append(assign[1], {100, 0}, 5); + controlLayout.append(assign[2], {100, 0}, 5); + controlLayout.append(spacer, {~0, 0}); + controlLayout.append(clearButton, {80, 0}); + + for(auto &emulator : application->emulator) { + systemList.append(emulator->information.name); + } + + systemList.onChange = {&InputSettings::systemChanged, this}; + portList.onChange = {&InputSettings::portChanged, this}; + deviceList.onChange = {&InputSettings::deviceChanged, this}; + inputList.onChange = {&InputSettings::synchronize, this}; + + systemChanged(); +} + +void InputSettings::synchronize() { + clearButton.setEnabled(inputList.selected()); +} + +Emulator::Interface& InputSettings::activeSystem() { + return *application->emulator[systemList.selection()]; +} + +Emulator::Interface::Port& InputSettings::activePort() { + return activeSystem().port[portList.selection()]; +} + +Emulator::Interface::Port::Device& InputSettings::activeDevice() { + return activePort().device[deviceList.selection()]; +} + +void InputSettings::systemChanged() { + portList.reset(); + for(auto &port : activeSystem().port) { + portList.append(port.name); + } + portChanged(); +} + +void InputSettings::portChanged() { + deviceList.reset(); + for(auto &device : activePort().device) { + deviceList.append(device.name); + } + deviceChanged(); +} + +void InputSettings::deviceChanged() { + inputList.reset(); + for(unsigned number : activeDevice().displayinput) { + auto &input = activeDevice().input[number]; + inputList.append(input.name, inputManager->inputMap(input.guid).mapping); + } + synchronize(); +} diff --git a/bsnes/target-ethos/settings/input.hpp b/bsnes/target-ethos/settings/input.hpp new file mode 100755 index 00000000..c5754afd --- /dev/null +++ b/bsnes/target-ethos/settings/input.hpp @@ -0,0 +1,25 @@ +struct InputSettings : SettingsLayout { + Label title; + HorizontalLayout selectionLayout; + ComboBox systemList; + ComboBox portList; + ComboBox deviceList; + ListView inputList; + HorizontalLayout controlLayout; + Button assign[3]; + Widget spacer; + Button clearButton; + + void synchronize(); + + Emulator::Interface& activeSystem(); + Emulator::Interface::Port& activePort(); + Emulator::Interface::Port::Device& activeDevice(); + + void systemChanged(); + void portChanged(); + void deviceChanged(); + InputSettings(); +}; + +extern InputSettings *inputSettings; diff --git a/bsnes/target-ethos/settings/settings.cpp b/bsnes/target-ethos/settings/settings.cpp new file mode 100755 index 00000000..22ca2b93 --- /dev/null +++ b/bsnes/target-ethos/settings/settings.cpp @@ -0,0 +1,52 @@ +#include "../ethos.hpp" +#include "video.cpp" +#include "audio.cpp" +#include "input.cpp" +Settings *settings = nullptr; + +void SettingsLayout::append(Sizable &sizable, const Size &size, unsigned spacing) { + layout.append(sizable, size, spacing); +} + +SettingsLayout::SettingsLayout() { + setMargin(5); + HorizontalLayout::append(spacer, {120, ~0}, 5); + HorizontalLayout::append(layout, { ~0, ~0}); +} + +Settings::Settings() { + setTitle("Configuration Settings"); + setGeometry({128, 128, 640, 360}); + setStatusFont(application->boldFont); + setStatusVisible(); + + layout.setMargin(5); + panelList.setFont(application->boldFont); + panelList.append("Video"); + panelList.append("Audio"); + panelList.append("Input"); + + append(layout); + layout.append(panelList, {120, ~0}, 5); + append(*videoSettings); + append(*audioSettings); + append(*inputSettings); + + panelList.onChange = {&Settings::panelChanged, this}; + + panelList.setSelection(2); + panelChanged(); +} + +void Settings::panelChanged() { + videoSettings->setVisible(false); + audioSettings->setVisible(false); + inputSettings->setVisible(false); + if(panelList.selected() == false) return; + + switch(panelList.selection()) { + case 0: return videoSettings->setVisible(); + case 1: return audioSettings->setVisible(); + case 2: return inputSettings->setVisible(); + } +} diff --git a/bsnes/target-ethos/settings/settings.hpp b/bsnes/target-ethos/settings/settings.hpp new file mode 100755 index 00000000..7fcc016a --- /dev/null +++ b/bsnes/target-ethos/settings/settings.hpp @@ -0,0 +1,22 @@ +struct SettingsLayout : HorizontalLayout { + Widget spacer; + VerticalLayout layout; + + void append(Sizable &widget, const Size &size, unsigned spacing = 0); + SettingsLayout(); +}; + +#include "video.hpp" +#include "audio.hpp" +#include "input.hpp" + +struct Settings : Window { + HorizontalLayout layout; + ListView panelList; + + void panelChanged(); + + Settings(); +}; + +extern Settings *settings; diff --git a/bsnes/target-ethos/settings/video.cpp b/bsnes/target-ethos/settings/video.cpp new file mode 100755 index 00000000..1ef0d2e5 --- /dev/null +++ b/bsnes/target-ethos/settings/video.cpp @@ -0,0 +1,8 @@ +VideoSettings *videoSettings = nullptr; + +VideoSettings::VideoSettings() { + title.setFont(application->titleFont); + title.setText("Video Settings"); + + append(title, {~0, 0}, 5); +} diff --git a/bsnes/target-ethos/settings/video.hpp b/bsnes/target-ethos/settings/video.hpp new file mode 100755 index 00000000..18cd68e6 --- /dev/null +++ b/bsnes/target-ethos/settings/video.hpp @@ -0,0 +1,7 @@ +struct VideoSettings : SettingsLayout { + Label title; + + VideoSettings(); +}; + +extern VideoSettings *videoSettings; diff --git a/bsnes/target-ethos/utility/utility.cpp b/bsnes/target-ethos/utility/utility.cpp new file mode 100755 index 00000000..71f27725 --- /dev/null +++ b/bsnes/target-ethos/utility/utility.cpp @@ -0,0 +1,51 @@ +#include "../ethos.hpp" + +Utility *utility = nullptr; + +void Utility::setInterface(Emulator::Interface *emulator) { + application->active = emulator; + presentation->synchronize(); +} + +void Utility::loadMedia(Emulator::Interface *emulator, Emulator::Interface::Media &media, const string &pathname) { + unload(); + setInterface(emulator); + this->pathname = pathname; + + string manifest; + manifest.readfile({pathname, "manifest.xml"}); + auto memory = file::read({pathname, media.name}); + system().load(media.id, vectorstream{memory}, manifest); + + for(auto &memory : system().memory) { + filestream fs({pathname, memory.name}); + system().load(memory.id, fs); + } +} + +void Utility::saveMedia() { + for(auto &memory : system().memory) { + file::write({pathname, memory.name}, memory.data, memory.size); + } +} + +void Utility::power() { + system().power(); +} + +void Utility::reset() { + system().reset(); +} + +void Utility::unload() { + if(application->active) { + saveMedia(); + system().unload(); + setInterface(nullptr); + } + video.clear(); +} + +void Utility::setStatusText(const string &text) { + presentation->setStatusText(text); +} diff --git a/bsnes/target-ethos/utility/utility.hpp b/bsnes/target-ethos/utility/utility.hpp new file mode 100755 index 00000000..3a316761 --- /dev/null +++ b/bsnes/target-ethos/utility/utility.hpp @@ -0,0 +1,15 @@ +struct Utility { + string pathname; + + void setInterface(Emulator::Interface *emulator); + void loadMedia(Emulator::Interface *emulator, Emulator::Interface::Media &media, const string &pathname); + void saveMedia(); + + void power(); + void reset(); + void unload(); + + void setStatusText(const string &text); +}; + +extern Utility *utility;