mirror of
https://github.com/bsnes-emu/bsnes.git
synced 2025-09-02 03:22:45 +02:00
v114.5
* improved appended firmware detection [devinacker] * added dynamic rate control support to ALSA and PulseAudio drivers [RedDwarf] * added option to use native file dialogs
This commit is contained in:
@@ -108,7 +108,7 @@ ifeq ($(findstring clang++,$(compiler)),clang++)
|
||||
flags += -fno-strict-aliasing -fwrapv -Wno-everything
|
||||
# gcc settings
|
||||
else ifeq ($(findstring g++,$(compiler)),g++)
|
||||
flags += -fno-strict-aliasing -fwrapv
|
||||
flags += -fno-strict-aliasing -fwrapv -Wno-trigraphs
|
||||
endif
|
||||
|
||||
# windows settings
|
||||
|
@@ -49,6 +49,55 @@ inline auto timestamp() -> uint64_t {
|
||||
return ::time(nullptr);
|
||||
}
|
||||
|
||||
//0 = failure condition
|
||||
inline auto timestamp(const string& datetime) -> uint64_t {
|
||||
static const uint monthDays[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
|
||||
uint64_t timestamp = 0;
|
||||
if(datetime.match("??????????")) {
|
||||
return datetime.natural();
|
||||
}
|
||||
if(datetime.match("????*")) {
|
||||
uint year = datetime.slice(0, 4).natural();
|
||||
if(year < 1970 || year > 2199) return 0;
|
||||
for(uint y = 1970; y < year && y < 2999; y++) {
|
||||
uint daysInYear = 365;
|
||||
if(y % 4 == 0 && (y % 100 != 0 || y % 400 == 0)) daysInYear++;
|
||||
timestamp += daysInYear * 24 * 60 * 60;
|
||||
}
|
||||
}
|
||||
if(datetime.match("????-??*")) {
|
||||
uint y = datetime.slice(0, 4).natural();
|
||||
uint month = datetime.slice(5, 2).natural();
|
||||
if(month < 1 || month > 12) return 0;
|
||||
for(uint m = 1; m < month && m < 12; m++) {
|
||||
uint daysInMonth = monthDays[m - 1];
|
||||
if(m == 2 && y % 4 == 0 && (y % 100 != 0 || y % 400 == 0)) daysInMonth++;
|
||||
timestamp += daysInMonth * 24 * 60 * 60;
|
||||
}
|
||||
}
|
||||
if(datetime.match("????-??-??*")) {
|
||||
uint day = datetime.slice(8, 2).natural();
|
||||
if(day < 1 || day > 31) return 0;
|
||||
timestamp += (day - 1) * 24 * 60 * 60;
|
||||
}
|
||||
if(datetime.match("????-??-?? ??*")) {
|
||||
uint hour = datetime.slice(11, 2).natural();
|
||||
if(hour > 23) return 0;
|
||||
timestamp += hour * 60 * 60;
|
||||
}
|
||||
if(datetime.match("????-??-?? ??:??*")) {
|
||||
uint minute = datetime.slice(14, 2).natural();
|
||||
if(minute > 59) return 0;
|
||||
timestamp += minute * 60;
|
||||
}
|
||||
if(datetime.match("????-??-?? ??:??:??*")) {
|
||||
uint second = datetime.slice(17, 2).natural();
|
||||
if(second > 59) return 0;
|
||||
timestamp += second;
|
||||
}
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
namespace utc {
|
||||
inline auto timeinfo(uint64_t time = 0) -> chrono::timeinfo {
|
||||
auto stamp = time ? (time_t)time : (time_t)timestamp();
|
||||
|
@@ -1,9 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
/* SQLite3 C++ RAII wrapper for nall
|
||||
*
|
||||
* Note on code below: it is safe (no-op) to call sqlite3_* functions on null sqlite3 objects
|
||||
*/
|
||||
//SQLite3 C++ RAII wrapper for nall
|
||||
//note: it is safe (no-op) to call sqlite3_* functions on null sqlite3 objects
|
||||
|
||||
#include <sqlite3.h>
|
||||
|
||||
@@ -34,24 +32,28 @@ struct SQLite3 {
|
||||
return sqlite3_data_count(statement());
|
||||
}
|
||||
|
||||
auto columns() -> unsigned {
|
||||
auto columns() -> uint {
|
||||
return sqlite3_column_count(statement());
|
||||
}
|
||||
|
||||
auto integer(unsigned column) -> int64_t {
|
||||
auto boolean(uint column) -> bool {
|
||||
return sqlite3_column_int64(statement(), column) != 0;
|
||||
}
|
||||
|
||||
auto integer(uint column) -> int64_t {
|
||||
return sqlite3_column_int64(statement(), column);
|
||||
}
|
||||
|
||||
auto natural(unsigned column) -> uint64_t {
|
||||
auto natural(uint column) -> uint64_t {
|
||||
return sqlite3_column_int64(statement(), column);
|
||||
}
|
||||
|
||||
auto real(unsigned column) -> double {
|
||||
auto real(uint column) -> double {
|
||||
return sqlite3_column_double(statement(), column);
|
||||
}
|
||||
|
||||
auto text(unsigned column) -> string {
|
||||
string result;
|
||||
auto string(uint column) -> nall::string {
|
||||
nall::string result;
|
||||
if(auto text = sqlite3_column_text(statement(), column)) {
|
||||
result.resize(sqlite3_column_bytes(statement(), column));
|
||||
memory::copy(result.get(), text, result.size());
|
||||
@@ -59,7 +61,7 @@ struct SQLite3 {
|
||||
return result;
|
||||
}
|
||||
|
||||
auto data(unsigned column) -> vector<uint8_t> {
|
||||
auto data(uint column) -> vector<uint8_t> {
|
||||
vector<uint8_t> result;
|
||||
if(auto data = sqlite3_column_blob(statement(), column)) {
|
||||
result.resize(sqlite3_column_bytes(statement(), column));
|
||||
@@ -68,18 +70,19 @@ struct SQLite3 {
|
||||
return result;
|
||||
}
|
||||
|
||||
auto boolean() -> bool { return boolean(_output++); }
|
||||
auto integer() -> int64_t { return integer(_output++); }
|
||||
auto natural() -> uint64_t { return natural(_output++); }
|
||||
auto real() -> double { return real(_output++); }
|
||||
auto text() -> string { return text(_output++); }
|
||||
auto string() -> nall::string { return string(_output++); }
|
||||
auto data() -> vector<uint8_t> { return data(_output++); }
|
||||
|
||||
protected:
|
||||
virtual auto statement() -> sqlite3_stmt* { return _statement; }
|
||||
|
||||
sqlite3_stmt* _statement = nullptr;
|
||||
signed _response = SQLITE_OK;
|
||||
unsigned _output = 0;
|
||||
int _response = SQLITE_OK;
|
||||
uint _output = 0;
|
||||
};
|
||||
|
||||
struct Query : Statement {
|
||||
@@ -102,22 +105,34 @@ struct SQLite3 {
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto& bind(unsigned column, nullptr_t) { sqlite3_bind_null(_statement, 1 + column); return *this; }
|
||||
auto& bind(unsigned column, int32_t value) { sqlite3_bind_int(_statement, 1 + column, value); return *this; }
|
||||
auto& bind(unsigned column, uint32_t value) { sqlite3_bind_int(_statement, 1 + column, value); return *this; }
|
||||
auto& bind(unsigned column, int64_t value) { sqlite3_bind_int64(_statement, 1 + column, value); return *this; }
|
||||
auto& bind(unsigned column, uint64_t value) { sqlite3_bind_int64(_statement, 1 + column, value); return *this; }
|
||||
auto& bind(unsigned column, double value) { sqlite3_bind_double(_statement, 1 + column, value); return *this; }
|
||||
auto& bind(unsigned column, const string& value) { sqlite3_bind_text(_statement, 1 + column, value.data(), value.size(), SQLITE_TRANSIENT); return *this; }
|
||||
auto& bind(unsigned column, const vector<uint8_t>& value) { sqlite3_bind_blob(_statement, 1 + column, value.data(), value.size(), SQLITE_TRANSIENT); return *this; }
|
||||
auto& bind(uint column, nullptr_t) { sqlite3_bind_null(_statement, 1 + column); return *this; }
|
||||
auto& bind(uint column, bool value) { sqlite3_bind_int(_statement, 1 + column, value); return *this; }
|
||||
auto& bind(uint column, int32_t value) { sqlite3_bind_int(_statement, 1 + column, value); return *this; }
|
||||
auto& bind(uint column, uint32_t value) { sqlite3_bind_int(_statement, 1 + column, value); return *this; }
|
||||
auto& bind(uint column, int64_t value) { sqlite3_bind_int64(_statement, 1 + column, value); return *this; }
|
||||
auto& bind(uint column, uint64_t value) { sqlite3_bind_int64(_statement, 1 + column, value); return *this; }
|
||||
auto& bind(uint column, intmax value) { sqlite3_bind_int64(_statement, 1 + column, value); return *this; }
|
||||
auto& bind(uint column, uintmax value) { sqlite3_bind_int64(_statement, 1 + column, value); return *this; }
|
||||
auto& bind(uint column, nall::boolean value) { sqlite3_bind_int64(_statement, 1 + column, value); return *this; }
|
||||
auto& bind(uint column, nall::integer value) { sqlite3_bind_int64(_statement, 1 + column, value); return *this; }
|
||||
auto& bind(uint column, nall::natural value) { sqlite3_bind_int64(_statement, 1 + column, value); return *this; }
|
||||
auto& bind(uint column, double value) { sqlite3_bind_double(_statement, 1 + column, value); return *this; }
|
||||
auto& bind(uint column, const nall::string& value) { sqlite3_bind_text(_statement, 1 + column, value.data(), value.size(), SQLITE_TRANSIENT); return *this; }
|
||||
auto& bind(uint column, const vector<uint8_t>& value) { sqlite3_bind_blob(_statement, 1 + column, value.data(), value.size(), SQLITE_TRANSIENT); return *this; }
|
||||
|
||||
auto& bind(nullptr_t) { return bind(_input++, nullptr); }
|
||||
auto& bind(bool value) { return bind(_input++, value); }
|
||||
auto& bind(int32_t value) { return bind(_input++, value); }
|
||||
auto& bind(uint32_t value) { return bind(_input++, value); }
|
||||
auto& bind(int64_t value) { return bind(_input++, value); }
|
||||
auto& bind(uint64_t value) { return bind(_input++, value); }
|
||||
auto& bind(intmax value) { return bind(_input++, value); }
|
||||
auto& bind(uintmax value) { return bind(_input++, value); }
|
||||
auto& bind(nall::boolean value) { return bind(_input++, value); }
|
||||
auto& bind(nall::integer value) { return bind(_input++, value); }
|
||||
auto& bind(nall::natural value) { return bind(_input++, value); }
|
||||
auto& bind(double value) { return bind(_input++, value); }
|
||||
auto& bind(const string& value) { return bind(_input++, value); }
|
||||
auto& bind(const nall::string& value) { return bind(_input++, value); }
|
||||
auto& bind(const vector<uint8_t>& value) { return bind(_input++, value); }
|
||||
|
||||
auto step() -> bool {
|
||||
@@ -145,7 +160,7 @@ struct SQLite3 {
|
||||
return _statement;
|
||||
}
|
||||
|
||||
unsigned _input = 0;
|
||||
uint _input = 0;
|
||||
bool _stepped = false;
|
||||
};
|
||||
|
||||
|
@@ -7,6 +7,9 @@
|
||||
namespace nall::DSP::Resampler {
|
||||
|
||||
struct Cubic {
|
||||
inline auto inputFrequency() const -> double { return _inputFrequency; }
|
||||
inline auto outputFrequency() const -> double { return _outputFrequency; }
|
||||
|
||||
inline auto reset(double inputFrequency, double outputFrequency = 0, uint queueSize = 0) -> void;
|
||||
inline auto setInputFrequency(double inputFrequency) -> void;
|
||||
inline auto pending() const -> bool;
|
||||
@@ -15,41 +18,41 @@ struct Cubic {
|
||||
inline auto serialize(serializer&) -> void;
|
||||
|
||||
private:
|
||||
double inputFrequency;
|
||||
double outputFrequency;
|
||||
double _inputFrequency;
|
||||
double _outputFrequency;
|
||||
|
||||
double ratio;
|
||||
double fraction;
|
||||
double history[4];
|
||||
queue<double> samples;
|
||||
double _ratio;
|
||||
double _fraction;
|
||||
double _history[4];
|
||||
queue<double> _samples;
|
||||
};
|
||||
|
||||
auto Cubic::reset(double inputFrequency, double outputFrequency, uint queueSize) -> void {
|
||||
this->inputFrequency = inputFrequency;
|
||||
this->outputFrequency = outputFrequency ? outputFrequency : this->inputFrequency;
|
||||
_inputFrequency = inputFrequency;
|
||||
_outputFrequency = outputFrequency ? outputFrequency : _inputFrequency;
|
||||
|
||||
ratio = inputFrequency / outputFrequency;
|
||||
fraction = 0.0;
|
||||
for(auto& sample : history) sample = 0.0;
|
||||
samples.resize(queueSize ? queueSize : this->outputFrequency * 0.02); //default to 20ms max queue size
|
||||
_ratio = _inputFrequency / _outputFrequency;
|
||||
_fraction = 0.0;
|
||||
for(auto& sample : _history) sample = 0.0;
|
||||
_samples.resize(queueSize ? queueSize : _outputFrequency * 0.02); //default to 20ms max queue size
|
||||
}
|
||||
|
||||
auto Cubic::setInputFrequency(double inputFrequency) -> void {
|
||||
this->inputFrequency = inputFrequency;
|
||||
ratio = inputFrequency / outputFrequency;
|
||||
_inputFrequency = inputFrequency;
|
||||
_ratio = _inputFrequency / _outputFrequency;
|
||||
}
|
||||
|
||||
auto Cubic::pending() const -> bool {
|
||||
return samples.pending();
|
||||
return _samples.pending();
|
||||
}
|
||||
|
||||
auto Cubic::read() -> double {
|
||||
return samples.read();
|
||||
return _samples.read();
|
||||
}
|
||||
|
||||
auto Cubic::write(double sample) -> void {
|
||||
auto& mu = fraction;
|
||||
auto& s = history;
|
||||
auto& mu = _fraction;
|
||||
auto& s = _history;
|
||||
|
||||
s[0] = s[1];
|
||||
s[1] = s[2];
|
||||
@@ -62,20 +65,20 @@ auto Cubic::write(double sample) -> void {
|
||||
double C = s[2] - s[0];
|
||||
double D = s[1];
|
||||
|
||||
samples.write(A * mu * mu * mu + B * mu * mu + C * mu + D);
|
||||
mu += ratio;
|
||||
_samples.write(A * mu * mu * mu + B * mu * mu + C * mu + D);
|
||||
mu += _ratio;
|
||||
}
|
||||
|
||||
mu -= 1.0;
|
||||
}
|
||||
|
||||
auto Cubic::serialize(serializer& s) -> void {
|
||||
s.real(inputFrequency);
|
||||
s.real(outputFrequency);
|
||||
s.real(ratio);
|
||||
s.real(fraction);
|
||||
s.array(history);
|
||||
samples.serialize(s);
|
||||
s.real(_inputFrequency);
|
||||
s.real(_outputFrequency);
|
||||
s.real(_ratio);
|
||||
s.real(_fraction);
|
||||
s.array(_history);
|
||||
_samples.serialize(s);
|
||||
}
|
||||
|
||||
}
|
||||
|
52
nall/encode/wav.hpp
Normal file
52
nall/encode/wav.hpp
Normal file
@@ -0,0 +1,52 @@
|
||||
#pragma once
|
||||
|
||||
namespace nall::Encode {
|
||||
|
||||
struct WAV {
|
||||
static auto stereo_16bit(const string& filename, array_view<int16_t> left, array_view<int16_t> right, uint frequency) -> bool {
|
||||
if(left.size() != right.size()) return false;
|
||||
static uint channels = 2;
|
||||
static uint bits = 16;
|
||||
static uint samples = left.size();
|
||||
|
||||
file_buffer fp;
|
||||
if(!fp.open(filename, file::mode::write)) return false;
|
||||
|
||||
fp.write('R');
|
||||
fp.write('I');
|
||||
fp.write('F');
|
||||
fp.write('F');
|
||||
fp.writel(4 + (8 + 16) + (8 + samples * 4), 4);
|
||||
|
||||
fp.write('W');
|
||||
fp.write('A');
|
||||
fp.write('V');
|
||||
fp.write('E');
|
||||
|
||||
fp.write('f');
|
||||
fp.write('m');
|
||||
fp.write('t');
|
||||
fp.write(' ');
|
||||
fp.writel(16, 4);
|
||||
fp.writel(1, 2);
|
||||
fp.writel(channels, 2);
|
||||
fp.writel(frequency, 4);
|
||||
fp.writel(frequency * channels * bits, 4);
|
||||
fp.writel(channels * bits, 2);
|
||||
fp.writel(bits, 2);
|
||||
|
||||
fp.write('d');
|
||||
fp.write('a');
|
||||
fp.write('t');
|
||||
fp.write('a');
|
||||
fp.writel(samples * 4, 4);
|
||||
for(uint sample : range(samples)) {
|
||||
fp.writel(left[sample], 2);
|
||||
fp.writel(right[sample], 2);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
@@ -38,6 +38,7 @@ namespace nall {
|
||||
#pragma clang diagnostic ignored "-Wtautological-compare"
|
||||
#pragma clang diagnostic ignored "-Wabsolute-value"
|
||||
#pragma clang diagnostic ignored "-Wshift-count-overflow"
|
||||
#pragma clang diagnostic ignored "-Wtrigraphs"
|
||||
|
||||
//temporary
|
||||
#pragma clang diagnostic ignored "-Winconsistent-missing-override"
|
||||
@@ -51,6 +52,7 @@ namespace nall {
|
||||
#pragma GCC diagnostic ignored "-Wunknown-pragmas"
|
||||
#pragma GCC diagnostic ignored "-Wpragmas"
|
||||
#pragma GCC diagnostic ignored "-Wswitch-bool"
|
||||
#pragma GCC diagnostic ignored "-Wtrigraphs"
|
||||
#elif defined(_MSC_VER)
|
||||
#define COMPILER_MICROSOFT
|
||||
constexpr auto compiler() -> Compiler { return Compiler::Microsoft; }
|
||||
|
@@ -291,7 +291,7 @@ public:
|
||||
inline auto remove(uint offset, uint length) -> type&;
|
||||
inline auto reverse() -> type&;
|
||||
inline auto size(int length, char fill = ' ') -> type&;
|
||||
inline auto slice(int offset = 0, int length = -1) -> string;
|
||||
inline auto slice(int offset = 0, int length = -1) const -> string;
|
||||
};
|
||||
|
||||
template<> struct vector<string> : vector_base<string> {
|
||||
|
@@ -9,16 +9,12 @@
|
||||
namespace nall {
|
||||
|
||||
struct DML {
|
||||
inline auto title() const -> string { return state.title; }
|
||||
inline auto subtitle() const -> string { return state.subtitle; }
|
||||
inline auto description() const -> string { return state.description; }
|
||||
inline auto content() const -> string { return state.output; }
|
||||
auto content() const -> string { return state.output; }
|
||||
|
||||
auto& setAllowHTML(bool allowHTML) { settings.allowHTML = allowHTML; return *this; }
|
||||
auto& setHost(const string& hostname) { settings.host = {hostname, "/"}; return *this; }
|
||||
auto& setHost(const string& hostname) { settings.host = hostname; return *this; }
|
||||
auto& setPath(const string& pathname) { settings.path = pathname; return *this; }
|
||||
auto& setReader(const function<string (string)>& reader) { settings.reader = reader; return *this; }
|
||||
auto& setSectioned(bool sectioned) { settings.sectioned = sectioned; return *this; }
|
||||
|
||||
auto parse(const string& filedata, const string& pathname) -> string;
|
||||
auto parse(const string& filename) -> string;
|
||||
@@ -28,18 +24,13 @@ struct DML {
|
||||
private:
|
||||
struct Settings {
|
||||
bool allowHTML = true;
|
||||
string host = "localhost/";
|
||||
string host = "localhost";
|
||||
string path;
|
||||
function<string (string)> reader;
|
||||
bool sectioned = true;
|
||||
} settings;
|
||||
|
||||
struct State {
|
||||
string title;
|
||||
string subtitle;
|
||||
string description;
|
||||
string output;
|
||||
uint sections = 0;
|
||||
} state;
|
||||
|
||||
struct Attribute {
|
||||
@@ -52,6 +43,7 @@ private:
|
||||
auto parseBlock(string& block, const string& pathname, uint depth) -> bool;
|
||||
auto count(const string& text, char value) -> uint;
|
||||
|
||||
auto address(string text) -> string;
|
||||
auto escape(const string& text) -> string;
|
||||
auto markup(const string& text) -> string;
|
||||
};
|
||||
@@ -83,7 +75,6 @@ inline auto DML::parseDocument(const string& filedata, const string& pathname, u
|
||||
|
||||
auto blocks = filedata.split("\n\n");
|
||||
for(auto& block : blocks) parseBlock(block, pathname, depth);
|
||||
if(settings.sectioned && state.sections && depth == 0) state.output.append("</section>\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -98,6 +89,18 @@ inline auto DML::parseBlock(string& block, const string& pathname, uint depth) -
|
||||
parseDocument(document, Location::path(filename), depth + 1);
|
||||
}
|
||||
|
||||
//attribute
|
||||
else if(block.beginsWith("? ")) {
|
||||
for(auto n : range(lines.size())) {
|
||||
if(!lines[n].beginsWith("? ")) continue;
|
||||
auto part = lines[n].trimLeft("? ", 1L).split(":", 1L);
|
||||
if(part.size() != 2) continue;
|
||||
auto name = part[0].strip();
|
||||
auto value = part[1].strip();
|
||||
attributes.append({name, value});
|
||||
}
|
||||
}
|
||||
|
||||
//html
|
||||
else if(block.beginsWith("<html>\n") && settings.allowHTML) {
|
||||
for(auto n : range(lines.size())) {
|
||||
@@ -106,52 +109,18 @@ inline auto DML::parseBlock(string& block, const string& pathname, uint depth) -
|
||||
}
|
||||
}
|
||||
|
||||
//attribute
|
||||
else if(block.beginsWith("! ")) {
|
||||
for(auto& line : lines) {
|
||||
auto parts = line.trimLeft("! ", 1L).split(":", 1L);
|
||||
if(parts.size() == 2) attributes.append({parts[0].strip(), parts[1].strip()});
|
||||
}
|
||||
}
|
||||
|
||||
//description
|
||||
else if(block.beginsWith("? ")) {
|
||||
while(lines) {
|
||||
state.description.append(lines.takeLeft().trimLeft("? ", 1L), " ");
|
||||
}
|
||||
state.description.strip();
|
||||
}
|
||||
|
||||
//section
|
||||
else if(block.beginsWith("# ")) {
|
||||
if(settings.sectioned) {
|
||||
if(state.sections++) state.output.append("</section>");
|
||||
state.output.append("<section>");
|
||||
}
|
||||
auto content = lines.takeLeft().trimLeft("# ", 1L).split("::", 1L).strip();
|
||||
auto data = markup(content[0]);
|
||||
auto name = escape(content(1, data.hash()));
|
||||
state.subtitle = content[0];
|
||||
state.output.append("<h2 id=\"", name, "\">", data);
|
||||
for(auto& line : lines) {
|
||||
if(!line.beginsWith("# ")) continue;
|
||||
state.output.append("<span>", line.trimLeft("# ", 1L), "</span>");
|
||||
}
|
||||
state.output.append("</h2>\n");
|
||||
}
|
||||
|
||||
//header
|
||||
else if(auto depth = count(block, '=')) {
|
||||
else if(auto depth = count(block, '#')) {
|
||||
auto content = slice(lines.takeLeft(), depth + 1).split("::", 1L).strip();
|
||||
auto data = markup(content[0]);
|
||||
auto name = escape(content(1, data.hash()));
|
||||
if(depth <= 4) {
|
||||
state.output.append("<h", depth + 2, " id=\"", name, "\">", data);
|
||||
if(depth <= 5) {
|
||||
state.output.append("<h", depth + 1, " id=\"", name, "\">", data);
|
||||
for(auto& line : lines) {
|
||||
if(count(line, '=') != depth) continue;
|
||||
if(count(line, '#') != depth) continue;
|
||||
state.output.append("<span>", slice(line, depth + 1), "</span>");
|
||||
}
|
||||
state.output.append("</h", depth + 2, ">\n");
|
||||
state.output.append("</h", depth + 1, ">\n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -239,6 +208,29 @@ inline auto DML::count(const string& text, char value) -> uint {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// . => domain
|
||||
// ./* => domain/*
|
||||
// ../subdomain => subdomain.domain
|
||||
// ../subdomain/* => subdomain.domain/*
|
||||
inline auto DML::address(string s) -> string {
|
||||
if(s.beginsWith("../")) {
|
||||
s.trimLeft("../", 1L);
|
||||
if(auto p = s.find("/")) {
|
||||
return {"//", s.slice(0, *p), ".", settings.host, s.slice(*p)};
|
||||
} else {
|
||||
return {"//", s, ".", settings.host};
|
||||
}
|
||||
}
|
||||
if(s.beginsWith("./")) {
|
||||
s.trimLeft(".", 1L);
|
||||
return {"//", settings.host, s};
|
||||
}
|
||||
if(s == ".") {
|
||||
return {"//", settings.host};
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
inline auto DML::escape(const string& text) -> string {
|
||||
string output;
|
||||
for(auto c : text) {
|
||||
@@ -281,8 +273,8 @@ inline auto DML::markup(const string& s) -> string {
|
||||
|
||||
if(link && !image && a == ']' && b == ']') {
|
||||
auto list = slice(s, link(), n - link()).split("::", 1L);
|
||||
string uri = list.last();
|
||||
string name = list.size() == 2 ? list.first() : list.last().split("//", 1L).last();
|
||||
string uri = address(list.last());
|
||||
string name = list.size() == 2 ? list.first() : uri.split("//", 1L).last();
|
||||
|
||||
t.append("<a href=\"", escape(uri), "\">", escape(name), "</a>");
|
||||
|
||||
@@ -294,8 +286,8 @@ inline auto DML::markup(const string& s) -> string {
|
||||
if(image && !link && a == '}' && b == '}') {
|
||||
auto side = slice(s, image(), n - image()).split("}{", 1L);
|
||||
auto list = side(0).split("::", 1L);
|
||||
string uri = list.last();
|
||||
string name = list.size() == 2 ? list.first() : list.last().split("//", 1L).last();
|
||||
string uri = address(list.last());
|
||||
string name = list.size() == 2 ? list.first() : uri.split("//", 1L).last();
|
||||
list = side(1).split("; ");
|
||||
boolean link, title, caption;
|
||||
string width, height;
|
||||
@@ -322,7 +314,6 @@ inline auto DML::markup(const string& s) -> string {
|
||||
if(link) t.append("<a href=\"", escape(uri), "\">");
|
||||
t.append("<img loading=\"lazy\" src=\"", escape(uri), "\" alt=\"", escape(name ? name : uri.hash()), "\"");
|
||||
if(title) t.append(" title=\"", escape(name), "\"");
|
||||
if(width && height) t.append(" style=\"width: ", escape(width), "px; max-height: ", escape(height), "px;\"");
|
||||
if(width) t.append(" width=\"", escape(width), "\"");
|
||||
if(height) t.append(" height=\"", escape(height), "\"");
|
||||
t.append(">");
|
||||
|
@@ -95,7 +95,7 @@ auto slice(string_view self, int offset, int length) -> string {
|
||||
return result;
|
||||
}
|
||||
|
||||
auto string::slice(int offset, int length) -> string {
|
||||
auto string::slice(int offset, int length) const -> string {
|
||||
return nall::slice(*this, offset, length);
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user