From f5e5bf17721c00ce7015811d80ac6c61524d1ac1 Mon Sep 17 00:00:00 2001 From: Tim Allen Date: Wed, 3 Aug 2016 22:32:40 +1000 Subject: [PATCH] Update to v100r16 release. byuu says: (Windows users may need to include at the top of nall/chrono.hpp, not sure.) Unchangelog: - forgot to add the Scheduler clock=0 fix because I have the memory of a goldfish Changelog: - new icarus database with nine additional games - hiro(GTK,Qt) won't constantly write its settings.bml file to disk anymore - added latency simulator for fun (settings.bml => Input/Latency in milliseconds) So the last one ... I wanted to test out nall::chrono, and I was also thinking that by polling every emulated frame, it's pretty wasteful when you are using Fast Forward and hitting 200+fps. As I've said before, calls to ruby::input::poll are not cheap. So to get around this, I added a limiter so that if you called the hardware poll function within N milliseconds, it'll return without doing any actual work. And indeed, that increases my framerate of Zelda 3 uncapped from 133fps to 142fps. Yay. But it's not a "real" speedup, as it only helps you when you exceed 100% speed (theoretically, you'd need to crack 300% speed since the game itself will poll at 16ms at 100% speed, but yet it sped up Zelda 3, so who am I to complain?) I threw the latency value into the settings file. It should be 16, but I set it to 5 since that was the lowest before it started negatively impacting uncapped speeds. You're wasting your time and CPU cycles setting it lower than 5, but if people like placebo effects it might work. Maybe I should let it be a signed integer so people can set it to -16 and think it's actually faster :P (I'm only joking. I took out the 96000hz audio placebo effect as well. Not really into psychological tricks anymore.) But yeah seriously, I didn't do this to start this discussion again for the billionth time. Please don't go there. And please don't tell me this WIP has higher/lower latency than before. I don't want to hear it. The only reason I bring it up is for the fun part that is worth discussing: put up or shut up time on how sensitive you are to latency! You can set the value above 5 to see how games feel. I personally can't really tell a difference until about 50. And I can't be 100% confident it's worse until about 75. But ... when I set it to 150, games become "extra difficult" ... the higher it goes, the worse it gets :D For this WIP, I've left no upper limit cap. I'll probably set a cap of something like 500ms or 1000ms for the official release. Need to balance user error/trolling with enjoyability. I'll think about it. [...] Now, what I worry about is stupid people seeing it and thinking it's an "added latency" setting, as if anyone would intentionally make things worse by default. This is a limiter. So if 5ms have passed since the game last polled, and that will be the case 99.9% of the time in games, the next poll will happen just in time, immediately when the game polls the inputs. Thus, a value below 1/ms is not only pointless, if you go too low it will ruin your fast forward max speeds. I did say I didn't want to resort to placebo tricks, but I also don't want to spark up public discussion on this again either. So it might be best to default Input/Latency to 0ms, and internally have a max(5, latency) wrapper around the value. --- higan/emulator/emulator.hpp | 2 +- .../configuration/configuration.cpp | 1 + higan/target-tomoko/input/hotkeys.cpp | 2 + higan/target-tomoko/input/input.cpp | 8 +- higan/target-tomoko/input/input.hpp | 2 + higan/target-tomoko/presentation/about.cpp | 2 +- higan/target-tomoko/program/interface.cpp | 5 +- higan/target-tomoko/program/program.cpp | 1 + higan/target-tomoko/program/state.cpp | 2 +- hiro/core/core.hpp | 1 - hiro/gtk/application.cpp | 3 - hiro/gtk/keyboard.cpp | 4 +- hiro/gtk/settings.cpp | 46 +++--- hiro/gtk/settings.hpp | 29 ++-- hiro/gtk/window.cpp | 25 ++-- hiro/qt/application.cpp | 3 - hiro/qt/keyboard.cpp | 4 +- hiro/qt/settings.cpp | 46 +++--- hiro/qt/settings.hpp | 25 ++-- hiro/qt/window.cpp | 30 ++-- icarus/Database/Super Famicom.bml | 136 +++++++++++++++++- nall/chrono.hpp | 134 +++++++++++++++++ nall/config.hpp | 110 -------------- nall/http/response.hpp | 2 +- nall/inode.hpp | 2 +- nall/nall.hpp | 2 +- nall/string.hpp | 8 +- nall/string/datetime.hpp | 30 ---- nall/string/format.hpp | 39 +---- 29 files changed, 404 insertions(+), 300 deletions(-) create mode 100644 nall/chrono.hpp delete mode 100644 nall/config.hpp delete mode 100644 nall/string/datetime.hpp diff --git a/higan/emulator/emulator.hpp b/higan/emulator/emulator.hpp index 828c9446..b54855d8 100644 --- a/higan/emulator/emulator.hpp +++ b/higan/emulator/emulator.hpp @@ -11,7 +11,7 @@ using namespace nall; namespace Emulator { static const string Name = "higan"; - static const string Version = "100.15"; + static const string Version = "100.16"; static const string Author = "byuu"; static const string License = "GPLv3"; static const string Website = "http://byuu.org/"; diff --git a/higan/target-tomoko/configuration/configuration.cpp b/higan/target-tomoko/configuration/configuration.cpp index 4e8c5544..95b6f18c 100644 --- a/higan/target-tomoko/configuration/configuration.cpp +++ b/higan/target-tomoko/configuration/configuration.cpp @@ -43,6 +43,7 @@ Settings::Settings() { set("Audio/Resampler", "Sinc"); set("Input/Driver", ruby::Input::optimalDriver()); + set("Input/Latency", 5); set("Input/FocusLoss/Pause", false); set("Input/FocusLoss/AllowInput", false); } diff --git a/higan/target-tomoko/input/hotkeys.cpp b/higan/target-tomoko/input/hotkeys.cpp index 1c660bf6..c6c76e3f 100644 --- a/higan/target-tomoko/input/hotkeys.cpp +++ b/higan/target-tomoko/input/hotkeys.cpp @@ -76,6 +76,8 @@ auto InputManager::appendHotkeys() -> void { } auto InputManager::pollHotkeys() -> void { + if(!presentation || !presentation->focused()) return; + for(auto& hotkey : hotkeys) { int16 state = hotkey->poll(); if(hotkey->state == 0 && state == 1 && hotkey->press) hotkey->press(); diff --git a/higan/target-tomoko/input/input.cpp b/higan/target-tomoko/input/input.cpp index 3a0d7412..b0caee68 100644 --- a/higan/target-tomoko/input/input.cpp +++ b/higan/target-tomoko/input/input.cpp @@ -144,6 +144,7 @@ auto InputMapping::deviceName() -> string { InputManager::InputManager() { inputManager = this; + latency = max(1u, settings["Input/Latency"].natural()); for(auto& emulator : program->emulators) { auto& inputEmulator = emulators(emulators.size()); @@ -201,6 +202,11 @@ auto InputManager::bind() -> void { } auto InputManager::poll() -> void { + //polling actual hardware is very time-consuming: skip call if poll was called too recently + auto thisPoll = chrono::millisecond(); + if(thisPoll - lastPoll < latency) return; + lastPoll = thisPoll; + auto devices = input->poll(); bool changed = devices.size() != this->devices.size(); if(!changed) { @@ -213,8 +219,6 @@ auto InputManager::poll() -> void { this->devices = devices; bind(); } - - if(presentation && presentation->focused()) pollHotkeys(); } auto InputManager::onChange(shared_pointer device, uint group, uint input, int16_t oldValue, int16_t newValue) -> void { diff --git a/higan/target-tomoko/input/input.hpp b/higan/target-tomoko/input/input.hpp index e65f3e7c..f27812fa 100644 --- a/higan/target-tomoko/input/input.hpp +++ b/higan/target-tomoko/input/input.hpp @@ -64,6 +64,8 @@ struct InputManager { vector hotkeys; InputEmulator* emulator = nullptr; //points to InputEmulator that represents the currently active emulator + uint64 lastPoll; //time in milliseconds since last call to poll() + uint64 latency; //minimum time in milliseconds before poll() can be called again }; extern unique_pointer inputManager; diff --git a/higan/target-tomoko/presentation/about.cpp b/higan/target-tomoko/presentation/about.cpp index c113a55d..782fc834 100644 --- a/higan/target-tomoko/presentation/about.cpp +++ b/higan/target-tomoko/presentation/about.cpp @@ -14,7 +14,7 @@ AboutWindow::AboutWindow() { "Website:" }); informationRight.setFont(Font().setBold()).setAlignment(0.0).setText({ - string{Emulator::Version}.replace("v", ""), "\n", + Emulator::Version, "\n", Emulator::Author, "\n", Emulator::License, "\n", Emulator::Website diff --git a/higan/target-tomoko/program/interface.cpp b/higan/target-tomoko/program/interface.cpp index 2324e3ea..415dac60 100644 --- a/higan/target-tomoko/program/interface.cpp +++ b/higan/target-tomoko/program/interface.cpp @@ -72,10 +72,10 @@ auto Program::videoRefresh(const uint32* data, uint pitch, uint width, uint heig } static uint frameCounter = 0; - static time_t previous, current; + static uint64 previous, current; frameCounter++; - time(¤t); + current = chrono::timestamp(); if(current != previous) { previous = current; statusText = {"FPS: ", frameCounter}; @@ -91,6 +91,7 @@ auto Program::audioSample(const double* samples, uint channels) -> void { auto Program::inputPoll(uint port, uint device, uint input) -> int16 { if(presentation->focused() || settings["Input/FocusLoss/AllowInput"].boolean()) { + inputManager->poll(); auto& mapping = inputManager->emulator->ports[port].devices[device].mappings[input]; return mapping.poll(); } diff --git a/higan/target-tomoko/program/program.cpp b/higan/target-tomoko/program/program.cpp index 8f0088b8..b8931cec 100644 --- a/higan/target-tomoko/program/program.cpp +++ b/higan/target-tomoko/program/program.cpp @@ -75,6 +75,7 @@ Program::Program(string_vector args) { auto Program::main() -> void { updateStatusText(); inputManager->poll(); + inputManager->pollHotkeys(); if(!emulator || !emulator->loaded() || pause || (!presentation->focused() && settings["Input/FocusLoss/Pause"].boolean())) { audio->clear(); diff --git a/higan/target-tomoko/program/state.cpp b/higan/target-tomoko/program/state.cpp index 68b37e07..489501b5 100644 --- a/higan/target-tomoko/program/state.cpp +++ b/higan/target-tomoko/program/state.cpp @@ -2,7 +2,7 @@ auto Program::stateName(uint slot, bool manager) -> string { return { mediumPaths(1), "higan/states/", manager ? "managed/" : "quick/", - "slot-", numeral(slot, 2L), ".bst" + "slot-", slot, ".bst" }; } diff --git a/hiro/core/core.hpp b/hiro/core/core.hpp index f385df9d..7c235d27 100644 --- a/hiro/core/core.hpp +++ b/hiro/core/core.hpp @@ -1,5 +1,4 @@ #include -#include #include #include #include diff --git a/hiro/gtk/application.cpp b/hiro/gtk/application.cpp index 9ca4c22a..7c09ad96 100644 --- a/hiro/gtk/application.cpp +++ b/hiro/gtk/application.cpp @@ -42,9 +42,6 @@ auto pApplication::initialize() -> void { display = XOpenDisplay(nullptr); #endif - settings = new Settings; - settings->load(); - //set WM_CLASS to Application::name() if(Application::state.name) gdk_set_program_class(Application::state.name); diff --git a/hiro/gtk/keyboard.cpp b/hiro/gtk/keyboard.cpp index 90315abf..7a579d43 100644 --- a/hiro/gtk/keyboard.cpp +++ b/hiro/gtk/keyboard.cpp @@ -8,7 +8,7 @@ auto pKeyboard::poll() -> vector { #if defined(DISPLAY_XORG) XQueryKeymap(pApplication::display, state); #endif - for(auto& code : settings->keycodes) { + for(auto& code : settings.keycodes) { result.append(_pressed(state, code)); } return result; @@ -227,7 +227,7 @@ auto pKeyboard::initialize() -> void { lo = lo ? (uint8_t)XKeysymToKeycode(pApplication::display, lo) : 0; hi = hi ? (uint8_t)XKeysymToKeycode(pApplication::display, hi) : 0; #endif - settings->keycodes.append(lo | (hi << 8)); + settings.keycodes.append(lo | (hi << 8)); }; #define map(name, ...) if(key == name) { append(__VA_ARGS__); continue; } diff --git a/hiro/gtk/settings.cpp b/hiro/gtk/settings.cpp index 594f127d..397ef32c 100644 --- a/hiro/gtk/settings.cpp +++ b/hiro/gtk/settings.cpp @@ -1,26 +1,38 @@ namespace hiro { Settings::Settings() { - geometry.append(geometry.frameX = 4, "FrameX"); - geometry.append(geometry.frameY = 24, "FrameY"); - geometry.append(geometry.frameWidth = 8, "FrameWidth"); - geometry.append(geometry.frameHeight = 28, "FrameHeight"); - geometry.append(geometry.menuHeight = 20, "MenuHeight"); - geometry.append(geometry.statusHeight = 20, "StatusHeight"); - append(geometry, "Geometry"); - window.append(window.backgroundColor = 0xedeceb, "BackgroundColor"); - append(window, "Window"); + string path = {Path::local(), "hiro/"}; + auto document = BML::unserialize(file::read({path, "gtk.bml"})); + + auto get = [&](string_view name) { + return document[name]; + }; + + geometry.frameX = get("Geometry/FrameX").integer(); + geometry.frameY = get("Geometry/FrameY").integer(); + geometry.frameWidth = get("Geometry/FrameWidth").integer(); + geometry.frameHeight = get("Geometry/FrameHeight").integer(); + geometry.menuHeight = get("Geometry/MenuHeight").integer(); + geometry.statusHeight = get("Geometry/StatusHeight").integer(); } -auto Settings::load() -> void { - string path = {Path::config(), "hiro/"}; - Configuration::Document::load({path, "gtk.bml"}); -} - -auto Settings::save() -> void { - string path = {Path::config(), "hiro/"}; +Settings::~Settings() { + string path = {Path::local(), "hiro/"}; directory::create(path, 0755); - Configuration::Document::save({path, "gtk.bml"}); + + Markup::Node document; + auto set = [&](string_view name, string_view value) { + document(name).setValue(value); + }; + + set("Geometry/FrameX", geometry.frameX); + set("Geometry/FrameY", geometry.frameY); + set("Geometry/FrameWidth", geometry.frameWidth); + set("Geometry/FrameHeight", geometry.frameHeight); + set("Geometry/MenuHeight", geometry.menuHeight); + set("Geometry/StatusHeight", geometry.statusHeight); + + file::write({path, "gtk.bml"}, BML::serialize(document)); } } diff --git a/hiro/gtk/settings.hpp b/hiro/gtk/settings.hpp index 8bcd1ed6..4223095c 100644 --- a/hiro/gtk/settings.hpp +++ b/hiro/gtk/settings.hpp @@ -1,26 +1,21 @@ namespace hiro { -struct Settings : Configuration::Document { +struct Settings { + Settings(); + ~Settings(); + vector keycodes; - struct Geometry : Configuration::Node { - signed frameX; - signed frameY; - signed frameWidth; - signed frameHeight; - signed menuHeight; - signed statusHeight; + struct Geometry { + int frameX = 4; + int frameY = 24; + int frameWidth = 8; + int frameHeight = 28; + int menuHeight = 20; + int statusHeight = 20; } geometry; - - struct Window : Configuration::Node { - unsigned backgroundColor; - } window; - - Settings(); - auto load() -> void; - auto save() -> void; }; -static Settings* settings = nullptr; +static Settings settings; } diff --git a/hiro/gtk/window.cpp b/hiro/gtk/window.cpp index 8cd4d289..ef0d43c0 100644 --- a/hiro/gtk/window.cpp +++ b/hiro/gtk/window.cpp @@ -48,11 +48,10 @@ static auto Window_configure(GtkWidget* widget, GdkEvent* event, pWindow* p) -> if(!p->state().fullScreen) { //update geometry settings - settings->geometry.frameX = client.x - border.x; - settings->geometry.frameY = client.y - border.y; - settings->geometry.frameWidth = border.width - client.width; - settings->geometry.frameHeight = border.height - client.height; - settings->save(); + settings.geometry.frameX = client.x - border.x; + settings.geometry.frameY = client.y - border.y; + settings.geometry.frameWidth = border.width - client.width; + settings.geometry.frameHeight = border.height - client.height; } Geometry geometry = { @@ -212,10 +211,10 @@ auto pWindow::frameMargin() const -> Geometry { }; return { - settings->geometry.frameX, - settings->geometry.frameY + _menuHeight(), - settings->geometry.frameWidth, - settings->geometry.frameHeight + _menuHeight() + _statusHeight() + settings.geometry.frameX, + settings.geometry.frameY + _menuHeight(), + settings.geometry.frameWidth, + settings.geometry.frameHeight + _menuHeight() + _statusHeight() }; } @@ -328,13 +327,13 @@ auto pWindow::setVisible(bool visible) -> void { if(gtk_widget_get_visible(gtkMenu)) { GtkAllocation allocation; gtk_widget_get_allocation(gtkMenu, &allocation); - settings->geometry.menuHeight = allocation.height; + settings.geometry.menuHeight = allocation.height; } if(gtk_widget_get_visible(gtkStatus)) { GtkAllocation allocation; gtk_widget_get_allocation(gtkStatus, &allocation); - settings->geometry.statusHeight = allocation.height; + settings.geometry.statusHeight = allocation.height; } } @@ -358,7 +357,7 @@ auto pWindow::_append(mMenu& menu) -> void { } auto pWindow::_menuHeight() const -> signed { - return gtk_widget_get_visible(gtkMenu) ? settings->geometry.menuHeight : 0; + return gtk_widget_get_visible(gtkMenu) ? settings.geometry.menuHeight : 0; } auto pWindow::_setIcon(const string& pathname) -> bool { @@ -414,7 +413,7 @@ auto pWindow::_setStatusVisible(bool visible) -> void { } auto pWindow::_statusHeight() const -> signed { - return gtk_widget_get_visible(gtkStatus) ? settings->geometry.statusHeight : 0; + return gtk_widget_get_visible(gtkStatus) ? settings.geometry.statusHeight : 0; } } diff --git a/hiro/qt/application.cpp b/hiro/qt/application.cpp index fc09378e..064c117c 100644 --- a/hiro/qt/application.cpp +++ b/hiro/qt/application.cpp @@ -41,9 +41,6 @@ auto pApplication::syncX() -> void { auto pApplication::initialize() -> void { display = XOpenDisplay(0); - settings = new Settings; - settings->load(); - static int argc = 1; static char* argv[] = {new char[8], nullptr}; strcpy(argv[0], "hiro"); diff --git a/hiro/qt/keyboard.cpp b/hiro/qt/keyboard.cpp index 2dad0af7..4872be66 100644 --- a/hiro/qt/keyboard.cpp +++ b/hiro/qt/keyboard.cpp @@ -6,7 +6,7 @@ auto pKeyboard::poll() -> vector { vector result; char state[256]; XQueryKeymap(pApplication::display, state); - for(auto& code : settings->keycodes) { + for(auto& code : settings.keycodes) { result.append(_pressed(state, code)); } return result; @@ -32,7 +32,7 @@ auto pKeyboard::initialize() -> void { auto append = [](unsigned lo, unsigned hi = 0) { lo = lo ? (uint8_t)XKeysymToKeycode(pApplication::display, lo) : 0; hi = hi ? (uint8_t)XKeysymToKeycode(pApplication::display, hi) : 0; - settings->keycodes.append(lo << 0 | hi << 8); + settings.keycodes.append(lo << 0 | hi << 8); }; #define map(name, ...) if(key == name) { append(__VA_ARGS__); continue; } diff --git a/hiro/qt/settings.cpp b/hiro/qt/settings.cpp index 484ccf31..3393a065 100644 --- a/hiro/qt/settings.cpp +++ b/hiro/qt/settings.cpp @@ -1,26 +1,38 @@ namespace hiro { -static Settings* settings = nullptr; - Settings::Settings() { - geometry.append(geometry.frameX = 4, "FrameX"); - geometry.append(geometry.frameY = 24, "FrameY"); - geometry.append(geometry.frameWidth = 8, "FrameWidth"); - geometry.append(geometry.frameHeight = 28, "FrameHeight"); - geometry.append(geometry.menuHeight = 20, "MenuHeight"); - geometry.append(geometry.statusHeight = 20, "StatusHeight"); - append(geometry, "Geometry"); + string path = {Path::local(), "hiro/"}; + auto document = BML::unserialize(file::read({path, "qt.bml"})); + + auto get = [&](string_view name) { + return document[name]; + }; + + geometry.frameX = get("Geometry/FrameX").integer(); + geometry.frameY = get("Geometry/FrameY").integer(); + geometry.frameWidth = get("Geometry/FrameWidth").integer(); + geometry.frameHeight = get("Geometry/FrameHeight").integer(); + geometry.menuHeight = get("Geometry/MenuHeight").integer(); + geometry.statusHeight = get("Geometry/StatusHeight").integer(); } -auto Settings::load() -> void { - string path{Path::config(), "hiro/"}; - Configuration::Document::load({path, "qt.bml"}); -} - -auto Settings::save() -> void { - string path{Path::config(), "hiro/"}; +Settings::~Settings() { + string path = {Path::local(), "hiro/"}; directory::create(path, 0755); - Configuration::Document::save({path, "qt.bml"}); + + Markup::Node document; + auto set = [&](string_view name, string_view value) { + document(name).setValue(value); + }; + + set("Geometry/FrameX", geometry.frameX); + set("Geometry/FrameY", geometry.frameY); + set("Geometry/FrameWidth", geometry.frameWidth); + set("Geometry/FrameHeight", geometry.frameHeight); + set("Geometry/MenuHeight", geometry.menuHeight); + set("Geometry/StatusHeight", geometry.statusHeight); + + file::write({path, "qt.bml"}, BML::serialize(document)); } } diff --git a/hiro/qt/settings.hpp b/hiro/qt/settings.hpp index c74657f1..4223095c 100644 --- a/hiro/qt/settings.hpp +++ b/hiro/qt/settings.hpp @@ -1,20 +1,21 @@ namespace hiro { -struct Settings : Configuration::Document { +struct Settings { + Settings(); + ~Settings(); + vector keycodes; - struct Geometry : Configuration::Node { - signed frameX; - signed frameY; - signed frameWidth; - signed frameHeight; - signed menuHeight; - signed statusHeight; + struct Geometry { + int frameX = 4; + int frameY = 24; + int frameWidth = 8; + int frameHeight = 28; + int menuHeight = 20; + int statusHeight = 20; } geometry; - - Settings(); - auto load() -> void; - auto save() -> void; }; +static Settings settings; + } diff --git a/hiro/qt/window.cpp b/hiro/qt/window.cpp index fe5fb798..e28452d4 100644 --- a/hiro/qt/window.cpp +++ b/hiro/qt/window.cpp @@ -70,10 +70,10 @@ auto pWindow::frameMargin() const -> Geometry { 0, _menuHeight() + _statusHeight() }; return { - settings->geometry.frameX, - settings->geometry.frameY + _menuHeight(), - settings->geometry.frameWidth, - settings->geometry.frameHeight + _menuHeight() + _statusHeight() + settings.geometry.frameX, + settings.geometry.frameY + _menuHeight(), + settings.geometry.frameWidth, + settings.geometry.frameHeight + _menuHeight() + _statusHeight() }; } @@ -215,11 +215,11 @@ auto pWindow::_append(mWidget& widget) -> void { } auto pWindow::_menuHeight() const -> signed { - return qtMenuBar->isVisible() ? settings->geometry.menuHeight : 0; + return qtMenuBar->isVisible() ? settings.geometry.menuHeight : 0; } auto pWindow::_statusHeight() const -> signed { - return qtStatusBar->isVisible() ? settings->geometry.statusHeight : 0; + return qtStatusBar->isVisible() ? settings.geometry.statusHeight : 0; } auto pWindow::_updateFrameGeometry() -> void { @@ -227,22 +227,20 @@ auto pWindow::_updateFrameGeometry() -> void { QRect border = qtWindow->frameGeometry(); QRect client = qtWindow->geometry(); - settings->geometry.frameX = client.x() - border.x(); - settings->geometry.frameY = client.y() - border.y(); - settings->geometry.frameWidth = border.width() - client.width(); - settings->geometry.frameHeight = border.height() - client.height(); + settings.geometry.frameX = client.x() - border.x(); + settings.geometry.frameY = client.y() - border.y(); + settings.geometry.frameWidth = border.width() - client.width(); + settings.geometry.frameHeight = border.height() - client.height(); if(qtMenuBar->isVisible()) { pApplication::syncX(); - settings->geometry.menuHeight = qtMenuBar->height(); + settings.geometry.menuHeight = qtMenuBar->height(); } if(qtStatusBar->isVisible()) { pApplication::syncX(); - settings->geometry.statusHeight = qtStatusBar->height(); + settings.geometry.statusHeight = qtStatusBar->height(); } - - settings->save(); } auto QtWindow::closeEvent(QCloseEvent* event) -> void { @@ -305,8 +303,8 @@ auto QtWindow::resizeEvent(QResizeEvent*) -> void { auto QtWindow::sizeHint() const -> QSize { unsigned width = p.state().geometry.width(); unsigned height = p.state().geometry.height(); - if(p.qtMenuBar->isVisible()) height += settings->geometry.menuHeight; - if(p.qtStatusBar->isVisible()) height += settings->geometry.statusHeight; + if(p.qtMenuBar->isVisible()) height += settings.geometry.menuHeight; + if(p.qtStatusBar->isVisible()) height += settings.geometry.statusHeight; return QSize(width, height); } diff --git a/icarus/Database/Super Famicom.bml b/icarus/Database/Super Famicom.bml index 7f63ba81..723a7f4f 100644 --- a/icarus/Database/Super Famicom.bml +++ b/icarus/Database/Super Famicom.bml @@ -25,6 +25,79 @@ cartridge sha256:e678a29a93111cf2016c487ba9977b14de8f719040651a42c74bd74eea2d0d8 : name: Death and Return of Superman, The : title: The Death and Return of Superman +cartridge sha256:039beb46f81ad9e0844ecec420cc78bfdbf2b1ae940adb4fdf08dbf1b55ac7ed + :board region=pal + : rom name=program.rom size=0x200000 + : map address=00-3f,80-bf:8000-ffff + : map address=40-7d,c0-ff:0000-ffff + : ram name=save.ram size=0x2000 + : map address=20-3f,a0-bf:6000-7fff mask=0xe000 + : + :information + : serial: SNSP-JG-NOE + : board: SHVC-1J3M-20 + : revision: 1.1 + : name: Illusion of Time + : title: Illusion of Time + +cartridge sha256:c341668847170d36fa5cfb720568b0b1ecbb24fc426a821f665f1d3853a46a6d + :board region=pal + : rom name=program.rom size=0x300000 + : map address=00-3f,80-bf:8000-ffff + : map address=40-7d,c0-ff:0000-ffff + : ram name=save.ram size=0x2000 + : map address=20-3f,a0-bf:6000-7fff mask=0xe000 + : + :information + : serial: SNSP-AEOD-NOE + : board: SHVC-1J3M-20 + : revision: 1.0 + : name: Secret of Evermore + : title: Secret of Evermore + +cartridge sha256:1444ab11f96f7750db992e9a4160532b27abede8a7054128c09f448300c91ebf + :board region=pal + : rom name=program.rom size=0x200000 + : map address=00-3f,80-bf:8000-ffff + : map address=40-7d,c0-ff:0000-ffff + : ram name=save.ram size=0x2000 + : map address=20-3f,a0-bf:6000-7fff mask=0xe000 + : + :information + : serial: SNSP-K2-NOE + : board: SHVC-1J3M-11 + : revision: 1.0 + : name: Secret of Mana + : title: Secret of Mana + +cartridge sha256:613cd1a31eaded18648168bd7453a57830ca9a6f3c10de5154625436fbd49556 + :board region=pal + : rom name=program.rom size=0x400000 + : map address=00-3f,80-bf:8000-ffff + : map address=40-7d,c0-ff:0000-ffff + : ram name=save.ram size=0x2000 + : map address=20-3f,a0-bf:6000-7fff mask=0xe000 + : + :information + : serial: SNSP-AQTD-NOE + : board: SHVC-1J3M-20 + : revision: 1.1 + : name: Terranigma + : title: Terranigma + +cartridge sha256:f1a75578e6711716340bb26ea93bf05d5762bc7da21dbc19576fc65de1e885b5 + :board region=pal + : rom name=program.rom size=0x100000 + : map address=00-7d,80-ff:8000-ffff mask=0x8000 + : map address=40-7d,c0-ff:0000-7fff mask=0x8000 + : + :information + : serial: SNSP-AT-UKV + : board: SHVC-1A0N-20 + : revision: 1.0 + : name: Super Aleste + : title: Super Aleste + cartridge sha256:2ffe8828480f943056fb1ab5c3c84d48a0bf8cbe3ed7c9960b349b59adb07f3b :board region=ntsc : rom name=program.rom size=0x200000 @@ -543,8 +616,8 @@ cartridge sha256:865919b25a9d241c907bcda18b380e3c704f33f4997ad44559046f0f08c4968 : serial: SNS-8L-USA : board: SHVC-YA0N-01 : revision: 1.0 - : name: Barbie Super Star - : title: Barbie Super Star + : name: Barbie Super Model + : title: Barbie Super Model cartridge sha256:fe1ad128313b2b9a47f89cf0d95d4c0cc2cb35a817ac5d915ee6c4d98d47d675 :board region=ntsc @@ -1759,6 +1832,19 @@ cartridge sha256:e6efb6361af04963f22c772f879a466543f56b3b6a084204fef2dcb4659d82d : name: David Crane's Amazing Tennis : title: David Crane's Amazing Tennis +cartridge sha256:300c1937e4b68108302e9b0f49974d1ec6b6c45dd8da69dddc19443f9562ecf4 + :board region=ntsc + : rom name=program.rom size=0x200000 + : map address=00-7d,80-ff:8000-ffff mask=0x8000 + : map address=40-7d,c0-ff:0000-7fff mask=0x8000 + : + :information + : serial: SNS-9D-USA + : board: SHVC-1A0N-20 + : revision: 1.0 + : name: Death and Return of Superman, The + : title: The Death and Return of Superman + cartridge sha256:752d24fab240f4dd1dfbfea5ec83438998316806ad44488bf8c84430ca5a2cd0 :board region=ntsc : rom name=program.rom size=0x200000 @@ -1886,6 +1972,21 @@ cartridge sha256:628147468c3539283197f58f03b94df49758a332831857481ea9cc31645f052 : name: Donkey Kong Country : title: Donkey Kong Country +cartridge sha256:07ff03fa8c8e31d2f8277ef2a9785022edebf7f79b694c66a00c66d8e563bce5 + :board region=ntsc + : rom name=program.rom size=0x400000 + : map address=00-3f,80-bf:8000-ffff + : map address=40-7d,c0-ff:0000-ffff + : ram name=save.ram size=0x800 + : map address=20-3f,a0-bf:6000-7fff mask=0xe000 + : + :information + : serial: SNS-8E-USA + : board: SHVC-1J1M-20 + : revision: 1.0 + : name: Donkey Kong Country - Competition Cartridge + : title: Donkey Kong Country: Competition Cartridge + cartridge sha256:35421a9af9dd011b40b91f792192af9f99c93201d8d394026bdfb42cbf2d8633 :board region=ntsc : rom name=program.rom size=0x400000 @@ -5287,6 +5388,21 @@ cartridge sha256:2a2dc2ef84efd9a773d1e8231b7e3e57f0de7e4528968670963f2f1f358eef3 : name: NHL '97 : title: NHL '97 +cartridge sha256:0d933149242a2a5278b9ada9294481db5b30aaa134c660951dc340bf8ab441e8 + :board region=ntsc + : rom name=program.rom size=0x180000 + : map address=00-3f,80-bf:8000-ffff + : map address=40-7d,c0-ff:0000-ffff + : ram name=save.ram size=0x2000 + : map address=20-3f,a0-bf:6000-7fff mask=0xe000 + : + :information + : serial: SNS-AH7E-USA + : board: SHVC-1J3M-20 + : revision: 1.1 + : name: NHL '97 + : title: NHL '97 + cartridge sha256:8113c2cedafc8fd5a56c8638ae340fb275f263ff5c5e18d04dc6c3ebc5cfffee :board region=ntsc : rom name=program.rom size=0x200000 @@ -7269,6 +7385,22 @@ cartridge sha256:3857b5294ea8f7468849437bb2d8271564e8a0ff30774622e9c872bcbd53a84 : name: Star Fox : title: Star Fox +cartridge sha256:2c0bac12a7866fad1cb306da768a201c12f2520332df1ef51cba1576db21ff06 + :board region=ntsc + : superfx + : map address=00-3f,80-bf:3000-34ff + : rom name=program.rom size=0x100000 + : map address=00-1f,80-9f:8000-ffff mask=0x8000 + : ram name=save.ram size=0x8000 + : map address=60-7d,e0-ff:0000-ffff + : + :information + : serial: SNS-FU-USA + : board: SHVC-1C0N + : revision: 1.0 + : name: Star Fox - Super Weekend + : title: Star Fox: Super Weekend + cartridge sha256:3a16ad45ae3d89b13c9e53e21c2a4c725ff7cec7fbe7896d538d163f92cb4aac :board region=ntsc : rom name=program.rom size=0x100000 diff --git a/nall/chrono.hpp b/nall/chrono.hpp new file mode 100644 index 00000000..abefb4a4 --- /dev/null +++ b/nall/chrono.hpp @@ -0,0 +1,134 @@ +#pragma once + +#include +#include + +namespace nall { namespace chrono { namespace { + +//passage of time functions (from unknown epoch) + +auto nanosecond() -> uint64_t { + timespec tv; + clock_gettime(CLOCK_MONOTONIC, &tv); + return tv.tv_sec * 1'000'000'000 + tv.tv_nsec; +} + +auto microsecond() -> uint64_t { return nanosecond() / 1'000; } +auto millisecond() -> uint64_t { return nanosecond() / 1'000'000; } +auto second() -> uint64_t { return nanosecond() / 1'000'000'000; } + +auto benchmark(const function& f, uint64_t times = 1) -> void { + auto start = nanosecond(); + while(times--) f(); + auto end = nanosecond(); + print("[chrono::benchmark] ", (double)(end - start) / 1'000'000'000.0, "s\n"); +} + +//exact date/time functions (from system epoch) + +struct timeinfo { + timeinfo( + uint year = 0, uint month = 0, uint day = 0, + uint hour = 0, uint minute = 0, uint second = 0, uint weekday = 0 + ) : year(year), month(month), day(day), + hour(hour), minute(minute), second(second), weekday(weekday) { + } + + explicit operator bool() const { return month; } + + uint year; //... + uint month; //1 - 12 + uint day; //1 - 31 + uint hour; //0 - 23 + uint minute; //0 - 59 + uint second; //0 - 60 + uint weekday; //0 - 6 +}; + +auto timestamp() -> uint64_t { + return ::time(nullptr); +} + +namespace utc { + auto timeinfo(uint64_t time = 0) -> chrono::timeinfo { + auto stamp = time ? (time_t)time : (time_t)timestamp(); + auto info = gmtime(&stamp); + return { + (uint)info->tm_year + 1900, + (uint)info->tm_mon + 1, + (uint)info->tm_mday, + (uint)info->tm_hour, + (uint)info->tm_min, + (uint)info->tm_sec, + (uint)info->tm_wday + }; + } + + auto year(uint64_t timestamp = 0) -> string { return pad(timeinfo(timestamp).year, 4, '0'); } + auto month(uint64_t timestamp = 0) -> string { return pad(timeinfo(timestamp).month, 2, '0'); } + auto day(uint64_t timestamp = 0) -> string { return pad(timeinfo(timestamp).day, 2, '0'); } + auto hour(uint64_t timestamp = 0) -> string { return pad(timeinfo(timestamp).hour, 2, '0'); } + auto minute(uint64_t timestamp = 0) -> string { return pad(timeinfo(timestamp).minute, 2, '0'); } + auto second(uint64_t timestamp = 0) -> string { return pad(timeinfo(timestamp).second, 2, '0'); } + + auto date(uint64_t timestamp = 0) -> string { + auto t = timeinfo(timestamp); + return {pad(t.year, 4, '0'), "-", pad(t.month, 2, '0'), "-", pad(t.day, 2, '0')}; + } + + auto time(uint64_t timestamp = 0) -> string { + auto t = timeinfo(timestamp); + return {pad(t.hour, 2, '0'), ":", pad(t.minute, 2, '0'), ":", pad(t.second, 2, '0')}; + } + + auto datetime(uint64_t timestamp = 0) -> string { + auto t = timeinfo(timestamp); + return { + pad(t.year, 4, '0'), "-", pad(t.month, 2, '0'), "-", pad(t.day, 2, '0'), " ", + pad(t.hour, 2, '0'), ":", pad(t.minute, 2, '0'), ":", pad(t.second, 2, '0') + }; + } +} + +namespace local { + auto timeinfo(uint64_t time = 0) -> chrono::timeinfo { + auto stamp = time ? (time_t)time : (time_t)timestamp(); + auto info = localtime(&stamp); + return { + (uint)info->tm_year + 1900, + (uint)info->tm_mon + 1, + (uint)info->tm_mday, + (uint)info->tm_hour, + (uint)info->tm_min, + (uint)info->tm_sec, + (uint)info->tm_wday + }; + } + + auto year(uint64_t timestamp = 0) -> string { return pad(timeinfo(timestamp).year, 4, '0'); } + auto month(uint64_t timestamp = 0) -> string { return pad(timeinfo(timestamp).month, 2, '0'); } + auto day(uint64_t timestamp = 0) -> string { return pad(timeinfo(timestamp).day, 2, '0'); } + auto hour(uint64_t timestamp = 0) -> string { return pad(timeinfo(timestamp).hour, 2, '0'); } + auto minute(uint64_t timestamp = 0) -> string { return pad(timeinfo(timestamp).minute, 2, '0'); } + auto second(uint64_t timestamp = 0) -> string { return pad(timeinfo(timestamp).second, 2, '0'); } + + auto date(uint64_t timestamp = 0) -> string { + auto t = timeinfo(timestamp); + return {pad(t.year, 4, '0'), "-", pad(t.month, 2, '0'), "-", pad(t.day, 2, '0')}; + } + + auto time(uint64_t timestamp = 0) -> string { + auto t = timeinfo(timestamp); + return {pad(t.hour, 2, '0'), ":", pad(t.minute, 2, '0'), ":", pad(t.second, 2, '0')}; + } + + auto datetime(uint64_t timestamp = 0) -> string { + auto t = timeinfo(timestamp); + return { + pad(t.year, 4, '0'), "-", pad(t.month, 2, '0'), "-", pad(t.day, 2, '0'), " ", + pad(t.hour, 2, '0'), ":", pad(t.minute, 2, '0'), ":", pad(t.second, 2, '0') + }; + } +} + +}}} diff --git a/nall/config.hpp b/nall/config.hpp deleted file mode 100644 index 83912d63..00000000 --- a/nall/config.hpp +++ /dev/null @@ -1,110 +0,0 @@ -#pragma once - -#include -#include -#include - -namespace nall { -namespace Configuration { - -struct Node { - string name; - string desc; - enum class Type : uint { Null, Boolean, Integer, Natural, Double, String } type = Type::Null; - void* data = nullptr; - vector children; - - explicit operator bool() const { return data; } - - auto get() const -> string { - switch(type) { - case Type::Boolean: return {*(bool*)data}; - case Type::Integer: return {*(int*)data}; - case Type::Natural: return {*(uint*)data}; - case Type::Double: return {*(double*)data}; - case Type::String: return {*(string*)data}; - } - return ""; - } - - auto set(const string& value) -> void { - switch(type) { - case Type::Boolean: *(bool*)data = (value != "false"); break; - case Type::Integer: *(int*)data = toInteger(value); break; - case Type::Natural: *(uint*)data = toNatural(value); break; - case Type::Double: *(double*)data = toReal(value); break; - case Type::String: *(string*)data = value; break; - } - } - - auto assign() { type = Type::Null; data = nullptr; } - auto assign(bool& bind) { type = Type::Boolean; data = (void*)&bind; } - auto assign(int& bind) { type = Type::Integer; data = (void*)&bind; } - auto assign(uint& bind) { type = Type::Natural; data = (void*)&bind; } - auto assign(double& bind) { type = Type::Double; data = (void*)&bind; } - auto assign(string& bind) { type = Type::String; data = (void*)&bind; } - auto assign(const Node& node) { operator=(node); } - - template auto append(T& data, const string& name, const string& desc = "") -> void { - Node node; - node.assign(data); - node.name = name; - node.desc = desc; - children.append(node); - } - - auto find(const string& path) -> maybe { - auto p = path.split("/"); - auto name = p.takeLeft(); - for(auto& child : children) { - if(child.name == name) { - if(p.size() == 0) return child; - return child.find(p.merge("/")); - } - } - return nothing; - } - - auto load(Markup::Node path) -> void { - for(auto& child : children) { - if(auto leaf = path[child.name]) { - if(child) child.set(leaf.text()); - child.load(leaf); - } - } - } - - auto save(file& fp, uint depth = 0) -> void { - for(auto& child : children) { - if(child.desc) { - for(auto n : range(depth)) fp.print(" "); - fp.print("//", child.desc, "\n"); - } - for(auto n : range(depth)) fp.print(" "); - fp.print(child.name); - if(child) fp.print(": ", child.get()); - fp.print("\n"); - child.save(fp, depth + 1); - if(depth == 0) fp.print("\n"); - } - } -}; - -struct Document : Node { - auto load(const string& filename) -> bool { - if(!file::exists(filename)) return false; - auto document = BML::unserialize(string::read(filename)); - Node::load(document); - return true; - } - - auto save(const string& filename) -> bool { - file fp(filename, file::mode::write); - if(!fp.open()) return false; - Node::save(fp); - return true; - } -}; - -} -} diff --git a/nall/http/response.hpp b/nall/http/response.hpp index 2e2c12e4..ec0dcf7a 100644 --- a/nall/http/response.hpp +++ b/nall/http/response.hpp @@ -230,7 +230,7 @@ auto Response::setData(const vector& value) -> type& { auto Response::setFile(const string& value) -> type& { _file = value; - string eTag = {"\"", string::datetime(file::timestamp(value, file::time::modify)), "\""}; + string eTag = {"\"", chrono::utc::datetime(file::timestamp(value, file::time::modify)), "\""}; header.assign("Content-Length", file::size(value)); header.assign("Cache-Control", "public"); header.assign("ETag", eTag); diff --git a/nall/inode.hpp b/nall/inode.hpp index b56374c4..364985d5 100644 --- a/nall/inode.hpp +++ b/nall/inode.hpp @@ -45,7 +45,7 @@ struct inode { return data.st_mode; } - static auto timestamp(const string& name, time mode = time::modify) -> time_t { + static auto timestamp(const string& name, time mode = time::modify) -> uint64_t { struct stat data = {0}; stat(name, &data); switch(mode) { default: diff --git a/nall/nall.hpp b/nall/nall.hpp index fa40e838..648da7f1 100644 --- a/nall/nall.hpp +++ b/nall/nall.hpp @@ -19,7 +19,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/nall/string.hpp b/nall/string.hpp index 956d6bba..5515081c 100644 --- a/nall/string.hpp +++ b/nall/string.hpp @@ -62,7 +62,7 @@ template struct stringify; //format.hpp template inline auto print(P&&...) -> void; template inline auto print(FILE*, P&&...) -> void; -template inline auto numeral(T value, long precision = 0, char padchar = '0') -> string; +template inline auto pad(const T& value, long precision = 0, char padchar = ' ') -> string; inline auto hex(uintmax_t value, long precision = 0, char padchar = '0') -> string; inline auto octal(uintmax_t value, long precision = 0, char padchar = '0') -> string; inline auto binary(uintmax_t value, long precision = 0, char padchar = '0') -> string; @@ -183,11 +183,6 @@ public: template inline auto _append(const stringify&) -> string&; inline auto length() const -> uint; - //datetime.hpp - inline static auto date(time_t = 0) -> string; - inline static auto time(time_t = 0) -> string; - inline static auto datetime(time_t = 0) -> string; - //find.hpp template inline auto _find(int, string_view) const -> maybe; @@ -316,7 +311,6 @@ struct string_format : vector { #include #include #include -#include #include #include #include diff --git a/nall/string/datetime.hpp b/nall/string/datetime.hpp deleted file mode 100644 index 9d24230c..00000000 --- a/nall/string/datetime.hpp +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once - -namespace nall { - -auto string::date(time_t timestamp) -> string { - if(timestamp == 0) timestamp = ::time(nullptr); - tm* info = localtime(×tamp); - return { - numeral(1900 + info->tm_year, 4L), "-", - numeral(1 + info->tm_mon, 2L), "-", - numeral(info->tm_mday, 2L) - }; -} - -auto string::time(time_t timestamp) -> string { - if(timestamp == 0) timestamp = ::time(nullptr); - tm* info = localtime(×tamp); - return { - numeral(info->tm_hour, 2L), ":", - numeral(info->tm_min, 2L), ":", - numeral(info->tm_sec, 2L) - }; -} - -auto string::datetime(time_t timestamp) -> string { - if(timestamp == 0) timestamp = ::time(nullptr); - return {string::date(timestamp), " ", string::time(timestamp)}; -} - -} diff --git a/nall/string/format.hpp b/nall/string/format.hpp index 78a61eeb..be86e812 100644 --- a/nall/string/format.hpp +++ b/nall/string/format.hpp @@ -78,44 +78,7 @@ template auto print(FILE* fp, P&&... p) -> void { fwrite(s.data(), 1, s.size(), fp); } -/* -auto integer(intmax_t value, long precision, char padchar) -> string { - string buffer; - buffer.resize(1 + sizeof(intmax_t) * 3); - char* p = buffer.get(); - - bool negative = value < 0; - if(negative) value = -value; //make positive - uint size = 0; - do { - p[size++] = '0' + (value % 10); - value /= 10; - } while(value); - if(negative) p[size++] = '-'; - buffer.resize(size); - buffer.reverse(); - if(precision) buffer.size(precision, padchar); - return buffer; -} - -auto natural(uintmax_t value, long precision, char padchar) -> string { - string buffer; - buffer.resize(sizeof(uintmax_t) * 3); - char* p = buffer.get(); - - uint size = 0; - do { - p[size++] = '0' + (value % 10); - value /= 10; - } while(value); - buffer.resize(size); - buffer.reverse(); - if(precision) buffer.size(precision, padchar); - return buffer; -} -*/ - -template auto numeral(T value, long precision, char padchar) -> string { +template auto pad(const T& value, long precision, char padchar) -> string { string buffer{value}; if(precision) buffer.size(precision, padchar); return buffer;