diff --git a/cpp/src/monoformat_parsehelpers.hpp b/cpp/src/monoformat_parsehelpers.hpp index 503159c..9e2ca4c 100644 --- a/cpp/src/monoformat_parsehelpers.hpp +++ b/cpp/src/monoformat_parsehelpers.hpp @@ -5,6 +5,7 @@ #include #include #include +#include // for std::ignore namespace monoformat { @@ -228,6 +229,125 @@ inline std::pair overflowingShl(std::uint8_t value, std::uin return {value << bits, overflow}; } +inline std::size_t writeU8LE(std::span& target, std::size_t pos, std::uint8_t value) { + if (pos < target.size()) { + target[pos] = static_cast(value); + } + return pos + 1; +} + +inline std::size_t writeU8BE(std::span& target, std::size_t pos, std::uint8_t value) { + if (pos < target.size()) { + target[pos] = static_cast(value); + } + return pos + 1; +} + +inline std::size_t writeU16LE(std::span& target, std::size_t pos, std::uint16_t value) { + if ((pos + 1) < target.size()) { + target[pos + 0] = static_cast((value >> 0u) & 0xffu); + target[pos + 1] = static_cast((value >> 8u) & 0xffu); + } + return pos + 2; +} + +inline std::size_t writeU16BE(std::span& target, std::size_t pos, std::uint16_t value) { + if ((pos + 1) < target.size()) { + target[pos + 0] = static_cast((value >> 8u) & 0xffu); + target[pos + 1] = static_cast((value >> 0u) & 0xffu); + } + return pos + 2; +} + +inline std::size_t writeU24LE(std::span& target, std::size_t pos, std::uint32_t value) { + if ((pos + 2) < target.size()) { + target[pos + 0] = static_cast((value >> 0u) & 0xffu); + target[pos + 1] = static_cast((value >> 8u) & 0xffu); + target[pos + 2] = static_cast((value >> 16u) & 0xffu); + } + return pos + 3; +} + +inline std::size_t writeU24BE(std::span& target, std::size_t pos, std::uint32_t value) { + if ((pos + 2) < target.size()) { + target[pos + 0] = static_cast((value >> 16u) & 0xffu); + target[pos + 1] = static_cast((value >> 8u) & 0xffu); + target[pos + 2] = static_cast((value >> 0u) & 0xffu); + } + return pos + 3; +} + +inline std::size_t writeU32LE(std::span& target, std::size_t pos, std::uint32_t value) { + if ((pos + 3) < target.size()) { + target[pos + 0] = static_cast((value >> 0u) & 0xffu); + target[pos + 1] = static_cast((value >> 8u) & 0xffu); + target[pos + 2] = static_cast((value >> 16u) & 0xffu); + target[pos + 3] = static_cast((value >> 24u) & 0xffu); + } + return pos + 4; +} + +inline std::size_t writeU32BE(std::span& target, std::size_t pos, std::uint32_t value) { + if ((pos + 3) < target.size()) { + target[pos + 0] = static_cast((value >> 24u) & 0xffu); + target[pos + 1] = static_cast((value >> 16u) & 0xffu); + target[pos + 2] = static_cast((value >> 8u) & 0xffu); + target[pos + 3] = static_cast((value >> 0u) & 0xffu); + } + return pos + 4; +} + +inline std::size_t writeU64LE(std::span& target, std::size_t pos, std::uint64_t value) { + if ((pos + 7) < target.size()) { + target[pos + 0] = static_cast((value >> 0u) & 0xffu); + target[pos + 1] = static_cast((value >> 8u) & 0xffu); + target[pos + 2] = static_cast((value >> 16u) & 0xffu); + target[pos + 3] = static_cast((value >> 24u) & 0xffu); + target[pos + 4] = static_cast((value >> 32u) & 0xffu); + target[pos + 5] = static_cast((value >> 40u) & 0xffu); + target[pos + 6] = static_cast((value >> 48u) & 0xffu); + target[pos + 7] = static_cast((value >> 56u) & 0xffu); + } + return pos + 8; +} + +inline std::size_t writeU64BE(std::span& target, std::size_t pos, std::uint64_t value) { + if ((pos + 3) < target.size()) { + target[pos + 0] = static_cast((value >> 56u) & 0xffu); + target[pos + 1] = static_cast((value >> 48u) & 0xffu); + target[pos + 2] = static_cast((value >> 40u) & 0xffu); + target[pos + 3] = static_cast((value >> 32u) & 0xffu); + target[pos + 4] = static_cast((value >> 24u) & 0xffu); + target[pos + 5] = static_cast((value >> 16u) & 0xffu); + target[pos + 6] = static_cast((value >> 8u) & 0xffu); + target[pos + 7] = static_cast((value >> 0u) & 0xffu); + } + return pos + 8; +} + +inline std::size_t writeBuffer(std::span& target, std::size_t pos, std::span value) { + if ((pos + value.size()) > target.size()) { + if (pos < value.size()) { + std::size_t const n = value.size() - pos; + std::copy(value.begin(), value.begin() + n, target.begin() + pos); + } + } else { + std::copy(value.begin(), value.end(), target.begin() + pos); + } + return pos + value.size(); +} + +inline std::size_t alignNextWrite(std::span& target, std::size_t pos, std::size_t alignment) { + std::ignore = target; + if (alignment <= 1) { + return pos; + } + if (pos % alignment == 0) { + return pos; + } + return ((pos + alignment - 1) / alignment) * alignment; +} + } // namespace monoformat #endif // MONOFORMAT_PARSEHELPERS_HPP diff --git a/cpp/src/monoformat_structured.cpp b/cpp/src/monoformat_structured.cpp index 940d435..0ac5475 100644 --- a/cpp/src/monoformat_structured.cpp +++ b/cpp/src/monoformat_structured.cpp @@ -122,7 +122,15 @@ ElementType ImageElement::elementType() const { } std::size_t ImageElement::serializeTo(std::span target) const { - // FIXME: serialize + std::size_t pos = 0; + pos = writeU16LE(target, pos, static_cast(ElementType::Image)); + pos = writeU16LE(target, pos, m_x); + pos = writeU16LE(target, pos, m_y); + pos = writeU16LE(target, pos, m_width); + pos = writeU16LE(target, pos, m_height); + pos = writeU16LE(target, pos, 0); + pos = writeBuffer(target, pos, m_buffer); + return alignNextWrite(target, pos, 4); } void ImageElement::drawTo(OneBitBufferInterface* imageBuffer, std::size_t animationTick, std::int64_t currentTimestamp) { @@ -209,7 +217,17 @@ ElementType AnimationElement::elementType() const { } std::size_t AnimationElement::serializeTo(std::span target) const { - // FIXME: implement this + std::size_t pos = 0; + pos = writeU16LE(target, pos, static_cast(ElementType::Animation)); + pos = writeU16LE(target, pos, m_x); + pos = writeU16LE(target, pos, m_y); + pos = writeU16LE(target, pos, m_width); + pos = writeU16LE(target, pos, m_height); + pos = writeU16LE(target, pos, m_numberOfFrames); + pos = writeU16LE(target, pos, m_updateInterval); + pos = writeU16LE(target, pos, 0); + pos = writeBuffer(target, pos, m_buffer); + return alignNextWrite(target, pos, 4); } void AnimationElement::drawTo(OneBitBufferInterface* imageBuffer, std::size_t animationTick, std::int64_t currentTimestamp) { @@ -285,7 +303,18 @@ ElementType HScrollImageElement::elementType() const { } std::size_t HScrollImageElement::serializeTo(std::span target) const { - // FIXME Serialize + std::size_t pos = 0; + pos = writeU16LE(target, pos, static_cast(ElementType::HScrollImage)); + pos = writeU16LE(target, pos, m_x); + pos = writeU16LE(target, pos, m_y); + pos = writeU16LE(target, pos, m_width); + pos = writeU16LE(target, pos, m_height); + pos = writeU16LE(target, pos, m_contentWidth); + pos = writeU8LE(target, pos, m_flags); + pos = writeU8LE(target, pos, m_scrollSpeed); + pos = writeU16LE(target, pos, 0); + pos = writeBuffer(target, pos, m_buffer); + return alignNextWrite(target, pos, 4); } void HScrollImageElement::drawTo(OneBitBufferInterface* imageBuffer, std::size_t animationTick, std::int64_t currentTimestamp) { @@ -361,7 +390,18 @@ ElementType VScrollImageElement::elementType() const { } std::size_t VScrollImageElement::serializeTo(std::span target) const { - // FIXME Serialize + std::size_t pos = 0; + pos = writeU16LE(target, pos, static_cast(ElementType::VScrollImage)); + pos = writeU16LE(target, pos, m_x); + pos = writeU16LE(target, pos, m_y); + pos = writeU16LE(target, pos, m_width); + pos = writeU16LE(target, pos, m_height); + pos = writeU16LE(target, pos, m_contentHeight); + pos = writeU8LE(target, pos, m_flags); + pos = writeU8LE(target, pos, m_scrollSpeed); + pos = writeU16LE(target, pos, 0); + pos = writeBuffer(target, pos, m_buffer); + return alignNextWrite(target, pos, 4); } void VScrollImageElement::drawTo(OneBitBufferInterface* imageBuffer, std::size_t animationTick, std::int64_t currentTimestamp) { @@ -415,7 +455,15 @@ ElementType LineElement::elementType() const { } std::size_t LineElement::serializeTo(std::span target) const { - // FIXME: serialize + std::size_t pos = 0; + pos = writeU16LE(target, pos, static_cast(ElementType::Line)); + pos = writeU16LE(target, pos, m_originX); + pos = writeU16LE(target, pos, m_originY); + pos = writeU16LE(target, pos, m_targetX); + pos = writeU16LE(target, pos, m_targetY); + pos = writeU8LE(target, pos, static_cast(m_lineStyle)); + pos = writeU8LE(target, pos, m_flags); + return alignNextWrite(target, pos, 4); } void LineElement::drawTo(OneBitBufferInterface* imageBuffer, std::size_t animationTick, std::int64_t currentTimestamp) { @@ -468,7 +516,16 @@ ElementType ClippedTextElement::elementType() const { } std::size_t ClippedTextElement::serializeTo(std::span target) const { - // FIXME: serialize + std::size_t pos = 0; + pos = writeU16LE(target, pos, static_cast(ElementType::HScrollImage)); + pos = writeU16LE(target, pos, m_x); + pos = writeU16LE(target, pos, m_y); + pos = writeU16LE(target, pos, m_width); + pos = writeU16LE(target, pos, m_height); + pos = writeU16LE(target, pos, m_fontIndex); + pos = writeU16LE(target, pos, m_text.size()); + pos = writeBuffer(target, pos, std::as_bytes(std::span{m_text})); + return alignNextWrite(target, pos, 4); } void ClippedTextElement::drawTo(OneBitBufferInterface* imageBuffer, std::size_t animationTick, std::int64_t currentTimestamp) { @@ -531,7 +588,18 @@ ElementType HScrollTextElement::elementType() const { } std::size_t HScrollTextElement::serializeTo(std::span target) const { - // FIXME serialize + std::size_t pos = 0; + pos = writeU16LE(target, pos, static_cast(ElementType::HScrollImage)); + pos = writeU16LE(target, pos, m_x); + pos = writeU16LE(target, pos, m_y); + pos = writeU16LE(target, pos, m_width); + pos = writeU16LE(target, pos, m_height); + pos = writeU8LE(target, pos, m_flags); + pos = writeU8LE(target, pos, m_scrollSpeed); + pos = writeU16LE(target, pos, m_fontIndex); + pos = writeU16LE(target, pos, m_text.size()); + pos = writeBuffer(target, pos, std::as_bytes(std::span{m_text})); + return alignNextWrite(target, pos, 4); } void HScrollTextElement::drawTo(OneBitBufferInterface* imageBuffer, std::size_t animationTick, std::int64_t currentTimestamp) { @@ -589,7 +657,16 @@ ElementType CurrentTimeElement::elementType() const { } std::size_t CurrentTimeElement::serializeTo(std::span target) const { - // FIXME: serialize + std::size_t pos = 0; + pos = writeU16LE(target, pos, static_cast(ElementType::HScrollImage)); + pos = writeU16LE(target, pos, m_x); + pos = writeU16LE(target, pos, m_y); + pos = writeU16LE(target, pos, m_width); + pos = writeU16LE(target, pos, m_height); + pos = writeU16LE(target, pos, m_fontIndex); + pos = writeU16LE(target, pos, m_utcOffset); + pos = writeU16LE(target, pos, m_flags); + return alignNextWrite(target, pos, 4); } void CurrentTimeElement::drawTo(OneBitBufferInterface* imageBuffer, std::size_t animationTick, std::int64_t currentTimestamp) { @@ -674,7 +751,32 @@ SectionType AlwaysDrawnSection::sectionType() const { } std::size_t AlwaysDrawnSection::serializeTo(std::span target) const { - // FIXME: serialize + std::size_t pos = 0; + pos = writeU8LE(target, pos, static_cast(SectionType::AlwaysDrawn)); + // Will be replaced later + pos = writeU24LE(target, pos, 0); + std::uint16_t flags = 0; + if (m_drawOnFront) { + flags |= std::uint16_t{1}; + } + if (m_drawOnBack) { + flags |= std::uint16_t{2}; + } + if (m_clearBeforeDrawing) { + flags |= std::uint16_t{4}; + } + pos = writeU16LE(target, pos, flags); + pos = writeU16LE(target, pos, m_elements.size()); + for (auto const& element : m_elements) { + if (pos < target.size()) { + pos += element->serializeTo(target.subspan(pos)); + } else { + pos += element->serializeTo({}); + } + } + pos = alignNextWrite(target, pos, 4); + std::ignore = writeU24LE(target, 1, pos); + return pos; } std::expected, ParseError> AlwaysDrawnSection::parse(std::span& buffer) { @@ -771,7 +873,34 @@ SectionType TimeBasedDrawnSection::sectionType() const { } std::size_t TimeBasedDrawnSection::serializeTo(std::span target) const { - // FIXME: serialize + std::size_t pos = 0; + pos = writeU8LE(target, pos, static_cast(SectionType::AlwaysDrawn)); + // Will be replaced later + pos = writeU24LE(target, pos, 0); + std::uint16_t flags = 0; + if (m_drawOnFront) { + flags |= std::uint16_t{1}; + } + if (m_drawOnBack) { + flags |= std::uint16_t{2}; + } + if (m_clearBeforeDrawing) { + flags |= std::uint16_t{4}; + } + pos = writeU16LE(target, pos, flags); + pos = writeU16LE(target, pos, m_elements.size()); + pos = writeU64LE(target, pos, std::bit_cast(m_startTimestamp)); + pos = writeU64LE(target, pos, std::bit_cast(m_endTimestamp)); + for (auto const& element : m_elements) { + if (pos < target.size()) { + pos += element->serializeTo(target.subspan(pos)); + } else { + pos += element->serializeTo({}); + } + } + pos = alignNextWrite(target, pos, 4); + std::ignore = writeU24LE(target, 1, pos); + return pos; } std::expected, ParseError> TimeBasedDrawnSection::parse(std::span& buffer) { @@ -795,7 +924,16 @@ SectionType CustomFontSection::sectionType() const { } std::size_t CustomFontSection::serializeTo(std::span target) const { - // FIXME: serailize + std::size_t pos = 0; + pos = writeU8LE(target, pos, static_cast(SectionType::AlwaysDrawn)); + // Will be replaced later + pos = writeU24LE(target, pos, 0); + pos = writeU24LE(target, pos, m_fontData.size()); + pos = writeU8LE(target, pos, 0); + pos = writeBuffer(target, pos, m_fontData); + pos = alignNextWrite(target, pos, 4); + std::ignore = writeU24LE(target, 1, pos); + return pos; } std::expected, ParseError> CustomFontSection::parse(std::span& buffer) { @@ -803,7 +941,23 @@ std::expected, ParseError> CustomFontSection: } std::size_t serializeFile(std::span& buffer, File const& file) { - // FIXME: serialize + std::size_t pos = 0; + pos = writeU8LE(buffer, pos, 0xAF); + pos = writeU8LE(buffer, pos, 0x7E); + pos = writeU8LE(buffer, pos, 0x2B); + pos = writeU8LE(buffer, pos, 0x64); + pos = writeU32LE(buffer, pos, 1); + pos = writeU16LE(buffer, pos, file.sections.size()); + pos = writeU16LE(buffer, pos, 0); + pos = alignNextWrite(buffer, pos, 4); + for (auto const& section : file.sections) { + if (pos < buffer.size()) { + pos += section->serializeTo(buffer.subspan(pos)); + } else { + pos += section->serializeTo({}); + } + } + return pos; } std::expected parseFile(std::span data) {