Update to v100r16 release.

byuu says:

(Windows users may need to include <sys/time.h> 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/<framerate>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.
This commit is contained in:
Tim Allen 2016-08-03 22:32:40 +10:00
parent c50723ef61
commit f5e5bf1772
29 changed files with 404 additions and 300 deletions

View File

@ -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/";

View File

@ -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);
}

View File

@ -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();

View File

@ -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<HID::Device> device, uint group, uint input, int16_t oldValue, int16_t newValue) -> void {

View File

@ -64,6 +64,8 @@ struct InputManager {
vector<InputHotkey*> 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> inputManager;

View File

@ -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

View File

@ -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(&current);
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();
}

View File

@ -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();

View File

@ -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"
};
}

View File

@ -1,5 +1,4 @@
#include <nall/platform.hpp>
#include <nall/config.hpp>
#include <nall/directory.hpp>
#include <nall/function.hpp>
#include <nall/image.hpp>

View File

@ -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);

View File

@ -8,7 +8,7 @@ auto pKeyboard::poll() -> vector<bool> {
#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; }

View File

@ -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));
}
}

View File

@ -1,26 +1,21 @@
namespace hiro {
struct Settings : Configuration::Document {
struct Settings {
Settings();
~Settings();
vector<uint16_t> 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;
}

View File

@ -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;
}
}

View File

@ -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");

View File

@ -6,7 +6,7 @@ auto pKeyboard::poll() -> vector<bool> {
vector<bool> 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; }

View File

@ -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));
}
}

View File

@ -1,20 +1,21 @@
namespace hiro {
struct Settings : Configuration::Document {
struct Settings {
Settings();
~Settings();
vector<uint16_t> 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;
}

View File

@ -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);
}

View File

@ -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

134
nall/chrono.hpp Normal file
View File

@ -0,0 +1,134 @@
#pragma once
#include <nall/function.hpp>
#include <nall/string.hpp>
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<void ()>& 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')
};
}
}
}}}

View File

@ -1,110 +0,0 @@
#pragma once
#include <nall/platform.hpp>
#include <nall/file.hpp>
#include <nall/string.hpp>
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<Node> 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<typename T> 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<Node&> {
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;
}
};
}
}

View File

@ -230,7 +230,7 @@ auto Response::setData(const vector<uint8_t>& 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);

View File

@ -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:

View File

@ -19,7 +19,7 @@
#include <nall/bit.hpp>
#include <nall/bit-field.hpp>
#include <nall/bit-vector.hpp>
#include <nall/config.hpp>
#include <nall/chrono.hpp>
#include <nall/directory.hpp>
#include <nall/dl.hpp>
#include <nall/endian.hpp>

View File

@ -62,7 +62,7 @@ template<typename T> struct stringify;
//format.hpp
template<typename... P> inline auto print(P&&...) -> void;
template<typename... P> inline auto print(FILE*, P&&...) -> void;
template<typename T> inline auto numeral(T value, long precision = 0, char padchar = '0') -> string;
template<typename T> 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<typename T> inline auto _append(const stringify<T>&) -> 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<bool, bool> inline auto _find(int, string_view) const -> maybe<uint>;
@ -316,7 +311,6 @@ struct string_format : vector<string> {
#include <nall/string/compare.hpp>
#include <nall/string/convert.hpp>
#include <nall/string/core.hpp>
#include <nall/string/datetime.hpp>
#include <nall/string/find.hpp>
#include <nall/string/format.hpp>
#include <nall/string/list.hpp>

View File

@ -1,30 +0,0 @@
#pragma once
namespace nall {
auto string::date(time_t timestamp) -> string {
if(timestamp == 0) timestamp = ::time(nullptr);
tm* info = localtime(&timestamp);
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(&timestamp);
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)};
}
}

View File

@ -78,44 +78,7 @@ template<typename... P> 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<typename T> auto numeral(T value, long precision, char padchar) -> string {
template<typename T> auto pad(const T& value, long precision, char padchar) -> string {
string buffer{value};
if(precision) buffer.size(precision, padchar);
return buffer;