110 lines
3.7 KiB
C++
110 lines
3.7 KiB
C++
#include <algorithm>
|
|
#include <charconv>
|
|
#include <cstring>
|
|
#include <limits>
|
|
#include <system_error>
|
|
|
|
#include <vamp_pico/http_client.hpp>
|
|
|
|
|
|
namespace {
|
|
|
|
auto copy_value(std::string_view value, std::char_t *output, std::size_t capacity) -> void {
|
|
if (capacity == 0U) {
|
|
return;
|
|
}
|
|
auto count = std::min(capacity - 1U, value.size());
|
|
std::memcpy(output, value.data(), count);
|
|
output[count] = '\0';
|
|
}
|
|
|
|
auto parse_uint(std::string_view value, std::uint32_t &output) -> std::bool_t {
|
|
if (value.empty()) {
|
|
return false;
|
|
}
|
|
auto parsed = std::uint64_t{};
|
|
auto const *begin = value.data();
|
|
auto const *end = value.data() + value.size();
|
|
auto result = std::from_chars(begin, end, parsed, 10);
|
|
if (result.ec != std::errc{} || result.ptr != end || parsed > std::numeric_limits<std::uint32_t>::max()) {
|
|
return false;
|
|
}
|
|
output = static_cast<std::uint32_t>(parsed);
|
|
return true;
|
|
}
|
|
|
|
auto strip_cr(std::string_view line) -> std::string_view {
|
|
if (!line.empty() && line.back() == '\r') {
|
|
line.remove_suffix(1U);
|
|
}
|
|
return line;
|
|
}
|
|
|
|
}
|
|
|
|
auto vamp_pico::parse_remote_display_body(std::string_view body, vamp_pico::remote_display_state &state) -> std::bool_t {
|
|
auto parsed = vamp_pico::remote_display_state{};
|
|
auto cursor = std::size_t{};
|
|
while (cursor <= body.size()) {
|
|
auto line_end = body.find('\n', cursor);
|
|
auto line = line_end == std::string_view::npos ? body.substr(cursor) : body.substr(cursor, line_end - cursor);
|
|
line = strip_cr(line);
|
|
auto equals = line.find('=');
|
|
if (equals != std::string_view::npos) {
|
|
auto key = line.substr(0U, equals);
|
|
auto value = line.substr(equals + 1U);
|
|
if (key == "active") {
|
|
parsed.active = value == "1";
|
|
} else if (key == "title") {
|
|
copy_value(value, parsed.title, sizeof(parsed.title));
|
|
} else if (key == "title_image") {
|
|
if (!value.empty() && !vamp_protocol::valid_title_id(value)) {
|
|
return false;
|
|
}
|
|
copy_value(value, parsed.title_image.data(), parsed.title_image.size());
|
|
} else if (key == "part") {
|
|
copy_value(value, parsed.part, sizeof(parsed.part));
|
|
} else if (key == "mode") {
|
|
copy_value(value, parsed.mode, sizeof(parsed.mode));
|
|
} else if (key == "focus") {
|
|
if (value.size() >= sizeof(parsed.focus)) {
|
|
return false;
|
|
}
|
|
copy_value(value, parsed.focus, sizeof(parsed.focus));
|
|
} else if (key == "phase") {
|
|
if (value.size() >= parsed.phase.size()) {
|
|
return false;
|
|
}
|
|
std::copy(value.begin(), value.end(), parsed.phase.begin());
|
|
} else if (key == "backing" || key == "metronome" || key == "editing" || key == "error" || key == "backing_available") {
|
|
if (value != "0" && value != "1") {
|
|
return false;
|
|
}
|
|
auto &flag = key == "backing_available" ? parsed.backing_available : key == "error" ? parsed.error
|
|
: key == "backing" ? parsed.backing
|
|
: key == "metronome" ? parsed.metronome
|
|
: parsed.editing;
|
|
flag = value == "1";
|
|
} else if (key == "bpm") {
|
|
if (!parse_uint(value, parsed.bpm) || parsed.bpm == 0U || parsed.bpm > 400U) {
|
|
return false;
|
|
}
|
|
} else if (key == "page") {
|
|
if (!parse_uint(value, parsed.page)) {
|
|
return false;
|
|
}
|
|
} else if (key == "pages") {
|
|
if (!parse_uint(value, parsed.pages)) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
if (line_end == std::string_view::npos) {
|
|
break;
|
|
}
|
|
cursor = line_end + 1U;
|
|
}
|
|
state = parsed;
|
|
return true;
|
|
}
|