diff --git a/higan/emulator/emulator.hpp b/higan/emulator/emulator.hpp index 170bae41..43a98c1d 100644 --- a/higan/emulator/emulator.hpp +++ b/higan/emulator/emulator.hpp @@ -12,7 +12,7 @@ using namespace nall; namespace Emulator { static const string Name = "higan"; - static const string Version = "106.26"; + static const string Version = "106.27"; static const string Author = "byuu"; static const string License = "GPLv3"; static const string Website = "https://byuu.org/"; diff --git a/higan/target-bsnes/bsnes.cpp b/higan/target-bsnes/bsnes.cpp index 13278d93..70f4c60f 100644 --- a/higan/target-bsnes/bsnes.cpp +++ b/higan/target-bsnes/bsnes.cpp @@ -9,11 +9,8 @@ auto locate(string name) -> string { string location = {Path::program(), name}; if(inode::exists(location)) return location; - location = {Path::config(), "bsnes/", name}; - if(inode::exists(location)) return location; - - directory::create({Path::local(), "bsnes/"}); - return {Path::local(), "bsnes/", name}; + directory::create({Path::userData(), "bsnes/"}); + return {Path::userData(), "bsnes/", name}; } #include diff --git a/higan/target-bsnes/presentation/presentation.cpp b/higan/target-bsnes/presentation/presentation.cpp index 858a2789..8084359f 100644 --- a/higan/target-bsnes/presentation/presentation.cpp +++ b/higan/target-bsnes/presentation/presentation.cpp @@ -99,6 +99,7 @@ Presentation::Presentation() { inputSettings.setText("Input ...").onActivate([&] { settingsWindow->show(0); }); hotkeySettings.setText("Hotkeys ...").onActivate([&] { settingsWindow->show(1); }); pathSettings.setText("Paths ...").onActivate([&] { settingsWindow->show(2); }); + advancedSettings.setText("Advanced ...").onActivate([&] { settingsWindow->show(3); }); toolsMenu.setText("Tools"); saveState.setText("Save State").setEnabled(false); @@ -122,10 +123,6 @@ Presentation::Presentation() { aboutWindow->setCentered(*this).setVisible().setFocused(); }); - image icon{Resource::Icon}; - icon.alphaBlend(0xff000000); - canvas.setIcon(icon).setVisible(false); - viewport.setDroppable().onDrop([&](auto locations) { program->gameQueue = locations; program->load(); @@ -149,10 +146,10 @@ Presentation::Presentation() { setCentered(); #if defined(PLATFORM_WINDOWS) - Application::Windows::onModalChange([](bool modal) { + Application::Windows::onModalChange([&](bool modal) { if(modal && audio) audio->clear(); }); - Application::Windows::onScreenSaver([]() -> bool { + Application::Windows::onScreenSaver([&]() -> bool { if(emulator->loaded()) { if(pauseEmulation.checked()) return true; if(!program->focused() && settingsWindow->input.pauseEmulation.checked()) return true; @@ -170,24 +167,27 @@ Presentation::Presentation() { #endif } -auto Presentation::showIcon() -> void { - Application::processEvents(); - int width = geometry().width(); - int height = geometry().height(); - int x = width - 144; - int y = height - 128; - if(x >= 0 && y >= 0) { - canvas.setVisible(true).setGeometry({x, y, 128, 128}); - } else { - canvas.setVisible(false); +auto Presentation::drawIcon(uint32_t* output, uint length, uint width, uint height) -> void { + int ox = width - 144; + int oy = height - 128; + if(ox >= 0 && oy >= 0) { + image icon{Resource::Icon}; + icon.alphaBlend(0xff000000); + for(uint y : range(128)) { + auto target = output + (y + oy) * (length >> 2) + ox; + auto source = (uint32_t*)icon.data() + y * 128; + memory::copy(target, source, 128 * sizeof(uint32_t)); + } } - viewport.setGeometry({0, 0, 1, 1}); } auto Presentation::clearViewport() -> void { - if(!emulator->loaded()) showIcon(); if(!video) return; + if(!emulator->loaded()) { + viewport.setGeometry({0, 0, geometry().width(), geometry().height()}); + } + uint32_t* output; uint length; uint width = viewport.geometry().width(); @@ -197,17 +197,21 @@ auto Presentation::clearViewport() -> void { auto line = output + y * (length >> 2); for(uint x : range(width)) *line++ = 0xff000000; } + if(!emulator->loaded()) drawIcon(output, length, width, height); video->unlock(); video->output(); } } auto Presentation::resizeViewport() -> void { - if(emulator->loaded()) canvas.setVisible(false); - uint windowWidth = geometry().width(); uint windowHeight = geometry().height(); + if(!emulator->loaded()) { + viewport.setGeometry({0, 0, windowWidth, windowHeight}); + return clearViewport(); + } + double width = 224 * (settings["View/AspectCorrection"].boolean() ? 8.0 / 7.0 : 1.0); double height = (settings["View/OverscanCropping"].boolean() ? 224.0 : 240.0); @@ -265,6 +269,9 @@ auto Presentation::toggleFullscreenMode() -> void { menuBar.setVisible(true); statusBar.setVisible(settings["UserInterface/ShowStatusBar"].boolean()); } + //hack: give window geometry time to update after toggling fullscreen and menu/status bars + usleep(20 * 1000); + Application::processEvents(); resizeViewport(); } diff --git a/higan/target-bsnes/presentation/presentation.hpp b/higan/target-bsnes/presentation/presentation.hpp index 73055bed..8c3c1349 100644 --- a/higan/target-bsnes/presentation/presentation.hpp +++ b/higan/target-bsnes/presentation/presentation.hpp @@ -10,7 +10,7 @@ struct AboutWindow : Window { struct Presentation : Window { Presentation(); - auto showIcon() -> void; + auto drawIcon(uint32_t* output, uint length, uint width, uint height) -> void; auto clearViewport() -> void; auto resizeViewport() -> void; auto resizeWindow() -> void; @@ -49,6 +49,7 @@ struct Presentation : Window { MenuItem inputSettings{&settingsMenu}; MenuItem hotkeySettings{&settingsMenu}; MenuItem pathSettings{&settingsMenu}; + MenuItem advancedSettings{&settingsMenu}; Menu toolsMenu{&menuBar}; Menu saveState{&toolsMenu}; MenuItem saveState1{&saveState}; @@ -67,7 +68,6 @@ struct Presentation : Window { MenuItem about{&helpMenu}; FixedLayout layout{this}; - Canvas canvas{&layout, Geometry{0, 0, 1, 1}}; Viewport viewport{&layout, Geometry{0, 0, 1, 1}}; StatusBar statusBar{this}; diff --git a/higan/target-bsnes/program/program.cpp b/higan/target-bsnes/program/program.cpp index f61e0093..d3d66953 100644 --- a/higan/target-bsnes/program/program.cpp +++ b/higan/target-bsnes/program/program.cpp @@ -14,7 +14,10 @@ Program::Program(string_vector arguments) { presentation->setVisible(); if(settings["Crashed"].boolean()) { - MessageDialog().setText("Driver crash detected. Hardware drivers have been disabled.").information(); + MessageDialog( + "Driver crash detected. Hardware drivers have been disabled.\n" + "Please reconfigure drivers in the advanced settings panel." + ).setParent(*presentation).information(); settings["Video/Driver"].setValue("None"); settings["Audio/Driver"].setValue("None"); settings["Input/Driver"].setValue("None"); @@ -24,8 +27,29 @@ Program::Program(string_vector arguments) { settings.save(); initializeVideoDriver(); + if(!video->ready()) { + MessageDialog({ + "Error: failed to initialize ", settings["Video/Driver"].text(), " video driver." + }).setParent(*presentation).error(); + settings["Video/Driver"].setValue("None"); + initializeVideoDriver(); + } initializeAudioDriver(); + if(!audio->ready()) { + MessageDialog({ + "Error: failed to initialize ", settings["Audio/Driver"].text(), " audio driver." + }).setParent(*presentation).error(); + settings["Audio/Driver"].setValue("None"); + initializeAudioDriver(); + } initializeInputDriver(); + if(!input->ready()) { + MessageDialog({ + "Error: failed to initialize ", settings["Input/Driver"].text(), " input driver." + }).setParent(*presentation).error(); + settings["Input/Driver"].setValue("None"); + initializeInputDriver(); + } settings["Crashed"].setValue(false); settings.save(); diff --git a/higan/target-bsnes/program/utility.cpp b/higan/target-bsnes/program/utility.cpp index 18ae6e9c..6e02873e 100644 --- a/higan/target-bsnes/program/utility.cpp +++ b/higan/target-bsnes/program/utility.cpp @@ -3,12 +3,11 @@ auto Program::initializeVideoDriver() -> void { video->setContext(presentation->viewport.handle()); video->setExclusive(false); video->setBlocking(settings["Video/Blocking"].boolean()); - if(!video->ready()) { - MessageDialog().setText("Failed to initialize video driver").warning(); - video = Video::create("None"); + + if(video->ready()) { + presentation->clearViewport(); + updateVideoShader(); } - presentation->clearViewport(); - updateVideoShader(); } auto Program::initializeAudioDriver() -> void { @@ -29,19 +28,11 @@ auto Program::initializeAudioDriver() -> void { } audio->setLatency(settings["Audio/Latency"].natural()); audio->setChannels(2); - if(!audio->ready()) { - MessageDialog().setText("Failed to initialize audio driver").warning(); - audio = Audio::create("None"); - } } auto Program::initializeInputDriver() -> void { input = Input::create(settings["Input/Driver"].text()); input->setContext(presentation->viewport.handle()); - if(!input->ready()) { - MessageDialog().setText("Failed to initialize input driver").warning(); - input = Input::create("None"); - } } auto Program::updateVideoShader() -> void { diff --git a/higan/target-bsnes/settings/advanced.cpp b/higan/target-bsnes/settings/advanced.cpp new file mode 100644 index 00000000..5b4841ab --- /dev/null +++ b/higan/target-bsnes/settings/advanced.cpp @@ -0,0 +1,120 @@ +AdvancedSettings::AdvancedSettings(TabFrame* parent) : TabFrameItem(parent) { + setIcon(Icon::Action::Settings); + setText("Advanced"); + + layout.setMargin(5); + + videoDriverLabel.setText("Video Driver:"); + for(auto& driver : Video::availableDrivers()) { + ComboButtonItem item; + item.setText(driver); + videoDriverOption.append(item); + if(video->driver() == driver) item.setSelected(); + } + videoDriverOption.onChange([&] { + auto item = videoDriverOption.selected(); + videoDriverChange.setEnabled(video->driver() != item.text()); + }); + videoDriverChange.setText("Change").setEnabled(false).onActivate([&] { + auto item = videoDriverOption.selected(); + settings["Video/Driver"].setValue(item.text()); + if(!emulator->loaded() || item.text() == "None" || MessageDialog( + "Warning: incompatible drivers may cause bsnes to crash.\n" + "It is highly recommended you unload your game first to avoid data loss.\n" + "Do you wish to proceed with the video driver change now anyway?" + ).setParent(*settingsWindow).question() == "Yes") { + settings["Crashed"].setValue(true); + settings.save(); + program->initializeVideoDriver(); + if(!video->ready()) { + MessageDialog({ + "Error: failed to initialize ", item.text(), " video driver." + }).setParent(*settingsWindow).error(); + settings["Video/Driver"].setValue("None"); + program->initializeVideoDriver(); + for(auto item : videoDriverOption.items()) { + if(video->driver() == item.text()) item.setSelected(); + } + } + settings["Crashed"].setValue(false); + settings.save(); + videoDriverChange.setEnabled(false); + } + }); + + audioDriverLabel.setText("Audio Driver:"); + for(auto& driver : Audio::availableDrivers()) { + ComboButtonItem item; + item.setText(driver); + audioDriverOption.append(item); + if(audio->driver() == driver) item.setSelected(); + } + audioDriverOption.onChange([&] { + auto item = audioDriverOption.selected(); + audioDriverChange.setEnabled(audio->driver() != item.text()); + }); + audioDriverChange.setText("Change").setEnabled(false).onActivate([&] { + auto item = audioDriverOption.selected(); + settings["Audio/Driver"].setValue(item.text()); + if(!emulator->loaded() || item.text() == "None" || MessageDialog( + "Warning: incompatible drivers may cause bsnes to crash.\n" + "It is highly recommended you unload your game first to avoid data loss.\n" + "Do you wish to proceed with the audio driver change now anyway?" + ).setParent(*settingsWindow).question() == "Yes") { + settings["Crashed"].setValue(true); + settings.save(); + program->initializeAudioDriver(); + if(!audio->ready()) { + MessageDialog({ + "Error: failed to initialize ", item.text(), " audio driver." + }).setParent(*settingsWindow).error(); + settings["Audio/Driver"].setValue("None"); + program->initializeAudioDriver(); + for(auto item : audioDriverOption.items()) { + if(audio->driver() == item.text()) item.setSelected(); + } + } + settings["Crashed"].setValue(false); + settings.save(); + audioDriverChange.setEnabled(false); + } + }); + + inputDriverLabel.setText("Input Driver:"); + for(auto& driver : Input::availableDrivers()) { + ComboButtonItem item; + item.setText(driver); + inputDriverOption.append(item); + if(input->driver() == driver) item.setSelected(); + } + inputDriverOption.onChange([&] { + auto item = inputDriverOption.selected(); + inputDriverChange.setEnabled(input->driver() != item.text()); + }); + inputDriverChange.setText("Change").setEnabled(false).onActivate([&] { + auto item = inputDriverOption.selected(); + settings["Input/Driver"].setValue(item.text()); + if(!emulator->loaded() || item.text() == "None" || MessageDialog( + "Warning: incompatible drivers may cause bsnes to crash.\n" + "It is highly recommended you unload your game first to avoid data loss.\n" + "Do you wish to proceed with the input driver change now anyway?" + ).setParent(*settingsWindow).question() == "Yes") { + settings["Crashed"].setValue(true); + settings.save(); + program->initializeInputDriver(); + if(!input->ready()) { + MessageDialog({ + "Error: failed to initialize ", item.text(), " input driver." + }).setParent(*settingsWindow).error(); + settings["Input/Driver"].setValue("None"); + program->initializeInputDriver(); + for(auto item : inputDriverOption.items()) { + if(input->driver() == item.text()) item.setSelected(); + } + } + settings["Crashed"].setValue(false); + settings.save(); + inputDriverChange.setEnabled(false); + } + }); +} diff --git a/higan/target-bsnes/settings/settings.cpp b/higan/target-bsnes/settings/settings.cpp index 477c655d..a50cd08b 100644 --- a/higan/target-bsnes/settings/settings.cpp +++ b/higan/target-bsnes/settings/settings.cpp @@ -2,6 +2,7 @@ #include "input.cpp" #include "hotkeys.cpp" #include "paths.cpp" +#include "advanced.cpp" Settings settings; unique_pointer settingsWindow; diff --git a/higan/target-bsnes/settings/settings.hpp b/higan/target-bsnes/settings/settings.hpp index 3ccd4217..c20b7544 100644 --- a/higan/target-bsnes/settings/settings.hpp +++ b/higan/target-bsnes/settings/settings.hpp @@ -94,6 +94,25 @@ public: Button cheatsReset{&cheatsLayout, Size{80, 0}}; }; +struct AdvancedSettings : TabFrameItem { + AdvancedSettings(TabFrame*); + +public: + VerticalLayout layout{this}; + HorizontalLayout videoDriverLayout{&layout, Size{~0, 0}}; + Label videoDriverLabel{&videoDriverLayout, Size{75, 0}}; + ComboButton videoDriverOption{&videoDriverLayout, Size{~0, 0}}; + Button videoDriverChange{&videoDriverLayout, Size{80, 0}}; + HorizontalLayout audioDriverLayout{&layout, Size{~0, 0}}; + Label audioDriverLabel{&audioDriverLayout, Size{75, 0}}; + ComboButton audioDriverOption{&audioDriverLayout, Size{~0, 0}}; + Button audioDriverChange{&audioDriverLayout, Size{80, 0}}; + HorizontalLayout inputDriverLayout{&layout, Size{~0, 0}}; + Label inputDriverLabel{&inputDriverLayout, Size{75, 0}}; + ComboButton inputDriverOption{&inputDriverLayout, Size{~0, 0}}; + Button inputDriverChange{&inputDriverLayout, Size{80, 0}}; +}; + struct SettingsWindow : Window { SettingsWindow(); auto setVisible(bool visible = true) -> SettingsWindow&; @@ -105,6 +124,7 @@ public: InputSettings input{&panel}; HotkeySettings hotkeys{&panel}; PathSettings paths{&panel}; + AdvancedSettings advanced{&panel}; StatusBar statusBar{this}; }; diff --git a/higan/target-higan/higan.cpp b/higan/target-higan/higan.cpp index f0e82b51..3abd1384 100644 --- a/higan/target-higan/higan.cpp +++ b/higan/target-higan/higan.cpp @@ -8,11 +8,8 @@ auto locate(string name) -> string { string location = {Path::program(), name}; if(inode::exists(location)) return location; - location = {Path::config(), "higan/", name}; - if(inode::exists(location)) return location; - - directory::create({Path::local(), "higan/"}); - return {Path::local(), "higan/", name}; + directory::create({Path::userData(), "higan/"}); + return {Path::userData(), "higan/", name}; } #include diff --git a/higan/target-higan/presentation/presentation.cpp b/higan/target-higan/presentation/presentation.cpp index 5a1437d7..eae196e9 100644 --- a/higan/target-higan/presentation/presentation.cpp +++ b/higan/target-higan/presentation/presentation.cpp @@ -143,10 +143,10 @@ Presentation::Presentation() { setCentered(); #if defined(PLATFORM_WINDOWS) - Application::Windows::onModalChange([](bool modal) { + Application::Windows::onModalChange([&](bool modal) { if(modal && audio) audio->clear(); }); - Application::Windows::onScreenSaver([]() -> bool { + Application::Windows::onScreenSaver([&]() -> bool { if(emulator && emulator->loaded()) { if(program->pause) return true; if(!program->focused() && settingsManager->input.pauseEmulation.checked()) return true; @@ -205,24 +205,27 @@ auto Presentation::updateEmulator() -> void { emulator->set("Scanline Emulation", scanlineEmulation.checked()); } -auto Presentation::showIcon() -> void { - Application::processEvents(); - int width = geometry().width(); - int height = geometry().height(); - int x = width - 128; - int y = height - 128; - if(x >= 0 && y >= 0) { - canvas.setVisible(true).setGeometry({x, y, 112, 112}); - } else { - canvas.setVisible(false); +auto Presentation::drawIcon(uint32_t* output, uint length, uint width, uint height) -> void { + int ox = width - 128; + int oy = height - 128; + if(ox >= 0 && oy >= 0) { + image icon{Resource::Icon}; + icon.alphaBlend(0xff000000); + for(uint y : range(112)) { + auto target = output + (y + oy) * (length >> 2) + ox; + auto source = (uint32_t*)icon.data() + y * 112; + memory::copy(target, source, 112 * sizeof(uint32_t)); + } } - viewport.setGeometry({0, 0, 1, 1}); } auto Presentation::clearViewport() -> void { - if(!emulator || !emulator->loaded()) showIcon(); if(!video) return; + if(!emulator || !emulator->loaded()) { + viewport.setGeometry({0, 0, geometry().width(), geometry().height()}); + } + uint32_t* output; uint length = 0; uint width = viewport.geometry().width(); @@ -232,15 +235,13 @@ auto Presentation::clearViewport() -> void { auto line = output + y * (length >> 2); for(uint x : range(width)) *line++ = 0xff000000; } - + if(!emulator || !emulator->loaded()) drawIcon(output, length, width, height); video->unlock(); video->output(); } } auto Presentation::resizeViewport(bool resizeWindow) -> void { - if(emulator && emulator->loaded()) canvas.setVisible(false); - //clear video area before resizing to avoid seeing distorted video momentarily clearViewport(); @@ -306,10 +307,14 @@ auto Presentation::resizeViewport(bool resizeWindow) -> void { } } - viewport.setGeometry({ - (viewportWidth - emulatorWidth) / 2, (viewportHeight - emulatorHeight) / 2, - emulatorWidth, emulatorHeight - }); + if(emulator && emulator->loaded()) { + viewport.setGeometry({ + (viewportWidth - emulatorWidth) / 2, (viewportHeight - emulatorHeight) / 2, + emulatorWidth, emulatorHeight + }); + } else { + viewport.setGeometry({0, 0, geometry().width(), geometry().height()}); + } //clear video area again to ensure entire viewport area has been painted in clearViewport(); @@ -331,6 +336,9 @@ auto Presentation::toggleFullScreen() -> void { menuBar.setVisible(true); statusBar.setVisible(settings["UserInterface/ShowStatusBar"].boolean()); } + //hack: give window geometry time to update after toggling fullscreen and menu/status bars + usleep(20 * 1000); + Application::processEvents(); resizeViewport(); } diff --git a/higan/target-higan/presentation/presentation.hpp b/higan/target-higan/presentation/presentation.hpp index e86de552..3ec8e710 100644 --- a/higan/target-higan/presentation/presentation.hpp +++ b/higan/target-higan/presentation/presentation.hpp @@ -11,7 +11,7 @@ struct AboutWindow : Window { struct Presentation : Window { Presentation(); auto updateEmulator() -> void; - auto showIcon() -> void; + auto drawIcon(uint32_t* output, uint length, uint width, uint height) -> void; auto clearViewport() -> void; auto resizeViewport(bool resizeWindow = true) -> void; auto toggleFullScreen() -> void; diff --git a/hiro/gtk/settings.cpp b/hiro/gtk/settings.cpp index 397ef32c..400a2f17 100644 --- a/hiro/gtk/settings.cpp +++ b/hiro/gtk/settings.cpp @@ -1,7 +1,7 @@ namespace hiro { Settings::Settings() { - string path = {Path::local(), "hiro/"}; + string path = {Path::userData(), "hiro/"}; auto document = BML::unserialize(file::read({path, "gtk.bml"})); auto get = [&](string_view name) { @@ -17,7 +17,7 @@ Settings::Settings() { } Settings::~Settings() { - string path = {Path::local(), "hiro/"}; + string path = {Path::userData(), "hiro/"}; directory::create(path, 0755); Markup::Node document; diff --git a/hiro/qt/settings.cpp b/hiro/qt/settings.cpp index 3393a065..7c16a2c5 100644 --- a/hiro/qt/settings.cpp +++ b/hiro/qt/settings.cpp @@ -1,7 +1,7 @@ namespace hiro { Settings::Settings() { - string path = {Path::local(), "hiro/"}; + string path = {Path::userData(), "hiro/"}; auto document = BML::unserialize(file::read({path, "qt.bml"})); auto get = [&](string_view name) { @@ -17,7 +17,7 @@ Settings::Settings() { } Settings::~Settings() { - string path = {Path::local(), "hiro/"}; + string path = {Path::userData(), "hiro/"}; directory::create(path, 0755); Markup::Node document; diff --git a/hiro/windows/status-bar.cpp b/hiro/windows/status-bar.cpp index 68ef63c4..9d03ceaf 100644 --- a/hiro/windows/status-bar.cpp +++ b/hiro/windows/status-bar.cpp @@ -4,7 +4,7 @@ namespace hiro { auto pStatusBar::construct() -> void { if(auto parent = _parent()) { - hwnd = CreateWindow(STATUSCLASSNAME, L"", WS_CHILD | WS_DISABLED, 0, 0, 0, 0, parent->hwnd, nullptr, GetModuleHandle(0), 0); + hwnd = CreateWindow(STATUSCLASSNAME, L"", WS_CHILD, 0, 0, 0, 0, parent->hwnd, nullptr, GetModuleHandle(0), 0); SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&reference); setEnabled(self().enabled(true)); setFont(self().font(true)); diff --git a/icarus/heuristics/super-famicom.cpp b/icarus/heuristics/super-famicom.cpp index 7f73616b..dc0bfa44 100644 --- a/icarus/heuristics/super-famicom.cpp +++ b/icarus/heuristics/super-famicom.cpp @@ -86,11 +86,6 @@ auto SuperFamicom::manifest() const -> string { output.append(Memory{}.type("RAM").size(size).content("Save").text()); } - if(board(0) == "GSU" && !ramSize() && !expansionRamSize()) { - //Starfox / Starwing reports having no RAM; but all GSU boards contain expansion RAM - output.append(Memory{}.type("RAM").size(0x8000).content("Save").isVolatile().text()); - } - if(0) { } else if(board(0) == "ARM") { output.append(Memory{}.type("ROM").size(0x20000).content("Program").manufacturer("SETA").architecture("ARM6").identifier(firmwareARM()).text()); @@ -273,10 +268,7 @@ auto SuperFamicom::board() const -> string { } if(!board) board.append(mode); - if(cartridgeTypeLo == 0x1 || cartridgeTypeLo == 0x4) board.append("RAM-"); //RAM without battery - if(cartridgeTypeLo == 0x2 || cartridgeTypeLo == 0x5) board.append("RAM-"); //RAM with battery - if(cartridgeTypeLo == 0x6); //battery without RAM - + if(ramSize() || expansionRamSize()) board.append("RAM-"); if(epsonRTC) board.append("EPSONRTC-"); if(sharpRTC) board.append("SHARPRTC-"); @@ -439,9 +431,14 @@ auto SuperFamicom::ramSize() const -> uint { } auto SuperFamicom::expansionRamSize() const -> uint { - if(data[headerAddress + 0x2a] != 0x33) return 0; - auto ramSize = data[headerAddress + 0x0d] & 7; - if(ramSize) return 1024 << ramSize; + if(data[headerAddress + 0x2a] == 0x33) { + auto ramSize = data[headerAddress + 0x0d] & 7; + if(ramSize) return 1024 << ramSize; + } + if((data[headerAddress + 0x26] >> 4) == 1) { + //GSU: Starfox / Starwing lacks an extended header; but still has expansion RAM + return 0x8000; + } return 0; } diff --git a/icarus/icarus.cpp b/icarus/icarus.cpp index 28c68860..0394f4d4 100644 --- a/icarus/icarus.cpp +++ b/icarus/icarus.cpp @@ -8,8 +8,8 @@ auto locate(string name) -> string { string location = {Path::program(), name}; if(inode::exists(location)) return location; - directory::create({Path::local(), "icarus/"}); - return {Path::local(), "icarus/", name}; + directory::create({Path::userData(), "icarus/"}); + return {Path::userData(), "icarus/", name}; } #include "settings.cpp" diff --git a/nall/decode/html.hpp b/nall/decode/html.hpp new file mode 100644 index 00000000..927d699b --- /dev/null +++ b/nall/decode/html.hpp @@ -0,0 +1,40 @@ +#pragma once + +namespace nall { namespace Decode { + +inline auto HTML(const string& input) -> string { + string output; + for(uint n = 0; n < input.size();) { + if(input[n] == '&') { + if(input(n + 1) == 'a' && input(n + 2) == 'm' && input(n + 3) == 'p' && input(n + 4) == ';') { + output.append('&'); + n += 5; + continue; + } + if(input(n + 1) == 'l' && input(n + 2) == 't' && input(n + 3) == ';') { + output.append('<'); + n += 4; + continue; + } + if(input(n + 1) == 'g' && input(n + 2) == 't' && input(n + 3) == ';') { + output.append('>'); + n += 4; + continue; + } + if(input(n + 1) == 'q' && input(n + 2) == 'u' && input(n + 3) == 'o' && input(n + 4) == 't' && input(n + 5) == ';') { + output.append('"'); + n += 6; + continue; + } + if(input(n + 1) == 'a' && input(n + 2) == 'p' && input(n + 3) == 'o' && input(n + 4) == 's' && input(n + 5) == ';') { + output.append('\''); + n += 6; + continue; + } + } + output.append(input[n++]); + } + return output; +} + +}} diff --git a/nall/encode/html.hpp b/nall/encode/html.hpp new file mode 100644 index 00000000..fa854474 --- /dev/null +++ b/nall/encode/html.hpp @@ -0,0 +1,18 @@ +#pragma once + +namespace nall { namespace Encode { + +inline auto HTML(const string& input) -> string { + string output; + for(char c : input) { + if(c == '&' ) { output.append("&" ); continue; } + if(c == '<' ) { output.append("<" ); continue; } + if(c == '>' ) { output.append(">" ); continue; } + if(c == '"' ) { output.append("""); continue; } + if(c == '\'') { output.append("'"); continue; } + output.append(c); + } + return output; +} + +}} diff --git a/nall/nall.hpp b/nall/nall.hpp index bebf0ea7..900863a8 100644 --- a/nall/nall.hpp +++ b/nall/nall.hpp @@ -62,12 +62,14 @@ #include #include #include +#include #include #include #include #include #include #include +#include #include #include #include diff --git a/nall/path.hpp b/nall/path.hpp index 5456aea1..ed4d148c 100644 --- a/nall/path.hpp +++ b/nall/path.hpp @@ -69,9 +69,10 @@ inline auto user() -> string { return result; } -// /home/username/.config/ +// /home/username/.local/share/ +// ~/Library/Application Support/ // c:/users/username/appdata/roaming/ -inline auto config() -> string { +inline auto userData() -> string { #if defined(PLATFORM_WINDOWS) wchar_t path[PATH_MAX] = L""; SHGetFolderPathW(nullptr, CSIDL_APPDATA | CSIDL_FLAG_CREATE, nullptr, 0, path); @@ -80,24 +81,6 @@ inline auto config() -> string { #elif defined(PLATFORM_MACOS) string result = {Path::user(), "Library/Application Support/"}; #else - string result = {Path::user(), ".config/"}; - #endif - if(!result) result = "."; - if(!result.endsWith("/")) result.append("/"); - return result; -} - -// /home/username/.local/share/ -// c:/users/username/appdata/local/ -inline auto local() -> string { - #if defined(PLATFORM_WINDOWS) - wchar_t path[PATH_MAX] = L""; - SHGetFolderPathW(nullptr, CSIDL_LOCAL_APPDATA | CSIDL_FLAG_CREATE, nullptr, 0, path); - string result = (const char*)utf8_t(path); - result.transform("\\", "/"); - #elif defined(PLATFORM_MACOS) - string result = {Path::user(), "Library/Application Support/"}; - #else string result = {Path::user(), ".local/share/"}; #endif if(!result) result = "."; @@ -108,7 +91,7 @@ inline auto local() -> string { // /usr/share // /Library/Application Support/ // c:/ProgramData/ -inline auto shared() -> string { +inline auto sharedData() -> string { #if defined(PLATFORM_WINDOWS) wchar_t path[PATH_MAX] = L""; SHGetFolderPathW(nullptr, CSIDL_COMMON_APPDATA | CSIDL_FLAG_CREATE, nullptr, 0, path); @@ -126,7 +109,7 @@ inline auto shared() -> string { // /tmp // c:/users/username/AppData/Local/Temp/ -inline auto temp() -> string { +inline auto temporary() -> string { #if defined(PLATFORM_WINDOWS) wchar_t path[PATH_MAX] = L""; GetTempPathW(PATH_MAX, path); diff --git a/nall/string.hpp b/nall/string.hpp index e26785a6..6c15e7c4 100644 --- a/nall/string.hpp +++ b/nall/string.hpp @@ -175,7 +175,7 @@ public: //core.hpp inline auto operator[](uint) const -> const char&; - inline auto operator()(uint, char) const -> char; + inline auto operator()(uint, char = 0) const -> char; template inline auto assign(P&&...) -> type&; template inline auto prepend(const T&, P&&...) -> type&; template inline auto prepend(const nall::string_format&, P&&...) -> type&; diff --git a/ruby/input/xlib.cpp b/ruby/input/xlib.cpp index ec6dfbe3..dc9bef1d 100644 --- a/ruby/input/xlib.cpp +++ b/ruby/input/xlib.cpp @@ -11,6 +11,16 @@ struct InputXlib : Input { InputXlib() : _keyboard(*this), _mouse(*this) { initialize(); } ~InputXlib() { terminate(); } + auto ready() -> bool { return _ready; } + + auto context() -> uintptr { return _context; } + + auto setContext(uintptr context) -> bool { + if(_context == context) return true; + _context = context; + return initialize(); + } + auto acquired() -> bool { return _mouse.acquired(); } @@ -37,6 +47,7 @@ struct InputXlib : Input { private: auto initialize() -> bool { terminate(); + if(!_context) return false; if(!_keyboard.initialize()) return false; if(!_mouse.initialize(_context)) return false; return _ready = true; diff --git a/ruby/ruby.cpp b/ruby/ruby.cpp index df10cfdb..670856a1 100644 --- a/ruby/ruby.cpp +++ b/ruby/ruby.cpp @@ -68,46 +68,49 @@ using namespace ruby; namespace ruby { -auto Video::create(const string& driver) -> Video* { - if(!driver) return create(optimalDriver()); +auto Video::create(string driver) -> Video* { + Video* video = nullptr; + if(!driver) driver = optimalDriver(); #if defined(VIDEO_CGL) - if(driver == "OpenGL") return new VideoCGL; + if(driver == "OpenGL") video = new VideoCGL; #endif #if defined(VIDEO_DIRECT3D) - if(driver == "Direct3D") return new VideoDirect3D; + if(driver == "Direct3D") video = new VideoDirect3D; #endif #if defined(VIDEO_DIRECTDRAW) - if(driver == "DirectDraw") return new VideoDirectDraw; + if(driver == "DirectDraw") video = new VideoDirectDraw; #endif #if defined(VIDEO_GDI) - if(driver == "GDI") return new VideoGDI; + if(driver == "GDI") video = new VideoGDI; #endif #if defined(VIDEO_GLX) - if(driver == "OpenGL") return new VideoGLX; + if(driver == "OpenGL") video = new VideoGLX; #endif #if defined(VIDEO_GLX2) - if(driver == "OpenGL2") return new VideoGLX2; + if(driver == "OpenGL2") video = new VideoGLX2; #endif #if defined(VIDEO_WGL) - if(driver == "OpenGL") return new VideoWGL; + if(driver == "OpenGL") video = new VideoWGL; #endif #if defined(VIDEO_XSHM) - if(driver == "XShm") return new VideoXShm; + if(driver == "XShm") video = new VideoXShm; #endif #if defined(VIDEO_XVIDEO) - if(driver == "XVideo") return new VideoXVideo; + if(driver == "XVideo") video = new VideoXVideo; #endif - return new Video; + if(!video) driver = "None", video = new Video; + video->_driver = driver; + return video; } auto Video::optimalDriver() -> string { @@ -246,50 +249,53 @@ auto Video::availableDrivers() -> string_vector { namespace ruby { -auto Audio::create(const string& driver) -> Audio* { - if(!driver) return create(optimalDriver()); +auto Audio::create(string driver) -> Audio* { + Audio* audio = nullptr; + if(!driver) driver = optimalDriver(); #if defined(AUDIO_ALSA) - if(driver == "ALSA") return new AudioALSA; + if(driver == "ALSA") audio = new AudioALSA; #endif #if defined(AUDIO_AO) - if(driver == "libao") return new AudioAO; + if(driver == "libao") audio = new AudioAO; #endif #if defined(AUDIO_ASIO) - if(driver == "ASIO") return new AudioASIO; + if(driver == "ASIO") audio = new AudioASIO; #endif #if defined(AUDIO_DIRECTSOUND) - if(driver == "DirectSound") return new AudioDirectSound; + if(driver == "DirectSound") audio = new AudioDirectSound; #endif #if defined(AUDIO_OPENAL) - if(driver == "OpenAL") return new AudioOpenAL; + if(driver == "OpenAL") audio = new AudioOpenAL; #endif #if defined(AUDIO_OSS) - if(driver == "OSS") return new AudioOSS; + if(driver == "OSS") audio = new AudioOSS; #endif #if defined(AUDIO_PULSEAUDIO) - if(driver == "PulseAudio") return new AudioPulseAudio; + if(driver == "PulseAudio") audio = new AudioPulseAudio; #endif #if defined(AUDIO_PULSEAUDIOSIMPLE) - if(driver == "PulseAudioSimple") return new AudioPulseAudioSimple; + if(driver == "PulseAudioSimple") audio = new AudioPulseAudioSimple; #endif #if defined(AUDIO_WASAPI) - if(driver == "WASAPI") return new AudioWASAPI; + if(driver == "WASAPI") audio = new AudioWASAPI; #endif #if defined(AUDIO_XAUDIO2) - if(driver == "XAudio2") return new AudioXAudio2; + if(driver == "XAudio2") audio = new AudioXAudio2; #endif - return new Audio; + if(!audio) driver = "None", audio = new Audio; + audio->_driver = driver; + return audio; } auto Audio::optimalDriver() -> string { @@ -420,34 +426,37 @@ auto Audio::availableDrivers() -> string_vector { namespace ruby { -auto Input::create(const string& driver) -> Input* { - if(!driver) return create(optimalDriver()); +auto Input::create(string driver) -> Input* { + Input* input = nullptr; + if(!driver) driver = optimalDriver(); #if defined(INPUT_WINDOWS) - if(driver == "Windows") return new InputWindows; + if(driver == "Windows") input = new InputWindows; #endif #if defined(INPUT_QUARTZ) - if(driver == "Quartz") return new InputQuartz; + if(driver == "Quartz") input = new InputQuartz; #endif #if defined(INPUT_CARBON) - if(driver == "Carbon") return new InputCarbon; + if(driver == "Carbon") input = new InputCarbon; #endif #if defined(INPUT_UDEV) - if(driver == "udev") return new InputUdev; + if(driver == "udev") input = new InputUdev; #endif #if defined(INPUT_SDL) - if(driver == "SDL") return new InputSDL; + if(driver == "SDL") input = new InputSDL; #endif #if defined(INPUT_XLIB) - if(driver == "Xlib") return new InputXlib; + if(driver == "Xlib") input = new InputXlib; #endif - return new Input; + if(!input) driver = "None", input = new Input; + input->_driver = driver; + return input; } auto Input::optimalDriver() -> string { diff --git a/ruby/ruby.hpp b/ruby/ruby.hpp index a22e1e00..f4c1af06 100644 --- a/ruby/ruby.hpp +++ b/ruby/ruby.hpp @@ -5,18 +5,15 @@ namespace ruby { struct Video { - static auto create(const nall::string& driver = "") -> Video*; + static auto create(nall::string driver = "") -> Video*; static auto optimalDriver() -> nall::string; static auto safestDriver() -> nall::string; static auto availableDrivers() -> nall::string_vector; - struct Information { - }; - virtual ~Video() = default; virtual auto ready() -> bool { return true; } - virtual auto information() -> Information { return {}; } + virtual auto driver() -> nall::string { return _driver; } virtual auto exclusive() -> bool { return false; } virtual auto context() -> uintptr { return 0; } @@ -36,10 +33,13 @@ struct Video { virtual auto lock(uint32_t*& data, uint& pitch, uint width, uint height) -> bool { return false; } virtual auto unlock() -> void {} virtual auto output() -> void {} + +private: + nall::string _driver; }; struct Audio { - static auto create(const nall::string& driver = "") -> Audio*; + static auto create(nall::string driver = "") -> Audio*; static auto optimalDriver() -> nall::string; static auto safestDriver() -> nall::string; static auto availableDrivers() -> nall::string_vector; @@ -52,6 +52,8 @@ struct Audio { virtual auto availableChannels() -> nall::vector { return {2}; } virtual auto ready() -> bool { return true; } + virtual auto driver() -> nall::string { return _driver; } + virtual auto exclusive() -> bool { return false; } virtual auto context() -> uintptr { return 0; } virtual auto device() -> nall::string { return "None"; } @@ -70,21 +72,21 @@ struct Audio { virtual auto clear() -> void {} virtual auto output(const double samples[]) -> void {} + +private: + nall::string _driver; }; struct Input { - static auto create(const nall::string& driver = "") -> Input*; + static auto create(nall::string driver = "") -> Input*; static auto optimalDriver() -> nall::string; static auto safestDriver() -> nall::string; static auto availableDrivers() -> nall::string_vector; - struct Information { - }; - virtual ~Input() = default; virtual auto ready() -> bool { return true; } - virtual auto information() -> Information { return {}; } + virtual auto driver() -> nall::string { return _driver; } virtual auto context() -> uintptr { return 0; } @@ -102,6 +104,7 @@ struct Input { } private: + nall::string _driver; nall::function device, uint group, uint input, int16_t oldValue, int16_t newValue)> _onChange; }; diff --git a/ruby/video/direct3d.cpp b/ruby/video/direct3d.cpp index f5685231..93421585 100644 --- a/ruby/video/direct3d.cpp +++ b/ruby/video/direct3d.cpp @@ -66,6 +66,12 @@ struct VideoDirect3D : Video { if(!ready()) return false; if(_lost && !recover()) return false; + //if output size changed, driver must be re-initialized. + //failure to do so causes scaling issues on some video drivers. + RECT rectangle; + GetClientRect((HWND)_context, &rectangle); + if(_windowWidth != rectangle.right || _windowHeight != rectangle.bottom) initialize(); + if(width != _inputWidth || height != _inputHeight) { resize(_inputWidth = width, _inputHeight = height); } @@ -91,13 +97,6 @@ struct VideoDirect3D : Video { if(!ready()) return; if(_lost && !recover()) return; - RECT rectangle; - GetClientRect((HWND)_context, &rectangle); - - //if output size changed, driver must be re-initialized. - //failure to do so causes scaling issues on some video drivers. - if(_windowWidth != rectangle.right || _windowHeight != rectangle.bottom) initialize(); - _device->BeginScene(); uint x = 0, y = 0; if(_exclusive) {