C++: add JSON serialization and parsing support

backup
Christian Seiler 2 weeks ago
parent ecf835175c
commit 5e66961854
  1. 5
      cpp/src/CMakeLists.txt
  2. 8
      cpp/src/monoformat_bithelpers.hpp
  3. 2
      cpp/src/monoformat_schema.hpp
  4. 629
      cpp/src/monoformat_structured.cpp
  5. 28
      cpp/src/monoformat_structured.hpp

@ -15,6 +15,8 @@ set(monoformat_HEADERS
option(MONOFORMAT_EMBEDDED "Only build for embedded devices" FALSE)
if(NOT MONOFORMAT_EMBEDDED)
find_package(nlohmann_json REQUIRED)
list(APPEND monoformat_SOURCES
monoformat_structured.cpp
)
@ -28,6 +30,9 @@ add_library(monoformat ${monoformat_SOURCES} ${monoformat_HEADERS})
target_compile_features(monoformat PUBLIC cxx_std_23)
target_include_directories(monoformat PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
if(NOT MONOFORMAT_EMBEDDED)
target_link_libraries(monoformat PUBLIC nlohmann_json::nlohmann_json)
endif()
install(TARGETS monoformat
RUNTIME DESTINATION bin

@ -10,7 +10,7 @@ namespace monoformat {
template<std::integral Int, std::integral OtherInt>
constexpr inline bool isBitSet(Int value, OtherInt bit) {
if (bit < 0 || bit >= (sizeof(Int) * CHAR_BIT)) [[unlikely]] {
if (bit < 0 || bit >= static_cast<Int>(sizeof(Int) * CHAR_BIT)) [[unlikely]] {
return false;
}
@ -25,7 +25,7 @@ constexpr inline bool isBitSet(std::byte value, OtherInt bit) {
template<std::integral Int, std::integral OtherInt>
constexpr inline void setBit(Int& value, OtherInt bit) {
if (bit < 0 || bit >= (sizeof(Int) * CHAR_BIT)) [[unlikely]] {
if (bit < 0 || bit >= static_cast<Int>(sizeof(Int) * CHAR_BIT)) [[unlikely]] {
return;
}
@ -45,7 +45,7 @@ constexpr inline void setBit(std::byte& value, OtherInt bit) {
template<std::integral Int, std::integral OtherInt>
constexpr inline void unsetBit(Int& value, OtherInt bit) {
if (bit < 0 || bit >= (sizeof(Int) * CHAR_BIT)) [[unlikely]] {
if (bit < 0 || bit >= static_cast<Int>(sizeof(Int) * CHAR_BIT)) [[unlikely]] {
return;
}
@ -65,7 +65,7 @@ constexpr inline void unsetBit(std::byte& value, OtherInt bit) {
template<std::integral Int, std::integral OtherInt>
constexpr inline void maybeSetBit(Int& value, OtherInt bit, bool set) {
if (bit < 0 || bit >= (sizeof(Int) * CHAR_BIT)) [[unlikely]] {
if (bit < 0 || bit >= static_cast<Int>(sizeof(Int) * CHAR_BIT)) [[unlikely]] {
return;
}

@ -6,6 +6,7 @@
#include <cstddef>
#include <cstdint>
#include <span>
#include <tuple>
namespace monoformat {
@ -62,6 +63,7 @@ struct LineFlags {
explicit LineFlags(std::uint8_t flags) noexcept
{
std::ignore = flags;
}
explicit operator std::uint8_t() const noexcept {

@ -3,9 +3,51 @@
#include "monoformat_bithelpers.hpp"
#include <iostream>
#include <stdexcept>
#include <format>
namespace monoformat {
static std::string base64Encode(std::span<std::byte const> buffer);
static std::vector<std::byte> base64Decode(std::string_view data);
NLOHMANN_JSON_SERIALIZE_ENUM(SectionType, {
{SectionType::AlwaysDrawn, "AlwaysDrawn"},
{SectionType::TimeBasedDrawn, "TimeBasedDrawn"},
{SectionType::CustomFont, "CustomFont"},
})
NLOHMANN_JSON_SERIALIZE_ENUM(ElementType, {
{ElementType::Image, "Image"},
{ElementType::Animation, "Animation"},
{ElementType::HScrollImage, "HScrollImage"},
{ElementType::VScrollImage, "VScrollImage"},
{ElementType::Line, "Line"},
{ElementType::ClippedText, "ClippedText"},
{ElementType::HScrollText, "HScrollText"},
{ElementType::CurrentTime, "CurrentTime"},
})
NLOHMANN_JSON_SERIALIZE_ENUM(LineStyle, {
{LineStyle::Solid, "Solid"},
})
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(ScrollFlags, endless, invertDirection, padBefore, padAfter)
// Don't use NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT here because our struct is empty
static void from_json(nlohmann::json const& j, LineFlags& v) {
if (!j.is_object() || j.size() > 0) {
throw std::runtime_error("Cannot deserialize a LineFlags that is not an empty object");
}
v = {};
}
static void to_json(nlohmann::json& j, LineFlags const& v) {
std::ignore = v;
j = nlohmann::json::object();
}
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(TimeDisplayFlags, use12h, showHours, showMinutes, showSeconds)
ImageElement::ImageElement(std::uint16_t x, std::uint16_t y, std::uint16_t width, std::uint16_t height)
: m_x{x}
, m_y{y}
@ -66,6 +108,8 @@ std::uint32_t ImageElement::minimumFormatVersion() const {
}
std::size_t ImageElement::serializeTo(std::span<std::byte> target, std::uint32_t formatVersion) const {
std::ignore = formatVersion;
std::size_t pos = 0;
pos = writeU16LE(target, pos, static_cast<std::uint16_t>(ElementType::Image));
pos = writeU16LE(target, pos, m_x);
@ -77,6 +121,27 @@ std::size_t ImageElement::serializeTo(std::span<std::byte> target, std::uint32_t
return alignNextWrite(target, pos, 4);
}
nlohmann::json ImageElement::serializeJSON(std::uint32_t formatVersion) const {
std::ignore = formatVersion;
nlohmann::json result;
result["type"] = "Image";
result["x"] = m_x;
result["y"] = m_y;
result["width"] = m_width;
result["height"] = m_height;
result["image"] = nlohmann::json::array();
auto ownImage = image();
for (std::uint16_t dy = 0; dy < m_height; ++dy) {
for (std::uint16_t dx = 0; dx < m_width; ++dx) {
result["image"].push_back(ownImage.isPixelSet(dx, dy) ? 1 : 0);
}
}
return result;
}
void ImageElement::drawTo(OneBitBufferInterface* imageBuffer, std::size_t animationTick, std::int64_t currentTimestamp, std::span<CustomFontSection const*> customFonts) {
std::ignore = animationTick;
std::ignore = currentTimestamp;
@ -134,6 +199,28 @@ std::expected<std::unique_ptr<ImageElement>, ParseError> ImageElement::parse(std
return result;
}
std::unique_ptr<ImageElement> ImageElement::parseJSON(nlohmann::json const& j, std::uint32_t formatVersion) {
std::ignore = formatVersion;
std::uint16_t x = j.at("x");
std::uint16_t y = j.at("y");
std::uint16_t width = j.at("width");
std::uint16_t height = j.at("height");
std::vector<int> pixels = j.at("image");
if (pixels.size() != width * height) {
throw std::runtime_error(std::format("Error unserializing an Image element: the width, {:d}, and height {:d}, of the image don't match the number of pixels in the image data, {:d}, expected {:d} instead", width, height, pixels.size(), width * height));
}
auto result = std::make_unique<ImageElement>(x, y, width, height);
auto image = result->image();
for (std::uint16_t dy = 0; dy < height; ++dy) {
std::size_t base = static_cast<std::size_t>(dy) * width;
for (std::uint16_t dx = 0; dx < width; ++dx) {
image.setPixel(dx, dy, !!pixels[base + dx]);
}
}
return result;
}
AnimationElement::AnimationElement(std::uint16_t x, std::uint16_t y, std::uint16_t width, std::uint16_t height, std::uint16_t numberOfFrames, std::uint16_t updateInterval)
: m_x{x}
, m_y{y}
@ -220,6 +307,8 @@ std::uint32_t AnimationElement::minimumFormatVersion() const {
}
std::size_t AnimationElement::serializeTo(std::span<std::byte> target, std::uint32_t formatVersion) const {
std::ignore = formatVersion;
std::size_t pos = 0;
pos = writeU16LE(target, pos, static_cast<std::uint16_t>(ElementType::Animation));
pos = writeU16LE(target, pos, m_x);
@ -233,6 +322,32 @@ std::size_t AnimationElement::serializeTo(std::span<std::byte> target, std::uint
return alignNextWrite(target, pos, 4);
}
nlohmann::json AnimationElement::serializeJSON(std::uint32_t formatVersion) const {
std::ignore = formatVersion;
nlohmann::json result;
result["type"] = "Animation";
result["x"] = m_x;
result["y"] = m_y;
result["width"] = m_width;
result["height"] = m_height;
result["updateInterval"] = m_updateInterval;
result["frames"] = nlohmann::json::array();
for (std::uint16_t f = 0; f < m_numberOfFrames; ++f) {
nlohmann::json frame = nlohmann::json::array();
auto ownImage = image(f);
for (std::uint16_t dy = 0; dy < m_height; ++dy) {
for (std::uint16_t dx = 0; dx < m_width; ++dx) {
frame.push_back(ownImage.isPixelSet(dx, dy) ? 1 : 0);
}
}
result["frames"].push_back(frame);
}
return result;
}
void AnimationElement::drawTo(OneBitBufferInterface* imageBuffer, std::size_t animationTick, std::int64_t currentTimestamp, std::span<CustomFontSection const*> customFonts) {
std::ignore = currentTimestamp;
std::ignore = customFonts;
@ -305,6 +420,38 @@ std::expected<std::unique_ptr<AnimationElement>, ParseError> AnimationElement::p
return result;
}
std::unique_ptr<AnimationElement> AnimationElement::parseJSON(nlohmann::json const& j, std::uint32_t formatVersion) {
std::ignore = formatVersion;
std::uint16_t x = j.at("x");
std::uint16_t y = j.at("y");
std::uint16_t width = j.at("width");
std::uint16_t height = j.at("height");
std::uint16_t updateInterval = j.at("updateInterval");
std::vector<std::vector<int>> frames = j.at("frames");
if (frames.size() > std::numeric_limits<std::uint16_t>::max()) {
throw std::runtime_error(std::format("Error unserializing an Animation element: the number of frames, {:d}, is too large", frames.size()));
}
for (std::size_t i = 0; i < frames.size(); ++i) {
if (frames[i].size() != width * height) {
throw std::runtime_error(std::format("Error unserializing an Animation element: in frame {:d}, the width, {:d}, and height {:d}, of the image don't match the number of pixels in the image data, {:d}, expected {:d} instead", i, width, height, frames[i].size(), width * height));
}
}
auto result = std::make_unique<AnimationElement>(x, y, width, height, static_cast<std::uint16_t>(frames.size()), updateInterval);
for (std::size_t i = 0; i < frames.size(); ++i) {
auto image = result->image(i);
for (std::uint16_t dy = 0; dy < height; ++dy) {
std::size_t base = static_cast<std::size_t>(dy) * width;
for (std::uint16_t dx = 0; dx < width; ++dx) {
image.setPixel(dx, dy, !!frames[i][base + dx]);
}
}
}
return result;
}
HScrollImageElement::HScrollImageElement(std::uint16_t x, std::uint16_t y, std::uint16_t width, std::uint16_t height, std::uint16_t contentWidth, ScrollFlags flags, std::uint8_t scrollSpeed)
: m_x{x}
, m_y{y}
@ -380,6 +527,8 @@ std::uint32_t HScrollImageElement::minimumFormatVersion() const {
}
std::size_t HScrollImageElement::serializeTo(std::span<std::byte> target, std::uint32_t formatVersion) const {
std::ignore = formatVersion;
std::size_t pos = 0;
pos = writeU16LE(target, pos, static_cast<std::uint16_t>(ElementType::HScrollImage));
pos = writeU16LE(target, pos, m_x);
@ -394,6 +543,30 @@ std::size_t HScrollImageElement::serializeTo(std::span<std::byte> target, std::u
return alignNextWrite(target, pos, 4);
}
nlohmann::json HScrollImageElement::serializeJSON(std::uint32_t formatVersion) const {
std::ignore = formatVersion;
nlohmann::json result;
result["type"] = "HScrollImage";
result["x"] = m_x;
result["y"] = m_y;
result["width"] = m_width;
result["height"] = m_height;
result["contentWidth"] = m_contentWidth;
result["flags"] = m_flags;
result["scrollSpeed"] = int(m_scrollSpeed);
result["image"] = nlohmann::json::array();
auto ownImage = image();
for (std::uint16_t dy = 0; dy < m_height; ++dy) {
for (std::uint16_t dx = 0; dx < m_contentWidth; ++dx) {
result["image"].push_back(ownImage.isPixelSet(dx, dy) ? 1 : 0);
}
}
return result;
}
void HScrollImageElement::drawTo(OneBitBufferInterface* imageBuffer, std::size_t animationTick, std::int64_t currentTimestamp, std::span<CustomFontSection const*> customFonts) {
std::ignore = currentTimestamp;
std::ignore = customFonts;
@ -555,6 +728,35 @@ std::expected<std::unique_ptr<HScrollImageElement>, ParseError> HScrollImageElem
return result;
}
std::unique_ptr<HScrollImageElement> HScrollImageElement::parseJSON(nlohmann::json const& j, std::uint32_t formatVersion) {
std::ignore = formatVersion;
std::uint16_t x = j.at("x");
std::uint16_t y = j.at("y");
std::uint16_t width = j.at("width");
std::uint16_t height = j.at("height");
std::uint16_t contentWidth = j.at("contentWidth");
ScrollFlags flags = j.at("flags");
int scrollSpeed = j.at("scrollSpeed");
std::vector<int> pixels = j.at("image");
if (scrollSpeed < 0 || scrollSpeed > 255) {
throw std::runtime_error(std::format("Error unserializing an HScrollImage element: invalid scroll speed {:d} specified", scrollSpeed));
}
if (pixels.size() != contentWidth * height) {
throw std::runtime_error(std::format("Error unserializing an HScrollImage element: the content width, {:d}, and height {:d}, of the image don't match the number of pixels in the image data, {:d}, expected {:d} instead", contentWidth, height, pixels.size(), contentWidth * height));
}
auto result = std::make_unique<HScrollImageElement>(x, y, width, height, contentWidth, flags, static_cast<std::uint8_t>(scrollSpeed));
auto image = result->image();
for (std::uint16_t dy = 0; dy < height; ++dy) {
std::size_t base = static_cast<std::size_t>(dy) * contentWidth;
for (std::uint16_t dx = 0; dx < contentWidth; ++dx) {
image.setPixel(dx, dy, !!pixels[base + dx]);
}
}
return result;
}
VScrollImageElement::VScrollImageElement(std::uint16_t x, std::uint16_t y, std::uint16_t width, std::uint16_t height, std::uint16_t contentHeight, ScrollFlags flags, std::uint8_t scrollSpeed)
: m_x{x}
, m_y{y}
@ -630,6 +832,8 @@ std::uint32_t VScrollImageElement::minimumFormatVersion() const {
}
std::size_t VScrollImageElement::serializeTo(std::span<std::byte> target, std::uint32_t formatVersion) const {
std::ignore = formatVersion;
std::size_t pos = 0;
pos = writeU16LE(target, pos, static_cast<std::uint16_t>(ElementType::VScrollImage));
pos = writeU16LE(target, pos, m_x);
@ -644,6 +848,30 @@ std::size_t VScrollImageElement::serializeTo(std::span<std::byte> target, std::u
return alignNextWrite(target, pos, 4);
}
nlohmann::json VScrollImageElement::serializeJSON(std::uint32_t formatVersion) const {
std::ignore = formatVersion;
nlohmann::json result;
result["type"] = "VScrollImage";
result["x"] = m_x;
result["y"] = m_y;
result["width"] = m_width;
result["height"] = m_height;
result["contentHeight"] = m_contentHeight;
result["flags"] = m_flags;
result["scrollSpeed"] = int(m_scrollSpeed);
result["image"] = nlohmann::json::array();
auto ownImage = image();
for (std::uint16_t dy = 0; dy < m_contentHeight; ++dy) {
for (std::uint16_t dx = 0; dx < m_width; ++dx) {
result["image"].push_back(ownImage.isPixelSet(dx, dy) ? 1 : 0);
}
}
return result;
}
void VScrollImageElement::drawTo(OneBitBufferInterface* imageBuffer, std::size_t animationTick, std::int64_t currentTimestamp, std::span<CustomFontSection const*> customFonts) {
std::ignore = currentTimestamp;
std::ignore = customFonts;
@ -805,6 +1033,34 @@ std::expected<std::unique_ptr<VScrollImageElement>, ParseError> VScrollImageElem
return result;
}
std::unique_ptr<VScrollImageElement> VScrollImageElement::parseJSON(nlohmann::json const& j, std::uint32_t formatVersion) {
std::ignore = formatVersion;
std::uint16_t x = j.at("x");
std::uint16_t y = j.at("y");
std::uint16_t width = j.at("width");
std::uint16_t height = j.at("height");
std::uint16_t contentHeight = j.at("contentHeight");
ScrollFlags flags = j.at("flags");
int scrollSpeed = j.at("scrollSpeed");
std::vector<int> pixels = j.at("image");
if (scrollSpeed < 0 || scrollSpeed > 255) {
throw std::runtime_error(std::format("Error unserializing an VScrollImage element: invalid scroll speed {:d} specified", scrollSpeed));
}
if (pixels.size() != width * contentHeight) {
throw std::runtime_error(std::format("Error unserializing an VScrollImage element: the width, {:d}, and content height {:d}, of the image don't match the number of pixels in the image data, {:d}, expected {:d} instead", width, contentHeight, pixels.size(), width * contentHeight));
}
auto result = std::make_unique<VScrollImageElement>(x, y, width, height, contentHeight, flags, static_cast<std::uint8_t>(scrollSpeed));
auto image = result->image();
for (std::uint16_t dy = 0; dy < contentHeight; ++dy) {
std::size_t base = static_cast<std::size_t>(dy) * width;
for (std::uint16_t dx = 0; dx < width; ++dx) {
image.setPixel(dx, dy, !!pixels[base + dx]);
}
}
return result;
}
LineElement::LineElement(std::uint16_t originX, std::uint16_t originY, std::uint16_t targetX, std::uint16_t targetY, LineStyle lineStyle, LineFlags flags)
: m_originX{originX}
@ -852,6 +1108,8 @@ std::uint32_t LineElement::minimumFormatVersion() const {
}
std::size_t LineElement::serializeTo(std::span<std::byte> target, std::uint32_t formatVersion) const {
std::ignore = formatVersion;
std::size_t pos = 0;
pos = writeU16LE(target, pos, static_cast<std::uint16_t>(ElementType::Line));
pos = writeU16LE(target, pos, m_originX);
@ -863,6 +1121,20 @@ std::size_t LineElement::serializeTo(std::span<std::byte> target, std::uint32_t
return alignNextWrite(target, pos, 4);
}
nlohmann::json LineElement::serializeJSON(std::uint32_t formatVersion) const {
std::ignore = formatVersion;
nlohmann::json result;
result["type"] = "Line";
result["originX"] = m_originX;
result["originY"] = m_originY;
result["targetX"] = m_targetX;
result["targetY"] = m_targetY;
result["lineStyle"] = m_lineStyle;
result["flags"] = m_flags;
return result;
}
void LineElement::drawTo(OneBitBufferInterface* imageBuffer, std::size_t animationTick, std::int64_t currentTimestamp, std::span<CustomFontSection const*> customFonts) {
std::ignore = animationTick;
std::ignore = currentTimestamp;
@ -930,6 +1202,20 @@ std::expected<std::unique_ptr<LineElement>, ParseError> LineElement::parse(std::
return result;
}
std::unique_ptr<LineElement> LineElement::parseJSON(nlohmann::json const& j, std::uint32_t formatVersion) {
std::ignore = formatVersion;
std::uint16_t originX = j.at("originX");
std::uint16_t originY = j.at("originY");
std::uint16_t targetX = j.at("targetX");
std::uint16_t targetY = j.at("targetY");
LineStyle lineStyle = j.at("lineStyle");
LineFlags flags = j.at("flags");
auto result = std::make_unique<LineElement>(originX, originY, targetX, targetY, lineStyle, flags);
return result;
}
ClippedTextElement::ClippedTextElement(std::uint16_t x, std::uint16_t y, std::uint16_t width, std::uint16_t height, std::uint16_t fontIndex, std::string text)
: m_x{x}
, m_y{y}
@ -976,6 +1262,8 @@ std::uint32_t ClippedTextElement::minimumFormatVersion() const {
}
std::size_t ClippedTextElement::serializeTo(std::span<std::byte> target, std::uint32_t formatVersion) const {
std::ignore = formatVersion;
std::size_t pos = 0;
pos = writeU16LE(target, pos, static_cast<std::uint16_t>(ElementType::ClippedText));
pos = writeU16LE(target, pos, m_x);
@ -988,6 +1276,20 @@ std::size_t ClippedTextElement::serializeTo(std::span<std::byte> target, std::ui
return alignNextWrite(target, pos, 4);
}
nlohmann::json ClippedTextElement::serializeJSON(std::uint32_t formatVersion) const {
std::ignore = formatVersion;
nlohmann::json result;
result["type"] = "ClippedText";
result["x"] = m_x;
result["y"] = m_y;
result["width"] = m_width;
result["height"] = m_height;
result["fontIndex"] = m_fontIndex;
result["text"] = m_text;
return result;
}
void ClippedTextElement::drawTo(OneBitBufferInterface* imageBuffer, std::size_t animationTick, std::int64_t currentTimestamp, std::span<CustomFontSection const*> customFonts) {
std::ignore = animationTick;
std::ignore = currentTimestamp;
@ -1056,6 +1358,20 @@ std::expected<std::unique_ptr<ClippedTextElement>, ParseError> ClippedTextElemen
return result;
}
std::unique_ptr<ClippedTextElement> ClippedTextElement::parseJSON(nlohmann::json const& j, std::uint32_t formatVersion) {
std::ignore = formatVersion;
std::uint16_t x = j.at("x");
std::uint16_t y = j.at("y");
std::uint16_t width = j.at("width");
std::uint16_t height = j.at("height");
std::uint16_t fontIndex = j.at("fontIndex");
std::string text = j.at("text");
auto result = std::make_unique<ClippedTextElement>(x, y, width, height, fontIndex, std::move(text));
return result;
}
HScrollTextElement::HScrollTextElement(std::uint16_t x, std::uint16_t y, std::uint16_t width, std::uint16_t height, ScrollFlags flags, std::uint8_t scrollSpeed, std::uint16_t fontIndex, std::string text)
: m_x{x}
, m_y{y}
@ -1112,6 +1428,8 @@ std::uint32_t HScrollTextElement::minimumFormatVersion() const {
}
std::size_t HScrollTextElement::serializeTo(std::span<std::byte> target, std::uint32_t formatVersion) const {
std::ignore = formatVersion;
std::size_t pos = 0;
pos = writeU16LE(target, pos, static_cast<std::uint16_t>(ElementType::HScrollText));
pos = writeU16LE(target, pos, m_x);
@ -1126,6 +1444,22 @@ std::size_t HScrollTextElement::serializeTo(std::span<std::byte> target, std::ui
return alignNextWrite(target, pos, 4);
}
nlohmann::json HScrollTextElement::serializeJSON(std::uint32_t formatVersion) const {
std::ignore = formatVersion;
nlohmann::json result;
result["type"] = "HScrollText";
result["x"] = m_x;
result["y"] = m_y;
result["width"] = m_width;
result["height"] = m_height;
result["flags"] = m_flags;
result["scrollSpeed"] = int(m_scrollSpeed);
result["fontIndex"] = m_fontIndex;
result["text"] = m_text;
return result;
}
void HScrollTextElement::drawTo(OneBitBufferInterface* imageBuffer, std::size_t animationTick, std::int64_t currentTimestamp, std::span<CustomFontSection const*> customFonts) {
std::ignore = currentTimestamp;
@ -1282,6 +1616,27 @@ std::expected<std::unique_ptr<HScrollTextElement>, ParseError> HScrollTextElemen
return result;
}
std::unique_ptr<HScrollTextElement> HScrollTextElement::parseJSON(nlohmann::json const& j, std::uint32_t formatVersion) {
std::ignore = formatVersion;
std::uint16_t x = j.at("x");
std::uint16_t y = j.at("y");
std::uint16_t width = j.at("width");
std::uint16_t height = j.at("height");
ScrollFlags flags = j.at("flags");
int scrollSpeed = j.at("scrollSpeed");
std::uint16_t fontIndex = j.at("fontIndex");
std::string text = j.at("text");
if (scrollSpeed < 0 || scrollSpeed > 255) {
throw std::runtime_error(std::format("Error unserializing an HScrollText element: invalid scroll speed {:d} specified", scrollSpeed));
}
auto result = std::make_unique<HScrollTextElement>(x, y, width, height, flags, static_cast<std::uint8_t>(scrollSpeed), fontIndex, std::move(text));
return result;
}
CurrentTimeElement::CurrentTimeElement(std::uint16_t x, std::uint16_t y, std::uint16_t width, std::uint16_t height, std::uint16_t fontIndex, std::uint16_t utcOffset, TimeDisplayFlags flags)
: m_x{x}
, m_y{y}
@ -1333,6 +1688,8 @@ std::uint32_t CurrentTimeElement::minimumFormatVersion() const {
}
std::size_t CurrentTimeElement::serializeTo(std::span<std::byte> target, std::uint32_t formatVersion) const {
std::ignore = formatVersion;
std::size_t pos = 0;
pos = writeU16LE(target, pos, static_cast<std::uint16_t>(ElementType::CurrentTime));
pos = writeU16LE(target, pos, m_x);
@ -1345,6 +1702,21 @@ std::size_t CurrentTimeElement::serializeTo(std::span<std::byte> target, std::ui
return alignNextWrite(target, pos, 4);
}
nlohmann::json CurrentTimeElement::serializeJSON(std::uint32_t formatVersion) const {
std::ignore = formatVersion;
nlohmann::json result;
result["type"] = "CurrentTime";
result["x"] = m_x;
result["y"] = m_y;
result["width"] = m_width;
result["height"] = m_height;
result["fontIndex"] = m_fontIndex;
result["utcOffset"] = m_utcOffset;
result["flags"] = m_flags;
return result;
}
void CurrentTimeElement::drawTo(OneBitBufferInterface* imageBuffer, std::size_t animationTick, std::int64_t currentTimestamp, std::span<CustomFontSection const*> customFonts) {
std::ignore = animationTick;
@ -1444,6 +1816,21 @@ std::expected<std::unique_ptr<CurrentTimeElement>, ParseError> CurrentTimeElemen
return result;
}
std::unique_ptr<CurrentTimeElement> CurrentTimeElement::parseJSON(nlohmann::json const& j, std::uint32_t formatVersion) {
std::ignore = formatVersion;
std::uint16_t x = j.at("x");
std::uint16_t y = j.at("y");
std::uint16_t width = j.at("width");
std::uint16_t height = j.at("height");
std::uint16_t fontIndex = j.at("fontIndex");
std::uint16_t utcOffset = j.at("utcOffset");
TimeDisplayFlags flags = j.at("flags");
auto result = std::make_unique<CurrentTimeElement>(x, y, width, height, fontIndex, utcOffset, flags);
return result;
}
AlwaysDrawnSection::~AlwaysDrawnSection() {
}
@ -1548,9 +1935,22 @@ std::size_t AlwaysDrawnSection::serializeTo(std::span<std::byte> target, std::ui
return pos;
}
std::expected<std::unique_ptr<AlwaysDrawnSection>, ParseError> AlwaysDrawnSection::parse(std::span<std::byte const>& buffer, std::uint32_t formatVersion) {
nlohmann::json AlwaysDrawnSection::serializeJSON(std::uint32_t formatVersion) const {
std::ignore = formatVersion;
nlohmann::json result;
result["type"] = "AlwaysDrawn";
result["flags"]["drawOnFront"] = m_drawOnFront;
result["flags"]["drawOnBack"] = m_drawOnBack;
result["flags"]["clearBeforeDrawing"] = m_clearBeforeDrawing;
result["elements"] = nlohmann::json::array();
for (auto const& element : m_elements) {
result["elements"].push_back(element->serializeJSON(formatVersion));
}
return result;
}
std::expected<std::unique_ptr<AlwaysDrawnSection>, ParseError> AlwaysDrawnSection::parse(std::span<std::byte const>& buffer, std::uint32_t formatVersion) {
auto type = readU8LE(buffer);
if (!type) {
return std::unexpected(type.error());
@ -1606,6 +2006,38 @@ std::expected<std::unique_ptr<AlwaysDrawnSection>, ParseError> AlwaysDrawnSectio
return section;
}
std::unique_ptr<AlwaysDrawnSection> AlwaysDrawnSection::parseJSON(nlohmann::json const& j, std::uint32_t formatVersion) {
bool drawOnFront = j.at("flags").at("drawOnFront");
bool drawOnBack = j.at("flags").at("drawOnBack");
bool clearBeforeDrawing = j.at("flags").at("clearBeforeDrawing");
auto section = std::make_unique<AlwaysDrawnSection>();
section->setDrawOnFront(drawOnFront);
section->setDrawOnBack(drawOnBack);
section->setClearBeforeDrawing(clearBeforeDrawing);
std::size_t const elementCount = j.at("elements").size();
for (std::size_t i = 0; i < elementCount; ++i) {
nlohmann::json const& jElement = j.at("elements").at(i);
ElementType type = jElement.at("type");
std::unique_ptr<Element> element;
switch (type) {
case ElementType::Image: element = ImageElement::parseJSON(jElement, formatVersion); break;
case ElementType::Animation: element = AnimationElement::parseJSON(jElement, formatVersion); break;
case ElementType::HScrollImage: element = HScrollImageElement::parseJSON(jElement, formatVersion); break;
case ElementType::VScrollImage: element = VScrollImageElement::parseJSON(jElement, formatVersion); break;
case ElementType::Line: element = LineElement::parseJSON(jElement, formatVersion); break;
case ElementType::ClippedText: element = ClippedTextElement::parseJSON(jElement, formatVersion); break;
case ElementType::HScrollText: element = HScrollTextElement::parseJSON(jElement, formatVersion); break;
case ElementType::CurrentTime: element = CurrentTimeElement::parseJSON(jElement, formatVersion); break;
default: throw std::runtime_error(std::format("Unsupported element type {:s} encountered while parsing a section",
jElement.at("type").get<std::string>()));
}
section->m_elements.push_back(std::move(element));
}
return section;
}
TimeBasedDrawnSection::~TimeBasedDrawnSection() {
}
@ -1728,6 +2160,21 @@ std::size_t TimeBasedDrawnSection::serializeTo(std::span<std::byte> target, std:
return pos;
}
nlohmann::json TimeBasedDrawnSection::serializeJSON(std::uint32_t formatVersion) const {
nlohmann::json result;
result["type"] = "TimeBasedDrawn";
result["flags"]["drawOnFront"] = m_drawOnFront;
result["flags"]["drawOnBack"] = m_drawOnBack;
result["flags"]["clearBeforeDrawing"] = m_clearBeforeDrawing;
result["startTimestamp"] = m_startTimestamp;
result["endTimestamp"] = m_endTimestamp;
result["elements"] = nlohmann::json::array();
for (auto const& element : m_elements) {
result["elements"].push_back(element->serializeJSON(formatVersion));
}
return result;
}
std::expected<std::unique_ptr<TimeBasedDrawnSection>, ParseError> TimeBasedDrawnSection::parse(std::span<std::byte const>& buffer, std::uint32_t formatVersion) {
std::ignore = formatVersion;
@ -1796,6 +2243,42 @@ std::expected<std::unique_ptr<TimeBasedDrawnSection>, ParseError> TimeBasedDrawn
return section;
}
std::unique_ptr<TimeBasedDrawnSection> TimeBasedDrawnSection::parseJSON(nlohmann::json const& j, std::uint32_t formatVersion) {
bool drawOnFront = j.at("flags").at("drawOnFront");
bool drawOnBack = j.at("flags").at("drawOnBack");
bool clearBeforeDrawing = j.at("flags").at("clearBeforeDrawing");
std::uint64_t startTimestamp = j.at("startTimestamp");
std::uint64_t endTimestamp = j.at("endTimestamp");
auto section = std::make_unique<TimeBasedDrawnSection>();
section->setDrawOnFront(drawOnFront);
section->setDrawOnBack(drawOnBack);
section->setClearBeforeDrawing(clearBeforeDrawing);
section->setStartTimestamp(startTimestamp);
section->setEndTimestamp(endTimestamp);
std::size_t const elementCount = j.at("elements").size();
for (std::size_t i = 0; i < elementCount; ++i) {
nlohmann::json const& jElement = j.at("elements").at(i);
ElementType type = jElement.at("type");
std::unique_ptr<Element> element;
switch (type) {
case ElementType::Image: element = ImageElement::parseJSON(jElement, formatVersion); break;
case ElementType::Animation: element = AnimationElement::parseJSON(jElement, formatVersion); break;
case ElementType::HScrollImage: element = HScrollImageElement::parseJSON(jElement, formatVersion); break;
case ElementType::VScrollImage: element = VScrollImageElement::parseJSON(jElement, formatVersion); break;
case ElementType::Line: element = LineElement::parseJSON(jElement, formatVersion); break;
case ElementType::ClippedText: element = ClippedTextElement::parseJSON(jElement, formatVersion); break;
case ElementType::HScrollText: element = HScrollTextElement::parseJSON(jElement, formatVersion); break;
case ElementType::CurrentTime: element = CurrentTimeElement::parseJSON(jElement, formatVersion); break;
default: throw std::runtime_error(std::format("Unsupported element type {:s} encountered while parsing a section",
jElement.at("type").get<std::string>()));
}
section->m_elements.push_back(std::move(element));
}
return section;
}
CustomFontSection::~CustomFontSection() {
}
@ -1817,6 +2300,8 @@ std::uint32_t CustomFontSection::minimumFormatVersion() const {
}
std::size_t CustomFontSection::serializeTo(std::span<std::byte> target, std::uint32_t formatVersion) const {
std::ignore = formatVersion;
std::size_t pos = 0;
pos = writeU8LE(target, pos, static_cast<std::uint16_t>(SectionType::AlwaysDrawn));
// Will be replaced later
@ -1829,6 +2314,15 @@ std::size_t CustomFontSection::serializeTo(std::span<std::byte> target, std::uin
return pos;
}
nlohmann::json CustomFontSection::serializeJSON(std::uint32_t formatVersion) const {
std::ignore = formatVersion;
nlohmann::json result;
result["type"] = "CustomFont";
result["data"] = base64Encode(m_fontData);
return result;
}
std::expected<std::unique_ptr<CustomFontSection>, ParseError> CustomFontSection::parse(std::span<std::byte const>& buffer, std::uint32_t formatVersion) {
std::ignore = formatVersion;
@ -1867,6 +2361,16 @@ std::expected<std::unique_ptr<CustomFontSection>, ParseError> CustomFontSection:
return section;
}
std::unique_ptr<CustomFontSection> CustomFontSection::parseJSON(nlohmann::json const& j, std::uint32_t formatVersion) {
std::ignore = formatVersion;
auto fontData = base64Decode(j.at("data").get<std::string>());
auto section = std::make_unique<CustomFontSection>();
section->setFontData(fontData);
return section;
}
std::size_t serializeFile(std::span<std::byte>& buffer, File const& file) {
std::size_t pos = 0;
std::uint32_t formatVersion = 1;
@ -1891,6 +2395,20 @@ std::size_t serializeFile(std::span<std::byte>& buffer, File const& file) {
return pos;
}
nlohmann::json serializeFileJSON(File const& file) {
std::uint32_t formatVersion = 1;
for (auto const& section : file.sections) {
formatVersion = std::max(formatVersion, section->minimumFormatVersion());
}
nlohmann::json result;
result["version"] = formatVersion;
result["sections"] = nlohmann::json::array();
for (auto const& section : file.sections) {
result["sections"].push_back(section->serializeJSON(formatVersion));
}
return result;
}
std::expected<File, ParseError> parseFile(std::span<std::byte const> data) {
auto magic = readU32LE(data);
if (!magic) {
@ -1943,4 +2461,113 @@ std::expected<File, ParseError> parseFile(std::span<std::byte const> data) {
return result;
}
File parseFileJSON(nlohmann::json const& j) {
File result;
std::uint32_t version = j.at("version");
std::size_t const sectionCount = j.at("sections").size();
for (std::size_t i = 0; i < sectionCount; ++i) {
nlohmann::json const& jSection = j.at("sections").at(i);
SectionType sectionType = jSection.at("type");
std::unique_ptr<Section> section;
switch (sectionType) {
case SectionType::AlwaysDrawn: section = AlwaysDrawnSection::parseJSON(jSection, version); break;
case SectionType::TimeBasedDrawn: section = TimeBasedDrawnSection::parseJSON(jSection, version); break;
case SectionType::CustomFont: section = CustomFontSection::parseJSON(jSection, version); break;
default: throw std::runtime_error(std::format("Unsupported section type {:s} encountered while parsing a file",
jSection.at("type").get<std::string>()));
}
result.sections.push_back(std::move(section));
}
return result;
}
constexpr static char const* const Base64Alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
constexpr static std::int8_t const Base64ParseLookup[256] = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -2, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1};
std::string base64Encode(std::span<std::byte const> buffer) {
std::string result;
uint8_t const* data = reinterpret_cast<uint8_t const*>(buffer.data());
std::size_t numberOfBlocks = (buffer.size() + 2) / 3;
result.reserve(numberOfBlocks);
for (std::size_t i = 0; i < numberOfBlocks; ++i) {
std::size_t ii = i * 3;
std::size_t ni = std::min(ii + 3, buffer.size()) - ii;
int c0 = (((data[ii + 0] >> 2) & 0x3f) << 0);
if (ni == 3) {
int c1 = (((data[ii + 0] >> 0) & 0x03) << 4)
| (((data[ii + 1] >> 4) & 0x0f) << 0);
int c2 = (((data[ii + 1] >> 0) & 0x0f) << 2)
| (((data[ii + 2] >> 6) & 0x03) << 0);
int c3 = (((data[ii + 2] >> 0) & 0x3f) << 0);
result.push_back(Base64Alphabet[c0]);
result.push_back(Base64Alphabet[c1]);
result.push_back(Base64Alphabet[c2]);
result.push_back(Base64Alphabet[c3]);
} else if (ni == 2) {
int c1 = (((data[ii + 0] >> 0) & 0x03) << 4)
| (((data[ii + 1] >> 4) & 0x0f) << 0);
int c2 = (((data[ii + 1] >> 0) & 0x0f) << 2);
result.push_back(Base64Alphabet[c0]);
result.push_back(Base64Alphabet[c1]);
result.push_back(Base64Alphabet[c2]);
} else if (ni == 1) {
int c1 = (((data[ii + 0] >> 0) & 0x03) << 4);
result.push_back(Base64Alphabet[c0]);
result.push_back(Base64Alphabet[c1]);
}
}
return result;
}
std::vector<std::byte> base64Decode(std::string_view data) {
std::vector<std::byte> result;
// This is just an initial estimate
std::size_t numberOfBlocks = (data.size() + 3) / 4;
result.reserve(numberOfBlocks * 3);
int state = 0;
int currentValue = 0;
for (std::size_t i = 0; i < data.size(); ++i) {
int value = Base64ParseLookup[static_cast<std::uint8_t>(data[i])];
if (value == -2) {
// '=': padding at the end
break;
} else if (value == -1) {
// any invalid character (especially whitespace): ignore
continue;
}
switch (state) {
case 0:
default:
currentValue = value << 2;
break;
case 1:
currentValue |= (value >> 4) & 0x03;
result.push_back(static_cast<std::byte>(currentValue));
currentValue = (value & 0x0f) << 4;
break;
case 2:
currentValue |= (value >> 2) & 0x0f;
result.push_back(static_cast<std::byte>(currentValue));
currentValue = (value & 0x03) << 6;
break;
case 3:
currentValue |= value & 0x3f;
result.push_back(static_cast<std::byte>(currentValue));
currentValue = 0;
break;
}
state = (state + 1) % 4;
}
// Ignore any errors here
return result;
}
} // namespace monoformat

@ -4,6 +4,8 @@
#include "monoformat_parsehelpers.hpp"
#include "monoformat_schema.hpp"
#include <nlohmann/json.hpp>
#include <cstddef>
#include <cstdint>
#include <memory>
@ -17,6 +19,7 @@ struct Section {
virtual SectionType sectionType() const = 0;
virtual std::uint32_t minimumFormatVersion() const = 0;
virtual std::size_t serializeTo(std::span<std::byte> target, std::uint32_t formatVersion) const = 0;
virtual nlohmann::json serializeJSON(std::uint32_t formatVersion) const = 0;
protected:
Section() = default;
@ -29,6 +32,7 @@ struct Element {
virtual ElementType elementType() const = 0;
virtual std::uint32_t minimumFormatVersion() const = 0;
virtual std::size_t serializeTo(std::span<std::byte> target, std::uint32_t formatVersion) const = 0;
virtual nlohmann::json serializeJSON(std::uint32_t formatVersion) const = 0;
virtual void drawTo(OneBitBufferInterface* imageBuffer, std::size_t animationTick, std::int64_t currentTimestamp, std::span<CustomFontSection const*> customFonts) = 0;
protected:
@ -52,9 +56,11 @@ struct ImageElement : public Element {
virtual ElementType elementType() const override;
virtual std::uint32_t minimumFormatVersion() const override;
virtual std::size_t serializeTo(std::span<std::byte> target, std::uint32_t formatVersion) const override;
virtual nlohmann::json serializeJSON(std::uint32_t formatVersion) const override;
virtual void drawTo(OneBitBufferInterface* imageBuffer, std::size_t animationTick, std::int64_t currentTimestamp, std::span<CustomFontSection const*> customFonts) override;
static std::expected<std::unique_ptr<ImageElement>, ParseError> parse(std::span<std::byte const>& buffer, std::uint32_t formatVersion);
static std::unique_ptr<ImageElement> parseJSON(nlohmann::json const& j, std::uint32_t formatVersion);
private:
std::uint16_t m_x{};
@ -85,9 +91,11 @@ struct AnimationElement : public Element {
virtual ElementType elementType() const override;
virtual std::uint32_t minimumFormatVersion() const override;
virtual std::size_t serializeTo(std::span<std::byte> target, std::uint32_t formatVersion) const override;
virtual nlohmann::json serializeJSON(std::uint32_t formatVersion) const override;
virtual void drawTo(OneBitBufferInterface* imageBuffer, std::size_t animationTick, std::int64_t currentTimestamp, std::span<CustomFontSection const*> customFonts) override;
static std::expected<std::unique_ptr<AnimationElement>, ParseError> parse(std::span<std::byte const>& buffer, std::uint32_t formatVersion);
static std::unique_ptr<AnimationElement> parseJSON(nlohmann::json const& j, std::uint32_t formatVersion);
private:
std::uint16_t m_x{};
@ -119,9 +127,11 @@ struct HScrollImageElement : public Element {
virtual ElementType elementType() const override;
virtual std::uint32_t minimumFormatVersion() const override;
virtual std::size_t serializeTo(std::span<std::byte> target, std::uint32_t formatVersion) const override;
virtual nlohmann::json serializeJSON(std::uint32_t formatVersion) const override;
virtual void drawTo(OneBitBufferInterface* imageBuffer, std::size_t animationTick, std::int64_t currentTimestamp, std::span<CustomFontSection const*> customFonts) override;
static std::expected<std::unique_ptr<HScrollImageElement>, ParseError> parse(std::span<std::byte const>& buffer, std::uint32_t formatVersion);
static std::unique_ptr<HScrollImageElement> parseJSON(nlohmann::json const& j, std::uint32_t formatVersion);
private:
std::uint16_t m_x{};
@ -154,9 +164,11 @@ struct VScrollImageElement : public Element {
virtual ElementType elementType() const override;
virtual std::uint32_t minimumFormatVersion() const override;
virtual std::size_t serializeTo(std::span<std::byte> target, std::uint32_t formatVersion) const override;
virtual nlohmann::json serializeJSON(std::uint32_t formatVersion) const override;
virtual void drawTo(OneBitBufferInterface* imageBuffer, std::size_t animationTick, std::int64_t currentTimestamp, std::span<CustomFontSection const*> customFonts) override;
static std::expected<std::unique_ptr<VScrollImageElement>, ParseError> parse(std::span<std::byte const>& buffer, std::uint32_t formatVersion);
static std::unique_ptr<VScrollImageElement> parseJSON(nlohmann::json const& j, std::uint32_t formatVersion);
private:
std::uint16_t m_x{};
@ -183,9 +195,11 @@ struct LineElement : public Element {
virtual ElementType elementType() const override;
virtual std::uint32_t minimumFormatVersion() const override;
virtual std::size_t serializeTo(std::span<std::byte> target, std::uint32_t formatVersion) const override;
virtual nlohmann::json serializeJSON(std::uint32_t formatVersion) const override;
virtual void drawTo(OneBitBufferInterface* imageBuffer, std::size_t animationTick, std::int64_t currentTimestamp, std::span<CustomFontSection const*> customFonts) override;
static std::expected<std::unique_ptr<LineElement>, ParseError> parse(std::span<std::byte const>& buffer, std::uint32_t formatVersion);
static std::unique_ptr<LineElement> parseJSON(nlohmann::json const& j, std::uint32_t formatVersion);
private:
std::uint16_t m_originX{};
@ -210,9 +224,11 @@ struct ClippedTextElement : public Element {
virtual ElementType elementType() const override;
virtual std::uint32_t minimumFormatVersion() const override;
virtual std::size_t serializeTo(std::span<std::byte> target, std::uint32_t formatVersion) const override;
virtual nlohmann::json serializeJSON(std::uint32_t formatVersion) const override;
virtual void drawTo(OneBitBufferInterface* imageBuffer, std::size_t animationTick, std::int64_t currentTimestamp, std::span<CustomFontSection const*> customFonts) override;
static std::expected<std::unique_ptr<ClippedTextElement>, ParseError> parse(std::span<std::byte const>& buffer, std::uint32_t formatVersion);
static std::unique_ptr<ClippedTextElement> parseJSON(nlohmann::json const& j, std::uint32_t formatVersion);
private:
std::uint16_t m_x{};
@ -239,9 +255,11 @@ struct HScrollTextElement : public Element {
virtual ElementType elementType() const override;
virtual std::uint32_t minimumFormatVersion() const override;
virtual std::size_t serializeTo(std::span<std::byte> target, std::uint32_t formatVersion) const override;
virtual nlohmann::json serializeJSON(std::uint32_t formatVersion) const override;
virtual void drawTo(OneBitBufferInterface* imageBuffer, std::size_t animationTick, std::int64_t currentTimestamp, std::span<CustomFontSection const*> customFonts) override;
static std::expected<std::unique_ptr<HScrollTextElement>, ParseError> parse(std::span<std::byte const>& buffer, std::uint32_t formatVersion);
static std::unique_ptr<HScrollTextElement> parseJSON(nlohmann::json const& j, std::uint32_t formatVersion);
private:
std::uint16_t m_x{};
@ -269,9 +287,11 @@ struct CurrentTimeElement : public Element {
virtual ElementType elementType() const override;
virtual std::uint32_t minimumFormatVersion() const override;
virtual std::size_t serializeTo(std::span<std::byte> target, std::uint32_t formatVersion) const override;
virtual nlohmann::json serializeJSON(std::uint32_t formatVersion) const override;
virtual void drawTo(OneBitBufferInterface* imageBuffer, std::size_t animationTick, std::int64_t currentTimestamp, std::span<CustomFontSection const*> customFonts) override;
static std::expected<std::unique_ptr<CurrentTimeElement>, ParseError> parse(std::span<std::byte const>& buffer, std::uint32_t formatVersion);
static std::unique_ptr<CurrentTimeElement> parseJSON(nlohmann::json const& j, std::uint32_t formatVersion);
private:
std::uint16_t m_x{};
@ -306,8 +326,10 @@ struct AlwaysDrawnSection : public Section {
virtual SectionType sectionType() const override;
virtual std::uint32_t minimumFormatVersion() const override;
virtual std::size_t serializeTo(std::span<std::byte> target, std::uint32_t formatVersion) const override;
virtual nlohmann::json serializeJSON(std::uint32_t formatVersion) const override;
static std::expected<std::unique_ptr<AlwaysDrawnSection>, ParseError> parse(std::span<std::byte const>& buffer, std::uint32_t formatVersion);
static std::unique_ptr<AlwaysDrawnSection> parseJSON(nlohmann::json const& j, std::uint32_t formatVersion);
private:
std::vector<std::unique_ptr<Element>> m_elements;
@ -344,8 +366,10 @@ struct TimeBasedDrawnSection : public Section {
virtual SectionType sectionType() const override;
virtual std::uint32_t minimumFormatVersion() const override;
virtual std::size_t serializeTo(std::span<std::byte> target, std::uint32_t formatVersion) const override;
virtual nlohmann::json serializeJSON(std::uint32_t formatVersion) const override;
static std::expected<std::unique_ptr<TimeBasedDrawnSection>, ParseError> parse(std::span<std::byte const>& buffer, std::uint32_t formatVersion);
static std::unique_ptr<TimeBasedDrawnSection> parseJSON(nlohmann::json const& j, std::uint32_t formatVersion);
private:
std::vector<std::unique_ptr<Element>> m_elements;
@ -366,8 +390,10 @@ struct CustomFontSection : public Section {
virtual SectionType sectionType() const override;
virtual std::uint32_t minimumFormatVersion() const override;
virtual std::size_t serializeTo(std::span<std::byte> target, std::uint32_t formatVersion) const override;
virtual nlohmann::json serializeJSON(std::uint32_t formatVersion) const override;
static std::expected<std::unique_ptr<CustomFontSection>, ParseError> parse(std::span<std::byte const>& buffer, std::uint32_t formatVersion);
static std::unique_ptr<CustomFontSection> parseJSON(nlohmann::json const& j, std::uint32_t formatVersion);
private:
std::vector<std::byte> m_fontData;
@ -378,7 +404,9 @@ struct File {
};
extern std::size_t serializeFile(std::span<std::byte>& buffer, File const& file);
extern nlohmann::json serializeFileJSON(File const& file);
extern std::expected<File, ParseError> parseFile(std::span<std::byte const> data);
extern File parseFileJSON(nlohmann::json const& j);
} // namespace monoformat

Loading…
Cancel
Save