diff --git a/bsnes/emulator/emulator.hpp b/bsnes/emulator/emulator.hpp index ee11d20e..84cd4c52 100755 --- a/bsnes/emulator/emulator.hpp +++ b/bsnes/emulator/emulator.hpp @@ -3,7 +3,7 @@ namespace Emulator { static const char Name[] = "bsnes"; - static const char Version[] = "088.10"; + static const char Version[] = "088.11"; static const char Author[] = "byuu"; static const char License[] = "GPLv3"; } diff --git a/bsnes/emulator/interface.hpp b/bsnes/emulator/interface.hpp index 2ecbee52..403850fb 100755 --- a/bsnes/emulator/interface.hpp +++ b/bsnes/emulator/interface.hpp @@ -8,9 +8,15 @@ struct Interface { string name; unsigned width; unsigned height; + double aspectRatio; unsigned frequency; - unsigned ports; bool resettable; + + struct Media { + string name; + string filter; + }; + vector media; } information; struct Firmware { @@ -20,23 +26,22 @@ struct Interface { }; vector firmware; - struct MediaObject { + struct Media { string displayname; + string path; string name; string filter; unsigned id; }; - struct Media : MediaObject { - vector slot; + struct Schema : Media { + vector slot; }; - vector media; + vector schema; struct Memory { - string name; unsigned id; - uint8_t *data; - unsigned size; + string name; }; vector memory; @@ -64,6 +69,7 @@ struct Interface { function videoRefresh; function audioSample; function inputPoll; + function mediaRequest; } callback; //audio/visual bindings (provided by user interface) @@ -85,9 +91,14 @@ struct Interface { return 0; } - //cartridge interface + virtual void mediaRequest(Media media) { + if(callback.mediaRequest) return callback.mediaRequest(media); + } + + //media interface virtual bool loaded() { return false; } virtual void load(unsigned id, const stream &memory, const string &markup = "") {} + virtual void save(unsigned id, const stream &memory) {} virtual void unload() {} //system interface diff --git a/bsnes/fc/cartridge/cartridge.cpp b/bsnes/fc/cartridge/cartridge.cpp index 39348a42..0490f28c 100755 --- a/bsnes/fc/cartridge/cartridge.cpp +++ b/bsnes/fc/cartridge/cartridge.cpp @@ -20,6 +20,8 @@ void Cartridge::load(const string &markup, const stream &memory) { board = Board::load(markup, memory); if(board == nullptr) return; + interface->memory.append({ID::RAM, "save.ram"}); + sha256_ctx sha; uint8_t hash[32]; sha256_init(&sha); diff --git a/bsnes/fc/interface/interface.cpp b/bsnes/fc/interface/interface.cpp index 6fb084be..1654f0ee 100755 --- a/bsnes/fc/interface/interface.cpp +++ b/bsnes/fc/interface/interface.cpp @@ -8,13 +8,23 @@ bool Interface::loaded() { return cartridge.loaded(); } -void Interface::load(unsigned id, const stream &memory, const string &markup) { - if(id == 0) { - cartridge.load(markup, memory); +void Interface::load(unsigned id, const stream &stream, const string &markup) { + if(id == ID::ROM) { + cartridge.load(markup, stream); system.power(); input.connect(0, Input::Device::Joypad); input.connect(1, Input::Device::Joypad); } + + if(id == ID::RAM) { + stream.read(cartridge.ram_data(), min(stream.size(), cartridge.ram_size())); + } +} + +void Interface::save(unsigned id, const stream &stream) { + if(id == ID::RAM) { + stream.write(cartridge.ram_data(), cartridge.ram_size()); + } } void Interface::unload() { @@ -40,20 +50,22 @@ void Interface::updatePalette() { Interface::Interface() { interface = this; - information.name = "Famicom"; - information.width = 256; - information.height = 240; - information.frequency = 1789772; - information.ports = 2; - information.resettable = true; + information.name = "Famicom"; + information.width = 256; + information.height = 240; + information.aspectRatio = 8.0 / 7.0; + information.frequency = 1789772; + information.resettable = true; + + information.media.append({"Famicom", "*.fc"}); { - Media media; - media.displayname = "Famicom"; - media.name = "program.rom"; - media.filter = "*.fc"; - media.id = 0; - this->media.append(media); + Schema schema; + schema.displayname = "Famicom"; + schema.name = "program.rom"; + schema.filter = "*.fc"; + schema.id = ID::ROM; + this->schema.append(schema); } { diff --git a/bsnes/fc/interface/interface.hpp b/bsnes/fc/interface/interface.hpp index 3cb03c56..1c93de25 100755 --- a/bsnes/fc/interface/interface.hpp +++ b/bsnes/fc/interface/interface.hpp @@ -2,9 +2,17 @@ namespace Famicom { #endif +struct ID { + enum : unsigned { + ROM, + RAM, + }; +}; + struct Interface : Emulator::Interface { bool loaded(); - void load(unsigned id, const stream &memory, const string &markup = ""); + void load(unsigned id, const stream &stream, const string &markup = ""); + void save(unsigned id, const stream &stream); void unload(); void power(); diff --git a/bsnes/gb/cartridge/cartridge.cpp b/bsnes/gb/cartridge/cartridge.cpp index 994cd0af..ea966b4e 100755 --- a/bsnes/gb/cartridge/cartridge.cpp +++ b/bsnes/gb/cartridge/cartridge.cpp @@ -64,6 +64,7 @@ void Cartridge::load(System::Revision revision, const string &markup, const stre loaded = true; sha256 = nall::sha256(romdata, romsize); + if(ramsize) interface->memory.append({ID::RAM, "save.ram"}); } void Cartridge::unload() { diff --git a/bsnes/gb/interface/interface.cpp b/bsnes/gb/interface/interface.cpp index eca9fe5e..40eb3595 100755 --- a/bsnes/gb/interface/interface.cpp +++ b/bsnes/gb/interface/interface.cpp @@ -9,21 +9,37 @@ bool Interface::loaded() { } void Interface::load(unsigned id, const stream &stream, const string &markup) { - if(id == 0) stream.read(system.bootROM.dmg, min( 256u, stream.size())); - if(id == 1) stream.read(system.bootROM.sgb, min( 256u, stream.size())); - if(id == 2) stream.read(system.bootROM.cgb, min(2048u, stream.size())); - if(id == 3) { + if(id == ID::GameBoyBootROM) { + stream.read(system.bootROM.dmg, min( 256u, stream.size())); + } + + if(id == ID::SuperGameBoyBootROM) { + stream.read(system.bootROM.sgb, min( 256u, stream.size())); + } + + if(id == ID::GameBoyColorBootROM) { + stream.read(system.bootROM.cgb, min(2048u, stream.size())); + } + + if(id == ID::GameBoyROM) { cartridge.load(System::Revision::GameBoy, markup, stream); system.power(); } - if(id == 4) { - cartridge.load(System::Revision::SuperGameBoy, markup, stream); - system.power(); - } - if(id == 5) { + + if(id == ID::GameBoyColorROM) { cartridge.load(System::Revision::GameBoyColor, markup, stream); system.power(); } + + if(id == ID::RAM) { + stream.read(cartridge.ramdata, min(stream.size(), cartridge.ramsize)); + } +} + +void Interface::save(unsigned id, const stream &stream) { + if(id == ID::RAM) { + stream.write(cartridge.ramdata, cartridge.ramsize); + } } void Interface::unload() { @@ -49,18 +65,21 @@ void Interface::updatePalette() { Interface::Interface() { interface = this; - information.name = "Game Boy"; - information.width = 160; - information.height = 144; - information.frequency = 4194304; - information.ports = 1; - information.resettable = false; + information.name = "Game Boy"; + information.width = 160; + information.height = 144; + information.aspectRatio = 1.0; + information.frequency = 4194304; + information.resettable = false; + + information.media.append({"Game Boy", "*.gb"}); + information.media.append({"Game Boy Color", "*.gbc"}); { Firmware firmware; firmware.displayname = "Game Boy"; firmware.name = "Game Boy.sys/boot.rom"; - firmware.id = 0; + firmware.id = ID::GameBoyBootROM; this->firmware.append(firmware); } @@ -68,7 +87,7 @@ Interface::Interface() { Firmware firmware; firmware.displayname = "Super Game Boy"; firmware.name = "Super Game Boy.sfc/boot.rom"; - firmware.id = 1; + firmware.id = ID::SuperGameBoyBootROM; this->firmware.append(firmware); } @@ -76,26 +95,26 @@ Interface::Interface() { Firmware firmware; firmware.displayname = "Game Boy Color"; firmware.name = "Game Boy Color.sys/boot.rom"; - firmware.id = 2; + firmware.id = ID::GameBoyColorBootROM; this->firmware.append(firmware); } { - Media media; - media.displayname = "Game Boy"; - media.name = "program.rom"; - media.filter = "*.gb"; - media.id = 3; - this->media.append(media); + Schema schema; + schema.displayname = "Game Boy"; + schema.name = "program.rom"; + schema.filter = "*.gb"; + schema.id = ID::GameBoyROM; + this->schema.append(schema); } { - Media media; - media.displayname = "Game Boy Color"; - media.name = "program.rom"; - media.filter = "*.gbc"; - media.id = 5; - this->media.append(media); + Schema schema; + schema.displayname = "Game Boy Color"; + schema.name = "program.rom"; + schema.filter = "*.gbc"; + schema.id = ID::GameBoyColorROM; + this->schema.append(schema); } { diff --git a/bsnes/gb/interface/interface.hpp b/bsnes/gb/interface/interface.hpp index 31225ef9..0bd79b7c 100755 --- a/bsnes/gb/interface/interface.hpp +++ b/bsnes/gb/interface/interface.hpp @@ -2,13 +2,25 @@ namespace GameBoy { #endif +struct ID { + enum : unsigned { + GameBoyBootROM, + SuperGameBoyBootROM, + GameBoyColorBootROM, + GameBoyROM, + GameBoyColorROM, + RAM, + }; +}; + struct Interface : Emulator::Interface { //Super Game Boy bindings virtual void lcdScanline() {} virtual void joypWrite(bool p15, bool p14) {} bool loaded(); - void load(unsigned id, const stream &memory, const string &markup = ""); + void load(unsigned id, const stream &stream, const string &markup = ""); + void save(unsigned id, const stream &stream); void unload(); void power(); diff --git a/bsnes/gba/cartridge/cartridge.cpp b/bsnes/gba/cartridge/cartridge.cpp index 777b3056..d92ebf6d 100755 --- a/bsnes/gba/cartridge/cartridge.cpp +++ b/bsnes/gba/cartridge/cartridge.cpp @@ -30,7 +30,7 @@ bool Cartridge::load(const string &markup, const stream &memory) { 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}); + interface->memory.append({2, "save.ram"}); } if(info["type"].data == "EEPROM") { @@ -42,7 +42,7 @@ bool Cartridge::load(const string &markup, const stream &memory) { 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}); + interface->memory.append({3, "save.ram"}); } if(info["type"].data == "FlashROM") { @@ -51,7 +51,7 @@ bool Cartridge::load(const string &markup, const stream &memory) { 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}); + interface->memory.append({4, "save.ram"}); } } diff --git a/bsnes/gba/interface/interface.cpp b/bsnes/gba/interface/interface.cpp index 3be8a816..66b2e022 100755 --- a/bsnes/gba/interface/interface.cpp +++ b/bsnes/gba/interface/interface.cpp @@ -9,25 +9,43 @@ bool Interface::loaded() { } void Interface::load(unsigned id, const stream &stream, const string &markup) { - if(id == 0) { + if(id == ID::BIOS) { + stream.read(bios.data, min(bios.size, stream.size())); + } + + if(id == ID::ROM) { memory.reset(); cartridge.load(markup, stream); system.power(); } - if(id == 1) { - stream.read(bios.data, min(bios.size, stream.size())); - } - if(id == 2) { + + if(id == ID::RAM) { stream.read(cartridge.ram.data, min(cartridge.ram.size, stream.size())); } - if(id == 3) { + + if(id == ID::EEPROM) { stream.read(cartridge.eeprom.data, min(cartridge.eeprom.size, stream.size())); } - if(id == 4) { + + if(id == ID::FlashROM) { stream.read(cartridge.flashrom.data, min(cartridge.flashrom.size, stream.size())); } } +void Interface::save(unsigned id, const stream &stream) { + if(id == ID::RAM) { + stream.write(cartridge.ram.data, cartridge.ram.size); + } + + if(id == ID::EEPROM) { + stream.write(cartridge.eeprom.data, cartridge.eeprom.size); + } + + if(id == ID::FlashROM) { + stream.write(cartridge.flashrom.data, cartridge.flashrom.size); + } +} + void Interface::unload() { cartridge.unload(); } @@ -51,28 +69,30 @@ 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.resettable = false; + information.name = "Game Boy Advance"; + information.width = 240; + information.height = 160; + information.aspectRatio = 1.0; + information.frequency = 32768; + information.resettable = false; + + information.media.append({"Game Boy Advance", "*.gba"}); { Firmware firmware; firmware.displayname = "Game Boy Advance"; firmware.name = "Game Boy Advance.sys/bios.rom"; - firmware.id = 1; + firmware.id = ID::BIOS; this->firmware.append(firmware); } { - Media media; - media.displayname = "Game Boy Advance"; - media.name = "program.rom"; - media.filter = "*.gba"; - media.id = 0; - this->media.append(media); + Schema schema; + schema.displayname = "Game Boy Advance"; + schema.name = "program.rom"; + schema.filter = "*.gba"; + schema.id = ID::ROM; + this->schema.append(schema); } { diff --git a/bsnes/gba/interface/interface.hpp b/bsnes/gba/interface/interface.hpp index 6f3b1240..736eadbe 100755 --- a/bsnes/gba/interface/interface.hpp +++ b/bsnes/gba/interface/interface.hpp @@ -2,9 +2,20 @@ namespace GameBoyAdvance { #endif +struct ID { + enum : unsigned { + BIOS, + ROM, + RAM, + EEPROM, + FlashROM, + }; +}; + struct Interface : Emulator::Interface { bool loaded(); - void load(unsigned id, const stream &memory, const string &markup = ""); + void load(unsigned id, const stream &stream, const string &markup = ""); + void save(unsigned id, const stream &stream); void unload(); void power(); diff --git a/bsnes/nall/stream/file.hpp b/bsnes/nall/stream/file.hpp index 1d559395..878418cf 100755 --- a/bsnes/nall/stream/file.hpp +++ b/bsnes/nall/stream/file.hpp @@ -27,6 +27,11 @@ struct filestream : stream { if(!pwritable) pfile.open(filename, file::mode::read); } + filestream(const string &filename, file::mode mode) { + pfile.open(filename, mode); + pwritable = mode == file::mode::write || mode == file::mode::readwrite; + } + private: mutable file pfile; bool pwritable; diff --git a/bsnes/nall/stream/mmap.hpp b/bsnes/nall/stream/mmap.hpp index 784f2b35..ce30f810 100755 --- a/bsnes/nall/stream/mmap.hpp +++ b/bsnes/nall/stream/mmap.hpp @@ -12,7 +12,7 @@ struct mmapstream : stream { bool seekable() const { return true; } bool readable() const { return true; } bool writable() const { return pwritable; } - bool randomaccess() const { return false; } + bool randomaccess() const { return true; } unsigned size() const { return pmmap.size(); } unsigned offset() const { return poffset; } diff --git a/bsnes/phoenix/core/core.cpp b/bsnes/phoenix/core/core.cpp index ec4aca3f..d7aa15e9 100755 --- a/bsnes/phoenix/core/core.cpp +++ b/bsnes/phoenix/core/core.cpp @@ -326,6 +326,11 @@ void Window::setMenuVisible(bool visible) { return p.setMenuVisible(visible); } +void Window::setModal(bool modal) { + state.modal = modal; + return p.setModal(modal); +} + void Window::setResizable(bool resizable) { state.resizable = resizable; return p.setResizable(resizable); diff --git a/bsnes/phoenix/core/core.hpp b/bsnes/phoenix/core/core.hpp index 241d7552..d8494342 100755 --- a/bsnes/phoenix/core/core.hpp +++ b/bsnes/phoenix/core/core.hpp @@ -199,6 +199,7 @@ struct Window : private nall::base_from_member, Object { void setGeometry(const Geometry &geometry); void setMenuFont(const nall::string &font); void setMenuVisible(bool visible = true); + void setModal(bool modal = true); void setResizable(bool resizable = true); void setStatusFont(const nall::string &font); void setStatusText(const nall::string &text); diff --git a/bsnes/phoenix/core/state.hpp b/bsnes/phoenix/core/state.hpp index 33dd0c52..e87dfc3c 100755 --- a/bsnes/phoenix/core/state.hpp +++ b/bsnes/phoenix/core/state.hpp @@ -18,6 +18,7 @@ struct Window::State { set menu; string menuFont; bool menuVisible; + bool modal; bool resizable; string statusFont; string statusText; @@ -29,11 +30,12 @@ struct Window::State { State() { backgroundColorOverride = false; - backgroundColor = { 0, 0, 0, 255 }; + backgroundColor = {0, 0, 0, 255}; fullScreen = false; - geometry = { 128, 128, 256, 256 }; + geometry = {128, 128, 256, 256}; ignore = false; menuVisible = false; + modal = false; resizable = true; statusVisible = false; visible = false; @@ -115,7 +117,7 @@ struct Widget::State { State() { abstract = false; enabled = true; - geometry = { 0, 0, 0, 0 }; + geometry = {0, 0, 0, 0}; visible = true; } }; diff --git a/bsnes/phoenix/gtk/platform.hpp b/bsnes/phoenix/gtk/platform.hpp index 6fc0203f..34fb2e5f 100755 --- a/bsnes/phoenix/gtk/platform.hpp +++ b/bsnes/phoenix/gtk/platform.hpp @@ -119,6 +119,7 @@ struct pWindow : public pObject { void setGeometry(const Geometry &geometry); void setMenuFont(const string &font); void setMenuVisible(bool visible); + void setModal(bool modal); void setResizable(bool resizable); void setStatusFont(const string &font); void setStatusText(const string &text); diff --git a/bsnes/phoenix/gtk/window.cpp b/bsnes/phoenix/gtk/window.cpp index be3f2a7f..f1df5584 100755 --- a/bsnes/phoenix/gtk/window.cpp +++ b/bsnes/phoenix/gtk/window.cpp @@ -233,6 +233,10 @@ void pWindow::setMenuVisible(bool visible) { gtk_widget_set_visible(menu, visible); } +void pWindow::setModal(bool modal) { + gtk_window_set_modal(GTK_WINDOW(widget), modal); +} + void pWindow::setResizable(bool resizable) { gtk_window_set_resizable(GTK_WINDOW(widget), resizable); gtk_statusbar_set_has_resize_grip(GTK_STATUSBAR(status), resizable); diff --git a/bsnes/phoenix/qt/platform.moc b/bsnes/phoenix/qt/platform.moc index 81420cee..d27d94f6 100755 --- a/bsnes/phoenix/qt/platform.moc +++ b/bsnes/phoenix/qt/platform.moc @@ -1,7 +1,7 @@ /**************************************************************************** ** Meta object code from reading C++ file 'platform.moc.hpp' ** -** Created: Thu Apr 26 04:47:17 2012 +** Created: Tue May 1 09:36:37 2012 ** by: The Qt Meta Object Compiler version 62 (Qt 4.6.3) ** ** WARNING! All changes made in this file will be lost! diff --git a/bsnes/phoenix/qt/platform.moc.hpp b/bsnes/phoenix/qt/platform.moc.hpp index dfb05cc1..915ce82a 100755 --- a/bsnes/phoenix/qt/platform.moc.hpp +++ b/bsnes/phoenix/qt/platform.moc.hpp @@ -1,4 +1,4 @@ -static QApplication *qtApplication = 0; +static QApplication *qtApplication = nullptr; struct Settings : public configuration { bidirectional_map keymap; @@ -133,6 +133,7 @@ public: void setGeometry(const Geometry &geometry); void setMenuFont(const string &font); void setMenuVisible(bool visible); + void setModal(bool modal); void setResizable(bool resizable); void setStatusFont(const string &font); void setStatusText(const string &text); diff --git a/bsnes/phoenix/qt/window.cpp b/bsnes/phoenix/qt/window.cpp index 4bec7b14..9ea0d9a4 100755 --- a/bsnes/phoenix/qt/window.cpp +++ b/bsnes/phoenix/qt/window.cpp @@ -121,6 +121,10 @@ void pWindow::setMenuVisible(bool visible) { setGeometry(window.state.geometry); } +void pWindow::setModal(bool modal) { + qtWindow->setWindowModality(modal ? Qt::ApplicationModal : Qt::NonModal); +} + void pWindow::setResizable(bool resizable) { if(resizable) { qtLayout->setSizeConstraint(QLayout::SetDefaultConstraint); diff --git a/bsnes/phoenix/reference/platform.hpp b/bsnes/phoenix/reference/platform.hpp index ac759c0f..a72cde70 100755 --- a/bsnes/phoenix/reference/platform.hpp +++ b/bsnes/phoenix/reference/platform.hpp @@ -85,6 +85,7 @@ struct pWindow : public pObject { void setGeometry(const Geometry &geometry); void setMenuFont(const string &font); void setMenuVisible(bool visible); + void setModal(bool modal); void setResizable(bool resizable); void setStatusFont(const string &font); void setStatusText(const string &text); diff --git a/bsnes/phoenix/reference/window.cpp b/bsnes/phoenix/reference/window.cpp index a865b619..128a9c2d 100755 --- a/bsnes/phoenix/reference/window.cpp +++ b/bsnes/phoenix/reference/window.cpp @@ -8,7 +8,7 @@ void pWindow::append(Widget &widget) { } Color pWindow::backgroundColor() { - return { 0, 0, 0, 255 }; + return {0, 0, 0, 255}; } bool pWindow::focused() { @@ -16,11 +16,11 @@ bool pWindow::focused() { } Geometry pWindow::frameMargin() { - return { 0, 0, 0, 0 }; + return {0, 0, 0, 0}; } Geometry pWindow::geometry() { - return { 0, 0, 0, 0 }; + return {0, 0, 0, 0}; } void pWindow::remove(Layout &layout) { @@ -50,6 +50,9 @@ void pWindow::setMenuFont(const string &font) { void pWindow::setMenuVisible(bool visible) { } +void pWindow::setModal(bool modal) { +} + void pWindow::setResizable(bool resizable) { } diff --git a/bsnes/phoenix/windows/object.cpp b/bsnes/phoenix/windows/object.cpp index 8165306f..78811d79 100755 --- a/bsnes/phoenix/windows/object.cpp +++ b/bsnes/phoenix/windows/object.cpp @@ -1,4 +1,4 @@ -array pObject::objects; +vector pObject::objects; pObject::pObject(Object &object) : object(object) { static unsigned uniqueId = 100; diff --git a/bsnes/phoenix/windows/platform.cpp b/bsnes/phoenix/windows/platform.cpp index bce8037e..7aa30ce5 100755 --- a/bsnes/phoenix/windows/platform.cpp +++ b/bsnes/phoenix/windows/platform.cpp @@ -152,6 +152,7 @@ static bool OS_keyboardProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { if(dynamic_cast(object)) { Window &window = (Window&)*object; + if(pWindow::modal.size() > 0 && !pWindow::modal.find(&window.p)) return false; Keyboard::Keycode keysym = Keysym(wparam, lparam); if(keysym != Keyboard::Keycode::None) { if((msg == WM_KEYDOWN || msg == WM_SYSKEYDOWN) && window.onKeyPress) window.onKeyPress(keysym); @@ -220,11 +221,18 @@ static LRESULT CALLBACK OS_windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM if(!object || !dynamic_cast(object)) return DefWindowProc(hwnd, msg, wparam, lparam); Window &window = (Window&)*object; - if(!osQuit) switch(msg) { + bool process = true; + if(pWindow::modal.size() > 0 && !pWindow::modal.find(&window.p)) process = false; + if(osQuit) process = false; + + if(process) switch(msg) { case WM_CLOSE: { window.state.ignore = false; if(window.onClose) window.onClose(); - if(window.state.ignore == false) window.setVisible(false); + if(window.state.ignore == false) { + window.setVisible(false); + window.setModal(false); + } return TRUE; } diff --git a/bsnes/phoenix/windows/platform.hpp b/bsnes/phoenix/windows/platform.hpp index 1cdc04a1..701da25d 100755 --- a/bsnes/phoenix/windows/platform.hpp +++ b/bsnes/phoenix/windows/platform.hpp @@ -3,6 +3,7 @@ struct Settings { }; struct pFont; +struct pObject; struct pWindow; struct pMenu; struct pLayout; @@ -25,7 +26,7 @@ struct pDesktop { struct pKeyboard { static bool pressed(Keyboard::Scancode scancode); - static array state(); + static vector state(); static void initialize(); }; @@ -49,10 +50,11 @@ struct pMessageWindow { }; struct pObject { + static vector objects; + Object &object; uintptr_t id; bool locked; - static array objects; pObject(Object &object); static pObject* find(unsigned id); @@ -83,6 +85,9 @@ struct pTimer : public pObject { }; struct pWindow : public pObject { + static vector modal; + static void updateModality(); + Window &window; HWND hwnd; HMENU hmenu; @@ -107,6 +112,7 @@ struct pWindow : public pObject { void setGeometry(const Geometry &geometry); void setMenuFont(const string &font); void setMenuVisible(bool visible); + void setModal(bool modal); void setResizable(bool resizable); void setStatusFont(const string &font); void setStatusText(const string &text); diff --git a/bsnes/phoenix/windows/window.cpp b/bsnes/phoenix/windows/window.cpp index 4319d7c4..dd75c64c 100755 --- a/bsnes/phoenix/windows/window.cpp +++ b/bsnes/phoenix/windows/window.cpp @@ -1,3 +1,14 @@ +vector pWindow::modal; + +void pWindow::updateModality() { + for(auto &object : pObject::objects) { + if(dynamic_cast(object) == nullptr) continue; + pWindow *p = (pWindow*)object; + if(modal.size() == 0) EnableWindow(p->hwnd, true); + else EnableWindow(p->hwnd, modal.find(p)); + } +} + static const unsigned FixedStyle = WS_SYSMENU | WS_CAPTION | WS_MINIMIZEBOX | WS_BORDER; static const unsigned ResizableStyle = WS_SYSMENU | WS_CAPTION | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_THICKFRAME; @@ -128,6 +139,15 @@ void pWindow::setMenuVisible(bool visible) { locked = false; } +void pWindow::setModal(bool modality) { + if(modality == false) { + if(auto position = modal.find(this)) modal.remove(position()); + } else { + modal.appendonce(this); + } + updateModality(); +} + void pWindow::setResizable(bool resizable) { SetWindowLongPtr(hwnd, GWL_STYLE, window.state.resizable ? ResizableStyle : FixedStyle); setGeometry(window.state.geometry); diff --git a/bsnes/sfc/cartridge/cartridge.cpp b/bsnes/sfc/cartridge/cartridge.cpp index 0fd8af16..35877df7 100755 --- a/bsnes/sfc/cartridge/cartridge.cpp +++ b/bsnes/sfc/cartridge/cartridge.cpp @@ -8,14 +8,18 @@ namespace SuperFamicom { Cartridge cartridge; -void Cartridge::load(Mode cartridge_mode, const string &markup) { - mode = cartridge_mode; +void Cartridge::load(const string &markup, const stream &stream) { information.markup = markup; + rom.copy(stream); + sha256 = nall::sha256(rom.data(), rom.size()); //TODO: special case SGB, BSX, ST mode SHA256 sums region = Region::NTSC; ram_size = 0; - has_bsx_slot = false; + has_gb_slot = false; + has_bs_cart = false; + has_bs_slot = false; + has_st_slot = false; has_nss_dip = false; has_superfx = false; has_sa1 = false; @@ -37,32 +41,12 @@ void Cartridge::load(Mode cartridge_mode, const string &markup) { if(ram_size > 0) { ram.map(allocate(ram_size, 0xff), ram_size); - nvram.append({ "save.ram", ram.data(), ram.size() }); + interface->memory.append({ID::RAM, "save.ram"}); } rom.write_protect(true); ram.write_protect(false); - switch((Mode)mode) { - case Mode::Normal: - case Mode::BsxSlotted: - sha256 = nall::sha256(rom.data(), rom.size()); - break; - case Mode::Bsx: - sha256 = nall::sha256(bsxflash.memory.data(), bsxflash.memory.size()); - break; - case Mode::SufamiTurbo: - sha256 = nall::sha256(sufamiturbo.slotA.rom.data(), sufamiturbo.slotA.rom.size()); - break; - case Mode::SuperGameBoy: - #if defined(GAMEBOY) - sha256 = GameBoy::cartridge.sha256(); - #else - throw "Game Boy support not present"; - #endif - break; - } - system.load(); loaded = true; } diff --git a/bsnes/sfc/cartridge/cartridge.hpp b/bsnes/sfc/cartridge/cartridge.hpp index 5b3df94f..5f55db0d 100755 --- a/bsnes/sfc/cartridge/cartridge.hpp +++ b/bsnes/sfc/cartridge/cartridge.hpp @@ -1,12 +1,4 @@ struct Cartridge : property { - enum class Mode : unsigned { - Normal, - BsxSlotted, - Bsx, - SufamiTurbo, - SuperGameBoy, - }; - enum class Region : unsigned { NTSC, PAL, @@ -27,11 +19,13 @@ struct Cartridge : property { readonly loaded; readonly sha256; - readonly mode; readonly region; readonly ram_size; - readonly has_bsx_slot; + readonly has_gb_slot; + readonly has_bs_cart; + readonly has_bs_slot; + readonly has_st_slot; readonly has_nss_dip; readonly has_superfx; readonly has_sa1; @@ -82,7 +76,7 @@ struct Cartridge : property { } nss; } information; - void load(Mode, const string&); + void load(const string &markup, const stream &stream); void unload(); void serialize(serializer&); diff --git a/bsnes/sfc/cartridge/markup.cpp b/bsnes/sfc/cartridge/markup.cpp index 1908cc23..ab75d8c1 100755 --- a/bsnes/sfc/cartridge/markup.cpp +++ b/bsnes/sfc/cartridge/markup.cpp @@ -108,7 +108,9 @@ void Cartridge::parse_markup_nss(XML::Node &root) { void Cartridge::parse_markup_icd2(XML::Node &root) { #if defined(GAMEBOY) if(root.exists() == false) return; - if(mode != Mode::SuperGameBoy) return; + has_gb_slot = true; + + interface->mediaRequest({"Game Boy", "", "program.rom", "*.gb", 5}); icd2.revision = max(1, numeral(root["revision"].data)); @@ -367,8 +369,10 @@ void Cartridge::parse_markup_armdsp(XML::Node &root) { void Cartridge::parse_markup_bsx(XML::Node &root) { if(root.exists() == false) return; - if(mode != Mode::BsxSlotted && mode != Mode::Bsx) return; - has_bsx_slot = true; + has_bs_cart = root["mmio"].exists(); + has_bs_slot = true; + + interface->mediaRequest({"BS-X Satellaview", "", "program.rom", "*.bs", 2}); for(auto &node : root["slot"]) { if(node.name != "map") continue; @@ -394,7 +398,9 @@ void Cartridge::parse_markup_bsx(XML::Node &root) { void Cartridge::parse_markup_sufamiturbo(XML::Node &root) { if(root.exists() == false) return; - if(mode != Mode::SufamiTurbo) return; + has_st_slot = true; + + interface->mediaRequest({"Sufami Turbo", "", "program.rom", "*.st", 3}); for(auto &slot : root) { if(slot.name != "slot") continue; diff --git a/bsnes/sfc/chip/bsx/cartridge/cartridge.cpp b/bsnes/sfc/chip/bsx/cartridge/cartridge.cpp index 8b77d1d0..d2441f80 100755 --- a/bsnes/sfc/chip/bsx/cartridge/cartridge.cpp +++ b/bsnes/sfc/chip/bsx/cartridge/cartridge.cpp @@ -8,11 +8,11 @@ void BSXCartridge::init() { void BSXCartridge::load() { sram.map(allocate(32 * 1024, 0xff), 32 * 1024); sram.write_protect(false); - cartridge.nvram.append({ "bsx.ram", sram.data(), sram.size() }); + interface->memory.append({ID::BsxRAM, "bsx.ram"}); psram.map(allocate(512 * 1024, 0xff), 512 * 1024); psram.write_protect(false); - cartridge.nvram.append({ "bsx.psram", psram.data(), psram.size() }); + interface->memory.append({ID::BsxPSRAM, "bsx.psram"}); } void BSXCartridge::unload() { diff --git a/bsnes/sfc/chip/spc7110/spc7110.cpp b/bsnes/sfc/chip/spc7110/spc7110.cpp index 80c9a2b7..8be35c0a 100755 --- a/bsnes/sfc/chip/spc7110/spc7110.cpp +++ b/bsnes/sfc/chip/spc7110/spc7110.cpp @@ -8,14 +8,14 @@ SPC7110 spc7110; #include "serialization.cpp" #include "decomp.cpp" -const unsigned SPC7110::months[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; +const unsigned SPC7110::months[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; void SPC7110::init() { } void SPC7110::load() { for(unsigned n = 0; n < 20; n++) rtc[n] = 0xff; - if(cartridge.has_spc7110rtc()) cartridge.nvram.append({ "rtc.ram", rtc, 20 }); + if(cartridge.has_spc7110rtc()) interface->memory.append({ID::SPC7110RTC, "rtc.ram"}); } void SPC7110::unload() { diff --git a/bsnes/sfc/chip/srtc/srtc.cpp b/bsnes/sfc/chip/srtc/srtc.cpp index cad4e399..bc0b65b9 100755 --- a/bsnes/sfc/chip/srtc/srtc.cpp +++ b/bsnes/sfc/chip/srtc/srtc.cpp @@ -7,14 +7,14 @@ SRTC srtc; #include "serialization.cpp" -const unsigned SRTC::months[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; +const unsigned SRTC::months[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; void SRTC::init() { } void SRTC::load() { for(unsigned n = 0; n < 20; n++) rtc[n] = 0xff; - cartridge.nvram.append({ "rtc.ram", rtc, 20 }); + interface->memory.append({ID::RTC, "rtc.ram"}); } void SRTC::unload() { diff --git a/bsnes/sfc/interface/interface.cpp b/bsnes/sfc/interface/interface.cpp index 59517c21..c0a8c5dd 100755 --- a/bsnes/sfc/interface/interface.cpp +++ b/bsnes/sfc/interface/interface.cpp @@ -9,17 +9,74 @@ bool Interface::loaded() { } void Interface::load(unsigned id, const stream &stream, const string &markup) { - if(id == 0) { + if(id == ID::IPLROM) { stream.read(smp.iplrom, min(64u, stream.size())); } - if(id == 1) { - cartridge.rom.copy(stream); - cartridge.load(Cartridge::Mode::Normal, markup); + if(id == ID::ROM) { + cartridge.load(markup, stream); system.power(); input.connect(0, Input::Device::Joypad); input.connect(1, Input::Device::Joypad); } + + if(id == ID::SuperGameBoyROM) { + GameBoy::cartridge.load(GameBoy::System::Revision::SuperGameBoy, markup, stream); + } + + if(id == ID::BsxFlashROM) { + bsxflash.memory.copy(stream); + } + + if(id == ID::SufamiTurboSlotAROM) { + sufamiturbo.slotA.rom.copy(stream); + } + + if(id == ID::SufamiTurboSlotBROM) { + sufamiturbo.slotB.rom.copy(stream); + } + + if(id == ID::RAM) { + stream.read(cartridge.ram.data(), min(cartridge.ram.size(), stream.size())); + } + + if(id == ID::RTC) { + stream.read(srtc.rtc, min(stream.size(), sizeof srtc.rtc)); + } + + if(id == ID::SPC7110RTC) { + stream.read(spc7110.rtc, min(stream.size(), sizeof srtc.rtc)); + } + + if(id == ID::BsxRAM) { + stream.read(bsxcartridge.sram.data(), min(stream.size(), bsxcartridge.sram.size())); + } + + if(id == ID::BsxPSRAM) { + stream.read(bsxcartridge.psram.data(), min(stream.size(), bsxcartridge.psram.size())); + } +} + +void Interface::save(unsigned id, const stream &stream) { + if(id == ID::RAM) { + stream.write(cartridge.ram.data(), cartridge.ram.size()); + } + + if(id == ID::RTC) { + stream.write(srtc.rtc, sizeof srtc.rtc); + } + + if(id == ID::SPC7110RTC) { + stream.write(spc7110.rtc, sizeof srtc.rtc); + } + + if(id == ID::BsxRAM) { + stream.write(bsxcartridge.sram.data(), bsxcartridge.sram.size()); + } + + if(id == ID::BsxPSRAM) { + stream.write(bsxcartridge.psram.data(), bsxcartridge.psram.size()); + } } void Interface::unload() { @@ -45,28 +102,95 @@ void Interface::updatePalette() { Interface::Interface() { interface = this; - information.name = "Super Famicom"; - information.width = 256; - information.height = 240; - information.frequency = 32040; - information.ports = 2; - information.resettable = true; + information.name = "Super Famicom"; + information.width = 256; + information.height = 240; + information.aspectRatio = 8.0 / 7.0; + information.frequency = 32040; + information.resettable = true; + + information.media.append({"Super Famicom", "*.sfc"}); + information.media.append({"BS-X Satellaview", "*.bs"}); + information.media.append({"Sufami Turbo", "*.st"}); + information.media.append({"Super Game Boy", "*.gb"}); { Firmware firmware; firmware.displayname = "Super Famicom"; firmware.name = "Super Famicom.sys/spc700.rom"; - firmware.id = 0; + firmware.id = ID::IPLROM; this->firmware.append(firmware); } { - Media media; - media.displayname = "Super Famicom"; - media.name = "program.rom"; - media.filter = "*.sfc"; - media.id = 1; - this->media.append(media); + Schema schema; + schema.displayname = "Super Famicom"; + schema.name = "program.rom"; + schema.filter = "*.sfc"; + schema.id = ID::ROM; + this->schema.append(schema); + } + + { + Schema schema; + schema.displayname = "Super Game Boy"; + schema.path = "Super Game Boy.sfc/"; + schema.name = "program.rom"; + schema.filter = "*.sfc"; + schema.id = ID::ROM; + { + Media slot; + slot.displayname = "Game Boy"; + slot.name = "program.rom"; + slot.filter = "*.gb"; + slot.id = ID::SuperGameBoyROM; + schema.slot.append(schema); + } + this->schema.append(schema); + } + + { + Schema schema; + schema.displayname = "BS-X Satellaview"; + schema.path = "BS-X Satellaview.sfc/"; + schema.name = "program.rom"; + schema.filter = "*.sfc"; + schema.id = ID::ROM; + { + Media slot; + slot.displayname = "BS-X Satellaview"; + slot.name = "program.rom"; + slot.filter = "*.bs"; + slot.id = ID::BsxFlashROM; + schema.slot.append(slot); + } + this->schema.append(schema); + } + + { + Schema schema; + schema.displayname = "Sufami Turbo"; + schema.path = "Sufami Turbo.sfc/"; + schema.name = "program.rom"; + schema.filter = "*.sfc"; + schema.id = ID::ROM; + { + Media slot; + slot.displayname = "Sufami Turbo - Slot A"; + slot.name = "program.rom"; + slot.filter = "*.st"; + slot.id = ID::SufamiTurboSlotAROM; + schema.slot.append(slot); + } + { + Media slot; + slot.displayname = "Sufami Turbo - Slot B"; + slot.name = "program.rom"; + slot.filter = "*.st"; + slot.id = ID::SufamiTurboSlotBROM; + schema.slot.append(slot); + } + this->schema.append(schema); } { diff --git a/bsnes/sfc/interface/interface.hpp b/bsnes/sfc/interface/interface.hpp index 7be5c62e..f4323e90 100755 --- a/bsnes/sfc/interface/interface.hpp +++ b/bsnes/sfc/interface/interface.hpp @@ -2,12 +2,29 @@ namespace SuperFamicom { #endif +struct ID { + enum : unsigned { + IPLROM, + ROM, + SuperGameBoyROM, + BsxFlashROM, + SufamiTurboSlotAROM, + SufamiTurboSlotBROM, + RAM, + RTC, + SPC7110RTC, + BsxRAM, + BsxPSRAM, + }; +}; + struct Interface : Emulator::Interface { virtual string path(unsigned slot, const string &hint) { return ""; } virtual void message(const string &text) {} bool loaded(); void load(unsigned id, const stream &stream, const string &markup = ""); + void save(unsigned id, const stream &stream); void unload(); void power(); diff --git a/bsnes/sfc/system/serialization.cpp b/bsnes/sfc/system/serialization.cpp index 3ac7b883..22517121 100755 --- a/bsnes/sfc/system/serialization.cpp +++ b/bsnes/sfc/system/serialization.cpp @@ -57,10 +57,10 @@ void System::serialize_all(serializer &s) { ppu.serialize(s); dsp.serialize(s); - if(cartridge.mode() == Cartridge::Mode::SufamiTurbo) sufamiturbo.serialize(s); #if defined(GAMEBOY) - if(cartridge.mode() == Cartridge::Mode::SuperGameBoy) icd2.serialize(s); + if(cartridge.has_gb_slot()) icd2.serialize(s); #endif + if(cartridge.has_st_slot()) sufamiturbo.serialize(s); if(cartridge.has_superfx()) superfx.serialize(s); if(cartridge.has_sa1()) sa1.serialize(s); if(cartridge.has_necdsp()) necdsp.serialize(s); diff --git a/bsnes/sfc/system/system.cpp b/bsnes/sfc/system/system.cpp index a8965690..f4bc1d4b 100755 --- a/bsnes/sfc/system/system.cpp +++ b/bsnes/sfc/system/system.cpp @@ -103,13 +103,12 @@ void System::load() { ppu.enable(); if(expansion() == ExpansionPortDevice::BSX) bsxsatellaview.load(); - if(cartridge.mode() == Cartridge::Mode::Bsx) bsxcartridge.load(); - if(cartridge.mode() == Cartridge::Mode::SufamiTurbo) sufamiturbo.load(); #if defined(GAMEBOY) - if(cartridge.mode() == Cartridge::Mode::SuperGameBoy) icd2.load(); + if(cartridge.has_gb_slot()) icd2.load(); #endif - - if(cartridge.has_bsx_slot()) bsxflash.load(); + if(cartridge.has_bs_cart()) bsxcartridge.load(); + if(cartridge.has_bs_slot()) bsxflash.load(); + if(cartridge.has_st_slot()) sufamiturbo.load(); if(cartridge.has_nss_dip()) nss.load(); if(cartridge.has_superfx()) superfx.load(); if(cartridge.has_sa1()) sa1.load(); @@ -129,13 +128,12 @@ void System::load() { void System::unload() { if(expansion() == ExpansionPortDevice::BSX) bsxsatellaview.unload(); - if(cartridge.mode() == Cartridge::Mode::Bsx) bsxcartridge.unload(); - if(cartridge.mode() == Cartridge::Mode::SufamiTurbo) sufamiturbo.unload(); #if defined(GAMEBOY) - if(cartridge.mode() == Cartridge::Mode::SuperGameBoy) icd2.unload(); + if(cartridge.has_gb_slot()) icd2.unload(); #endif - - if(cartridge.has_bsx_slot()) bsxflash.unload(); + if(cartridge.has_bs_cart()) bsxcartridge.unload(); + if(cartridge.has_bs_slot()) bsxflash.unload(); + if(cartridge.has_st_slot()) sufamiturbo.unload(); if(cartridge.has_nss_dip()) nss.unload(); if(cartridge.has_superfx()) superfx.unload(); if(cartridge.has_sa1()) sa1.unload(); @@ -168,12 +166,11 @@ void System::power() { ppu.power(); if(expansion() == ExpansionPortDevice::BSX) bsxsatellaview.power(); - if(cartridge.mode() == Cartridge::Mode::Bsx) bsxcartridge.power(); #if defined(GAMEBOY) - if(cartridge.mode() == Cartridge::Mode::SuperGameBoy) icd2.power(); + if(cartridge.has_gb_slot()) icd2.power(); #endif - - if(cartridge.has_bsx_slot()) bsxflash.power(); + if(cartridge.has_bs_cart()) bsxcartridge.power(); + if(cartridge.has_bs_slot()) bsxflash.power(); if(cartridge.has_nss_dip()) nss.power(); if(cartridge.has_superfx()) superfx.power(); if(cartridge.has_sa1()) sa1.power(); @@ -197,13 +194,11 @@ void System::reset() { ppu.reset(); if(expansion() == ExpansionPortDevice::BSX) bsxsatellaview.reset(); - - if(cartridge.mode() == Cartridge::Mode::Bsx) bsxcartridge.reset(); #if defined(GAMEBOY) - if(cartridge.mode() == Cartridge::Mode::SuperGameBoy) icd2.reset(); + if(cartridge.has_gb_slot()) icd2.reset(); #endif - - if(cartridge.has_bsx_slot()) bsxflash.reset(); + if(cartridge.has_bs_cart()) bsxcartridge.reset(); + if(cartridge.has_bs_slot()) bsxflash.reset(); if(cartridge.has_nss_dip()) nss.reset(); if(cartridge.has_superfx()) superfx.reset(); if(cartridge.has_sa1()) sa1.reset(); @@ -218,7 +213,7 @@ void System::reset() { if(cartridge.has_link()) link.reset(); #if defined(GAMEBOY) - if(cartridge.mode() == Cartridge::Mode::SuperGameBoy) cpu.coprocessors.append(&icd2); + if(cartridge.has_gb_slot()) cpu.coprocessors.append(&icd2); #endif if(cartridge.has_superfx()) cpu.coprocessors.append(&superfx); if(cartridge.has_sa1()) cpu.coprocessors.append(&sa1); diff --git a/bsnes/target-ethos/Makefile b/bsnes/target-ethos/Makefile index 52efb36f..5ce67b7c 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-utility ui-input ui-general ui-settings +ui_objects := ui-ethos ui-configuration ui-interface ui-utility ui-input ui-general ui-settings ui_objects += phoenix ruby ui_objects += $(if $(call streq,$(platform),win),resource) @@ -39,6 +39,7 @@ objects := $(ui_objects) $(objects) objects := $(patsubst %,obj/%.o,$(objects)) obj/ui-ethos.o: $(ui)/ethos.cpp $(call rwildcard,$(ui)/) +obj/ui-configuration.o: $(ui)/configuration/configuration.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)/) diff --git a/bsnes/target-ethos/bootstrap.cpp b/bsnes/target-ethos/bootstrap.cpp index ac40604b..869d7a0e 100755 --- a/bsnes/target-ethos/bootstrap.cpp +++ b/bsnes/target-ethos/bootstrap.cpp @@ -16,6 +16,7 @@ void Application::bootstrap() { system->callback.videoRefresh = {&Interface::videoRefresh, interface}; system->callback.audioSample = {&Interface::audioSample, interface}; system->callback.inputPoll = {&Interface::inputPoll, interface}; + system->callback.mediaRequest = {&Interface::mediaRequest, interface}; system->updatePalette(); for(auto &firmware : system->firmware) { diff --git a/bsnes/target-ethos/configuration/configuration.cpp b/bsnes/target-ethos/configuration/configuration.cpp new file mode 100755 index 00000000..8e53a966 --- /dev/null +++ b/bsnes/target-ethos/configuration/configuration.cpp @@ -0,0 +1,18 @@ +#include "../ethos.hpp" +Configuration *config = nullptr; + +Configuration::Configuration() { + append(video.scaleMode = 0, "Video::ScaleMode"); + append(video.aspectCorrection = true, "Video::AspectCorrection"); + + load(); +} + +void Configuration::load() { + configuration::load(application->path("settings.cfg")); + save(); //creates file if it does not exist +} + +void Configuration::save() { + configuration::save(application->path("settings.cfg")); +} diff --git a/bsnes/target-ethos/configuration/configuration.hpp b/bsnes/target-ethos/configuration/configuration.hpp new file mode 100755 index 00000000..b83b5a4c --- /dev/null +++ b/bsnes/target-ethos/configuration/configuration.hpp @@ -0,0 +1,12 @@ +struct Configuration : configuration { + struct Video { + unsigned scaleMode; + bool aspectCorrection; + } video; + + void load(); + void save(); + Configuration(); +}; + +extern Configuration *config; diff --git a/bsnes/target-ethos/ethos.cpp b/bsnes/target-ethos/ethos.cpp index 501ef2f8..b78e9d29 100755 --- a/bsnes/target-ethos/ethos.cpp +++ b/bsnes/target-ethos/ethos.cpp @@ -61,6 +61,7 @@ Application::Application(int argc, char **argv) { monospaceFont = "Liberation Mono, 8"; } + config = new Configuration; utility = new Utility; inputManager = new InputManager; browser = new Browser; @@ -93,7 +94,7 @@ Application::Application(int argc, char **argv) { dspaudio.setPrecision(16); dspaudio.setVolume(2.0); dspaudio.setBalance(0.0); - dspaudio.setResampler(DSP::ResampleEngine::Linear); + dspaudio.setResampler(DSP::ResampleEngine::Sinc); dspaudio.setResamplerFrequency(48000u); while(quit == false) { @@ -101,6 +102,8 @@ Application::Application(int argc, char **argv) { run(); } + if(active && system().loaded()) utility->unload(); + config->save(); browser->saveConfiguration(); inputManager->saveConfiguration(); } diff --git a/bsnes/target-ethos/ethos.hpp b/bsnes/target-ethos/ethos.hpp index a01cff0d..eaef9379 100755 --- a/bsnes/target-ethos/ethos.hpp +++ b/bsnes/target-ethos/ethos.hpp @@ -7,6 +7,7 @@ #include #include #include +#include #include using namespace nall; @@ -16,6 +17,7 @@ using namespace phoenix; #include using namespace ruby; +#include "configuration/configuration.hpp" #include "interface/interface.hpp" #include "utility/utility.hpp" #include "input/input.hpp" diff --git a/bsnes/target-ethos/general/browser.cpp b/bsnes/target-ethos/general/browser.cpp index 550b8244..f2a4fa4c 100755 --- a/bsnes/target-ethos/general/browser.cpp +++ b/bsnes/target-ethos/general/browser.cpp @@ -41,6 +41,7 @@ Browser::Browser() { fileList.onChange = {&Browser::synchronize, this}; fileList.onActivate = openButton.onActivate = {&Browser::fileListActivate, this}; + onClose = [&] { dialogActive = false; }; synchronize(); } @@ -49,7 +50,7 @@ void Browser::synchronize() { openButton.setEnabled(fileList.selected()); if(fileList.selected()) { for(auto &folder : folderList) { - if(folder.filter == media.filter) { + if(folder.filter == filter) { folder.selection = fileList.selection(); } } @@ -62,10 +63,10 @@ void Browser::saveConfiguration() { void Browser::bootstrap() { for(auto &emulator : application->emulator) { - for(auto &media : emulator->media) { + for(auto &media : emulator->information.media) { bool found = false; for(auto &folder : folderList) { - if(folder.filter == media.filter) { + if(folder.filter == filter) { found = true; break; } @@ -89,16 +90,13 @@ void Browser::bootstrap() { config.save(application->path("paths.cfg")); } -void Browser::open(Emulator::Interface::Media &media, function callback) { - this->media = media; - this->callback = callback; - - setTitle({"Load ", media.displayname}); +string Browser::select(const string &title, const string &filter) { + this->filter = filter; string path; unsigned selection = 0; for(auto &folder : folderList) { - if(folder.filter == media.filter) { + if(folder.filter == filter) { path = folder.path; selection = folder.selection; break; @@ -107,15 +105,25 @@ void Browser::open(Emulator::Interface::Media &media, function ca if(path.empty()) path = application->basepath; setPath(path, selection); - filterLabel.setText({"Files of type: ", media.filter}); + filterLabel.setText({"Files of type: ", filter}); + setTitle(title); + setModal(); setVisible(); fileList.setFocused(); + dialogActive = true; + outputFilename = ""; + while(dialogActive == true) { + OS::processEvents(); + } + + return outputFilename; } void Browser::setPath(const string &path, unsigned selection) { + //save path for next browser selection for(auto &folder : folderList) { - if(folder.filter == media.filter) folder.path = path; + if(folder.filter == filter) folder.path = path; } this->path = path; @@ -127,7 +135,7 @@ void Browser::setPath(const string &path, unsigned selection) { lstring contents = directory::folders(path); for(auto &filename : contents) { - string filter = {media.filter, "/"}; + string filter = {this->filter, "/"}; if(!filename.wildcard(R"(*.??/)") && !filename.wildcard(R"(*.???/)")) { string name = filename; name.rtrim<1>("/"); @@ -138,7 +146,7 @@ void Browser::setPath(const string &path, unsigned selection) { } for(auto &filename : contents) { - string filter = {media.filter, "/"}; + string filter = {this->filter, "/"}; if(filename.wildcard(R"(*.??/)") || filename.wildcard(R"(*.???/)")) { if(filename.wildcard(filter)) { string name = filename; @@ -158,8 +166,10 @@ void Browser::setPath(const string &path, unsigned selection) { void Browser::fileListActivate() { unsigned selection = fileList.selection(); string filename = filenameList[selection]; - string filter = {media.filter, "/"}; + string filter = {this->filter, "/"}; if(filename.wildcard(filter) == false) return setPath({path, filename}); + setVisible(false); - if(callback) callback({path, filename}); + dialogActive = false; + outputFilename = {path, filename}; } diff --git a/bsnes/target-ethos/general/browser.hpp b/bsnes/target-ethos/general/browser.hpp index ff64a18c..7162cf7e 100755 --- a/bsnes/target-ethos/general/browser.hpp +++ b/bsnes/target-ethos/general/browser.hpp @@ -9,7 +9,7 @@ struct Browser : Window { Label filterLabel; Button openButton; - void open(Emulator::Interface::Media &media, function callback); + string select(const string &title, const string &filter); void saveConfiguration(); void synchronize(); void bootstrap(); @@ -24,8 +24,10 @@ private: }; vector folderList; - Emulator::Interface::Media media; - function callback; + bool dialogActive; + string outputFilename; + + string filter; string path; lstring filenameList; diff --git a/bsnes/target-ethos/general/presentation.cpp b/bsnes/target-ethos/general/presentation.cpp index 09944e42..5f2de4a2 100755 --- a/bsnes/target-ethos/general/presentation.cpp +++ b/bsnes/target-ethos/general/presentation.cpp @@ -6,9 +6,16 @@ void Presentation::synchronize() { if(system->interface == application->active) { activeSystem = system; system->menu.setVisible(true); - return; } } + + switch(config->video.scaleMode) { + case 0: centerVideo.setChecked(); break; + case 1: scaleVideo.setChecked(); break; + case 2: stretchVideo.setChecked(); break; + } + aspectCorrection.setChecked(config->video.aspectCorrection); + resizeWindow.setVisible(application->active && config->video.scaleMode != 2); } void Presentation::setSystemName(const string &name) { @@ -28,22 +35,39 @@ Presentation::Presentation() : activeSystem(nullptr) { loadMenu.setText("Load"); settingsMenu.setText("Settings"); + videoMenu.setText("Video"); + centerVideo.setText("Center"); + scaleVideo.setText("Scale"); + stretchVideo.setText("Stretch"); + RadioItem::group(centerVideo, scaleVideo, stretchVideo); + aspectCorrection.setText("Correct Aspect Ratio"); configurationSettings.setText("Configuration ..."); toolsMenu.setText("Tools"); + resizeWindow.setText("Resize Window"); append(loadMenu); for(auto &item : loadList) loadMenu.append(*item); for(auto &system : emulatorList) append(system->menu); append(settingsMenu); + settingsMenu.append(videoMenu); + videoMenu.append(centerVideo, scaleVideo, stretchVideo, *new Separator, aspectCorrection); + settingsMenu.append(*new Separator); settingsMenu.append(configurationSettings); append(toolsMenu); + toolsMenu.append(resizeWindow); append(layout); layout.append(viewport, {0, 0, 720, 480}); + onSize = [&] { utility->resize(); }; onClose = [&] { application->quit = true; }; + centerVideo.onActivate = [&] { config->video.scaleMode = 0; utility->resize(); }; + scaleVideo.onActivate = [&] { config->video.scaleMode = 1; utility->resize(); }; + stretchVideo.onActivate = [&] { config->video.scaleMode = 2; utility->resize(); }; + aspectCorrection.onToggle = [&] { config->video.aspectCorrection = aspectCorrection.checked(); utility->resize(); }; configurationSettings.onActivate = [&] { settings->setVisible(); }; + resizeWindow.onActivate = [&] { utility->resize(true); }; synchronize(); } @@ -53,13 +77,11 @@ void Presentation::bootstrap() { System *system = new System; system->interface = emulator; - for(auto &media : emulator->media) { + for(auto &schema : emulator->schema) { Item *item = new Item; - item->setText({media.displayname, " ..."}); - item->onActivate = [=, &media] { - browser->open(media, [=, &media](string filename) { - utility->loadMedia(system->interface, media, filename); - }); + item->setText({schema.displayname, " ..."}); + item->onActivate = [=, &schema] { + utility->loadSchema(system->interface, schema); }; loadList.append(item); } diff --git a/bsnes/target-ethos/general/presentation.hpp b/bsnes/target-ethos/general/presentation.hpp index 9aefbd46..7c65686e 100755 --- a/bsnes/target-ethos/general/presentation.hpp +++ b/bsnes/target-ethos/general/presentation.hpp @@ -15,10 +15,16 @@ struct Presentation : Window { vector emulatorList; Menu loadMenu; - vector loadList; + vector loadList; Menu settingsMenu; + Menu videoMenu; + RadioItem centerVideo; + RadioItem scaleVideo; + RadioItem stretchVideo; + CheckItem aspectCorrection; Item configurationSettings; Menu toolsMenu; + Item resizeWindow; void synchronize(); void setSystemName(const string &name); diff --git a/bsnes/target-ethos/input/hotkeys.cpp b/bsnes/target-ethos/input/hotkeys.cpp index d28fb772..744e3a12 100755 --- a/bsnes/target-ethos/input/hotkeys.cpp +++ b/bsnes/target-ethos/input/hotkeys.cpp @@ -1,4 +1,16 @@ void InputManager::appendHotkeys() { + { + auto hotkey = new HotkeyInput; + hotkey->name = "Toggle Fullscreen Mode"; + hotkey->mapping = "KB0::Alt,KB0::Return"; + hotkey->logic = 1; + hotkeyMap.append(hotkey); + + hotkey->press = [] { + utility->toggleFullScreen(); + }; + } + { auto hotkey = new HotkeyInput; hotkey->name = "Fast Forward"; diff --git a/bsnes/target-ethos/interface/interface.cpp b/bsnes/target-ethos/interface/interface.cpp index da0c8bfc..82b35b48 100755 --- a/bsnes/target-ethos/interface/interface.cpp +++ b/bsnes/target-ethos/interface/interface.cpp @@ -46,3 +46,13 @@ int16_t Interface::inputPoll(unsigned port, unsigned device, unsigned input) { unsigned guid = system().port[port].device[device].input[input].guid; return inputManager->inputMap[guid]->poll(); } + +void Interface::mediaRequest(Emulator::Interface::Media media) { + string pathname = browser->select({"Load ", media.displayname}, media.filter); + if(pathname.empty()) return; + + string markup; + markup.readfile({pathname, "manifest.xml"}); + mmapstream stream({pathname, media.name}); + system().load(media.id, stream, markup); +} diff --git a/bsnes/target-ethos/interface/interface.hpp b/bsnes/target-ethos/interface/interface.hpp index eeae179f..ecfb9502 100755 --- a/bsnes/target-ethos/interface/interface.hpp +++ b/bsnes/target-ethos/interface/interface.hpp @@ -3,6 +3,7 @@ struct Interface { 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 input); + void mediaRequest(Emulator::Interface::Media media); }; extern Interface *interface; diff --git a/bsnes/target-ethos/settings/settings.hpp b/bsnes/target-ethos/settings/settings.hpp index e4b1ad9b..f0ed1026 100755 --- a/bsnes/target-ethos/settings/settings.hpp +++ b/bsnes/target-ethos/settings/settings.hpp @@ -16,7 +16,6 @@ struct Settings : Window { ListView panelList; void panelChanged(); - Settings(); }; diff --git a/bsnes/target-ethos/utility/utility.cpp b/bsnes/target-ethos/utility/utility.cpp index 1775eebe..0a6f8b97 100755 --- a/bsnes/target-ethos/utility/utility.cpp +++ b/bsnes/target-ethos/utility/utility.cpp @@ -7,6 +7,15 @@ void Utility::setInterface(Emulator::Interface *emulator) { presentation->synchronize(); } +void Utility::loadSchema(Emulator::Interface *emulator, Emulator::Interface::Schema &schema) { + string pathname; + if(!schema.path.empty()) pathname = application->path(schema.path); + if(!directory::exists(pathname)) pathname = browser->select(schema.displayname, schema.filter); + if(!directory::exists(pathname)) return; + + loadMedia(emulator, schema, pathname); +} + void Utility::loadMedia(Emulator::Interface *emulator, Emulator::Interface::Media &media, const string &pathname) { unload(); setInterface(emulator); @@ -29,11 +38,13 @@ void Utility::loadMedia(Emulator::Interface *emulator, Emulator::Interface::Medi displayname.rtrim<1>("/"); presentation->setTitle(notdir(nall::basename(displayname))); presentation->setSystemName(media.displayname); + resize(); } void Utility::saveMedia() { for(auto &memory : system().memory) { - file::write({pathname, memory.name}, memory.data, memory.size); + filestream fs({pathname, memory.name}, file::mode::write); + system().save(memory.id, fs); } } @@ -57,6 +68,70 @@ void Utility::unload() { video.clear(); } +void Utility::resize(bool resizeWindow) { + if(application->active == nullptr) return; + Geometry geometry = presentation->geometry(); + unsigned width = system().information.width; + unsigned height = system().information.height; + + unsigned scaledWidth = geometry.width / width; + unsigned scaledHeight = geometry.height / height; + unsigned multiplier = max(1u, min(scaledWidth, scaledHeight)); + + if(config->video.aspectCorrection) { + width *= system().information.aspectRatio; + } + + width *= multiplier; + height *= multiplier; + + unsigned scaleMode = 0; + + if(config->video.scaleMode == 1) { + width = (double)width * ((double)geometry.height / height); + height = geometry.height; + } + + if(config->video.scaleMode == 2) { + width = geometry.width; + height = geometry.height; + } + + if(resizeWindow == false) { + if(geometry.width < width ) width = geometry.width; + if(geometry.height < height) height = geometry.height; + + presentation->viewport.setGeometry({ + (geometry.width - width) / 2, (geometry.height - height) / 2, width, height + }); + } else { + presentation->setGeometry({geometry.x, geometry.y, width, height}); + presentation->viewport.setGeometry({0, 0, width, height}); + } + + presentation->synchronize(); +} + +void Utility::toggleFullScreen() { + static Geometry geometry; + + if(presentation->fullScreen() == false) { + geometry = presentation->geometry(); + presentation->setMenuVisible(false); + presentation->setStatusVisible(false); + presentation->setFullScreen(true); + input.acquire(); + } else { + input.unacquire(); + presentation->setMenuVisible(true); + presentation->setStatusVisible(true); + presentation->setFullScreen(false); + presentation->setGeometry(geometry); + } + + resize(); +} + 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 index 3a316761..64e09a6d 100755 --- a/bsnes/target-ethos/utility/utility.hpp +++ b/bsnes/target-ethos/utility/utility.hpp @@ -2,6 +2,7 @@ struct Utility { string pathname; void setInterface(Emulator::Interface *emulator); + void loadSchema(Emulator::Interface *emulator, Emulator::Interface::Schema &schema); void loadMedia(Emulator::Interface *emulator, Emulator::Interface::Media &media, const string &pathname); void saveMedia(); @@ -9,6 +10,8 @@ struct Utility { void reset(); void unload(); + void resize(bool resizeWindow = false); + void toggleFullScreen(); void setStatusText(const string &text); };