#include #include #include #include #include #include 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::max()) { return false; } output = static_cast(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; }