154 lines
4.0 KiB
C++
154 lines
4.0 KiB
C++
#pragma once
|
|
|
|
#include <algorithm>
|
|
#include <array>
|
|
#include <cstdint>
|
|
#include <charconv>
|
|
#include <string_view>
|
|
|
|
#include <vamp_protocol/page_control.hpp>
|
|
|
|
|
|
namespace vamp_pico {
|
|
|
|
constexpr auto digit_width = std::size_t{22};
|
|
constexpr auto max_pages = std::uint32_t{10000};
|
|
|
|
inline auto counter_text(std::uint32_t page, std::uint32_t pages) -> std::array<std::char_t, 12> {
|
|
auto text = std::array<std::char_t, 12>{};
|
|
if (pages == 0U || pages > max_pages || page == 0U || page > pages) {
|
|
return text;
|
|
}
|
|
auto first = std::to_chars(text.data(), text.data() + text.size() - 1U, page);
|
|
*first.ptr = '/';
|
|
std::to_chars(first.ptr + 1U, text.data() + text.size() - 1U, pages);
|
|
return text;
|
|
}
|
|
|
|
enum class sprite : std::size_t {
|
|
VAMP,
|
|
CONNECTING,
|
|
OFFLINE,
|
|
BUSY,
|
|
READY,
|
|
LOADING,
|
|
WIFI,
|
|
ERROR,
|
|
PAGE,
|
|
ACOUSTIC,
|
|
ELECTRIC,
|
|
VOCAL,
|
|
UNAVAILABLE,
|
|
MAIN_MENU,
|
|
PERFORMANCE,
|
|
SETTINGS,
|
|
BACKING,
|
|
METRONOME,
|
|
TEMPO,
|
|
BACK,
|
|
ON,
|
|
OFF,
|
|
BPM,
|
|
MANUAL,
|
|
PREPARING,
|
|
COUNT_IN,
|
|
PLAYING,
|
|
SUSTAIN,
|
|
HOLD_TO_EXIT,
|
|
DIGIT_0,
|
|
DIGIT_1,
|
|
DIGIT_2,
|
|
DIGIT_3,
|
|
DIGIT_4,
|
|
DIGIT_5,
|
|
DIGIT_6,
|
|
DIGIT_7,
|
|
DIGIT_8,
|
|
DIGIT_9,
|
|
SLASH,
|
|
COUNT,
|
|
};
|
|
|
|
struct coverage_image {
|
|
std::string_view pixels{};
|
|
std::size_t width{};
|
|
std::size_t height{};
|
|
auto at(std::size_t x, std::size_t y) const -> std::uint8_t {
|
|
if (x >= width || y >= height || (y * width + x) / 2U >= pixels.size()) {
|
|
return 0U;
|
|
}
|
|
auto index = y * width + x;
|
|
auto packed = static_cast<std::uint8_t>(pixels[index / 2U]);
|
|
return (index & 1U) == 0U ? packed >> 4U : packed & 15U;
|
|
}
|
|
};
|
|
|
|
inline auto static_sprite(std::string_view atlas, sprite value) -> coverage_image {
|
|
constexpr auto count = static_cast<std::size_t>(sprite::COUNT);
|
|
auto index = static_cast<std::size_t>(value);
|
|
if (atlas.size() < 8U + count * 8U || atlas.substr(0U, 4U) != "VPSA" ||
|
|
static_cast<std::uint8_t>(atlas[4]) != count || atlas[5] != 0 || atlas[6] != 0 || atlas[7] != 0 || index >= count) {
|
|
return {};
|
|
}
|
|
auto word = [&](std::size_t offset) -> std::size_t {
|
|
return static_cast<std::uint8_t>(atlas[offset]) | (static_cast<std::size_t>(static_cast<std::uint8_t>(atlas[offset + 1U])) << 8U);
|
|
};
|
|
auto entry = 8U + index * 8U;
|
|
auto width = word(entry);
|
|
auto height = word(entry + 2U);
|
|
auto offset = word(entry + 4U) | (word(entry + 6U) << 16U);
|
|
auto size = (width * height + 1U) / 2U;
|
|
if (width == 0U || width > 176U || height != 28U || offset < 8U + count * 8U || offset > atlas.size() || size > atlas.size() - offset) {
|
|
return {};
|
|
}
|
|
return {atlas.substr(offset, size), width, height};
|
|
}
|
|
|
|
class title_cache {
|
|
public:
|
|
auto select(std::string_view identifier) -> std::bool_t {
|
|
if (identifier == identifier_.data()) {
|
|
return false;
|
|
}
|
|
identifier_.fill(0);
|
|
if (vamp_protocol::valid_title_id(identifier)) {
|
|
std::copy(identifier.begin(), identifier.end(), identifier_.begin());
|
|
}
|
|
ready_ = false;
|
|
failed_ = false;
|
|
return true;
|
|
}
|
|
auto accept(std::string_view identifier, std::string_view bytes) -> std::bool_t {
|
|
if (identifier != identifier_.data() || !vamp_protocol::valid_title_id(identifier) || !vamp_protocol::valid_title_image(bytes)) {
|
|
return false;
|
|
}
|
|
std::copy(bytes.begin() + vamp_protocol::title_header_size, bytes.end(), pixels_.begin());
|
|
ready_ = true;
|
|
failed_ = false;
|
|
return true;
|
|
}
|
|
auto fail(std::string_view identifier) -> std::bool_t {
|
|
if (identifier != identifier_.data() || ready_) {
|
|
return false;
|
|
}
|
|
failed_ = true;
|
|
return true;
|
|
}
|
|
auto ready() const -> std::bool_t {
|
|
return ready_;
|
|
}
|
|
auto failed() const -> std::bool_t {
|
|
return failed_;
|
|
}
|
|
auto image() const -> coverage_image {
|
|
return ready_ ? coverage_image{{pixels_.data(), pixels_.size()}, vamp_protocol::title_width, vamp_protocol::title_height} : coverage_image{};
|
|
}
|
|
private:
|
|
std::array<std::char_t, vamp_protocol::title_id_size + 1U> identifier_{};
|
|
std::array<std::char_t, vamp_protocol::title_width * vamp_protocol::title_height / 2U> pixels_{};
|
|
std::bool_t ready_{};
|
|
std::bool_t failed_{};
|
|
};
|
|
|
|
}
|