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;