From c4a5b80f0a0e6deb6f7093e33acec89ab9f3c404 Mon Sep 17 00:00:00 2001 From: Unreal-Dan <72595612+Unreal-Dan@users.noreply.github.com> Date: Fri, 26 Apr 2024 15:32:42 -0700 Subject: [PATCH 1/2] Daniel/core/serialize harden (#226) * work for now * hardened all serialization code * hardened all serialize code * more hardening * fix wasm --- VortexEngine/VortexLib/VortexLib.cpp | 13 +-- VortexEngine/src/Colors/ColorTypes.cpp | 30 +++++-- VortexEngine/src/Colors/ColorTypes.h | 4 +- VortexEngine/src/Colors/Colorset.cpp | 52 ++++++++---- VortexEngine/src/Colors/Colorset.h | 6 +- .../src/Menus/MenuList/EditorConnection.cpp | 12 ++- .../src/Menus/MenuList/Randomizer.cpp | 8 +- VortexEngine/src/Modes/Mode.cpp | 54 ++++++++++--- VortexEngine/src/Modes/Mode.h | 2 +- VortexEngine/src/Modes/Modes.cpp | 40 +++++++--- .../src/Patterns/Multi/Sequencer/Sequence.cpp | 80 ++++++++++++++----- .../src/Patterns/Multi/Sequencer/Sequence.h | 16 ++-- VortexEngine/src/Patterns/Pattern.cpp | 22 +++-- VortexEngine/src/Patterns/Pattern.h | 4 +- VortexEngine/src/Patterns/PatternArgs.cpp | 19 +++-- VortexEngine/src/Patterns/PatternArgs.h | 2 +- VortexEngine/src/Patterns/PatternBuilder.cpp | 11 ++- VortexEngine/src/Serial/ByteStream.cpp | 60 ++------------ VortexEngine/src/Serial/ByteStream.h | 24 ++---- VortexEngine/src/Serial/Serial.cpp | 2 +- VortexEngine/src/VortexEngine.cpp | 9 ++- 21 files changed, 285 insertions(+), 185 deletions(-) diff --git a/VortexEngine/VortexLib/VortexLib.cpp b/VortexEngine/VortexLib/VortexLib.cpp index d325ae874d..50817c5ff1 100644 --- a/VortexEngine/VortexLib/VortexLib.cpp +++ b/VortexEngine/VortexLib/VortexLib.cpp @@ -166,15 +166,16 @@ EMSCRIPTEN_BINDINGS(Vortex) { .function("sanity", &ByteStream::sanity) .function("checkCRC", &ByteStream::checkCRC) .function("isCRCDirty", &ByteStream::isCRCDirty) - .function("serialize", select_overload(&ByteStream::serialize)) - .function("serialize16", select_overload(&ByteStream::serialize)) - .function("serialize32", select_overload(&ByteStream::serialize)) + .function("serialize8", &ByteStream::serialize8) + .function("serialize16", &ByteStream::serialize16) + .function("serialize32", &ByteStream::serialize32) .function("resetUnserializer", &ByteStream::resetUnserializer) .function("moveUnserializer", &ByteStream::moveUnserializer) .function("unserializerAtEnd", &ByteStream::unserializerAtEnd) - .function("unserialize8", &ByteStream::unserialize8) - .function("unserialize16", &ByteStream::unserialize16) - .function("unserialize32", &ByteStream::unserialize32) + // TODO: provide better apis here + //.function("unserialize8", &ByteStream::unserialize8) + //.function("unserialize16", &ByteStream::unserialize16) + //.function("unserialize32", &ByteStream::unserialize32) .function("peek8", &ByteStream::peek8) .function("peek16", &ByteStream::peek16) .function("peek32", &ByteStream::peek32) diff --git a/VortexEngine/src/Colors/ColorTypes.cpp b/VortexEngine/src/Colors/ColorTypes.cpp index 8b1c009bc6..5dec968e3b 100644 --- a/VortexEngine/src/Colors/ColorTypes.cpp +++ b/VortexEngine/src/Colors/ColorTypes.cpp @@ -198,18 +198,32 @@ RGBColor RGBColor::adjustBrightness(uint8_t fadeBy) return *this; } -void RGBColor::serialize(ByteStream &buffer) const +bool RGBColor::serialize(ByteStream &buffer) const { - buffer.serialize(red); - buffer.serialize(green); - buffer.serialize(blue); + if (!buffer.serialize8(red)) { + return false; + } + if (!buffer.serialize8(green)) { + return false; + } + if (!buffer.serialize8(blue)) { + return false; + } + return true; } -void RGBColor::unserialize(ByteStream &buffer) +bool RGBColor::unserialize(ByteStream &buffer) { - buffer.unserialize(&red); - buffer.unserialize(&green); - buffer.unserialize(&blue); + if (!buffer.unserialize8(&red)) { + return false; + } + if (!buffer.unserialize8(&green)) { + return false; + } + if (!buffer.unserialize8(&blue)) { + return false; + } + return true; } // ======================================================== diff --git a/VortexEngine/src/Colors/ColorTypes.h b/VortexEngine/src/Colors/ColorTypes.h index c4c69a5aa6..c4e92b2936 100644 --- a/VortexEngine/src/Colors/ColorTypes.h +++ b/VortexEngine/src/Colors/ColorTypes.h @@ -79,8 +79,8 @@ class RGBColor void clear(); RGBColor adjustBrightness(uint8_t fadeBy); - void serialize(ByteStream &buffer) const; - void unserialize(ByteStream &buffer); + bool serialize(ByteStream &buffer) const; + bool unserialize(ByteStream &buffer); uint32_t raw() const { return ((uint32_t)red << 16) | ((uint32_t)green << 8) | (uint32_t)blue; } diff --git a/VortexEngine/src/Colors/Colorset.cpp b/VortexEngine/src/Colors/Colorset.cpp index c5ebfafa7f..ea469c2384 100644 --- a/VortexEngine/src/Colors/Colorset.cpp +++ b/VortexEngine/src/Colors/Colorset.cpp @@ -450,52 +450,76 @@ bool Colorset::onEnd() const return (m_curIndex == m_numColors - 1); } -void Colorset::serialize(ByteStream &buffer) const +bool Colorset::serialize(ByteStream &buffer) const { - buffer.serialize(m_numColors); + if (!buffer.serialize8(m_numColors)) { + return false; + } // write all the reds/greens/blues together to maximize chance of // repeated values to improve RLE compression for (uint8_t i = 0; i < m_numColors; ++i) { - buffer.serialize(m_palette[i].red); + if (!buffer.serialize8(m_palette[i].red)) { + return false; + } } for (uint8_t i = 0; i < m_numColors; ++i) { - buffer.serialize(m_palette[i].green); + if (!buffer.serialize8(m_palette[i].green)) { + return false; + } } for (uint8_t i = 0; i < m_numColors; ++i) { - buffer.serialize(m_palette[i].blue); + if (!buffer.serialize8(m_palette[i].blue)) { + return false; + } } + return true; } -void Colorset::unserialize(ByteStream &buffer) +bool Colorset::unserialize(ByteStream &buffer) { - buffer.unserialize(&m_numColors); - initPalette(m_numColors); + if (!buffer.unserialize8(&m_numColors)) { + return false; + } + if (m_numColors > MAX_COLOR_SLOTS) { + return false; + } + if (!initPalette(m_numColors)) { + return false; + } for (uint8_t i = 0; i < m_numColors; ++i) { - buffer.unserialize(&m_palette[i].red); + if (!buffer.unserialize8(&m_palette[i].red)) { + return false; + } } for (uint8_t i = 0; i < m_numColors; ++i) { - buffer.unserialize(&m_palette[i].green); + if (!buffer.unserialize8(&m_palette[i].green)) { + return false; + } } for (uint8_t i = 0; i < m_numColors; ++i) { - buffer.unserialize(&m_palette[i].blue); + if (!buffer.unserialize8(&m_palette[i].blue)) { + return false; + } } + return true; } -void Colorset::initPalette(uint8_t numColors) +bool Colorset::initPalette(uint8_t numColors) { if (m_palette) { delete[] m_palette; m_palette = nullptr; } if (!numColors) { - return; + return true; } //m_palette = (RGBColor *)vcalloc(numColors, sizeof(RGBColor)); m_palette = new RGBColor[numColors]; if (!m_palette) { ERROR_OUT_OF_MEMORY(); - return; + return false; } m_numColors = numColors; + return true; } diff --git a/VortexEngine/src/Colors/Colorset.h b/VortexEngine/src/Colors/Colorset.h index 099b4ab177..9eb1d635f5 100644 --- a/VortexEngine/src/Colors/Colorset.h +++ b/VortexEngine/src/Colors/Colorset.h @@ -143,12 +143,12 @@ class Colorset bool onEnd() const; // serialize the colorset to save/load - void serialize(ByteStream &buffer) const; - void unserialize(ByteStream &buffer); + bool serialize(ByteStream &buffer) const; + bool unserialize(ByteStream &buffer); private: // pre-allocate the palette - void initPalette(uint8_t numColors); + bool initPalette(uint8_t numColors); // palette of colors RGBColor *m_palette; diff --git a/VortexEngine/src/Menus/MenuList/EditorConnection.cpp b/VortexEngine/src/Menus/MenuList/EditorConnection.cpp index 56cbd8953f..b47b25146e 100644 --- a/VortexEngine/src/Menus/MenuList/EditorConnection.cpp +++ b/VortexEngine/src/Menus/MenuList/EditorConnection.cpp @@ -49,7 +49,9 @@ bool EditorConnection::receiveMessage(const char *message) return false; } for (size_t i = 0; i < len; ++i) { - m_receiveBuffer.unserialize(&byte); + if (!m_receiveBuffer.unserialize8(&byte)) { + return false; + } } // if everything was read out, reset if (m_receiveBuffer.unserializerAtEnd()) { @@ -264,7 +266,9 @@ bool EditorConnection::receiveModes() return false; } // okay unserialize now, first unserialize the size - m_receiveBuffer.unserialize(&size); + if (!m_receiveBuffer.unserialize32(&size)) { + return false; + } // create a new ByteStream that will hold the full buffer of data ByteStream buf(m_receiveBuffer.rawSize()); // then copy everything from the receive buffer into the rawdata @@ -294,7 +298,9 @@ bool EditorConnection::receiveDemoMode() return false; } // okay unserialize now, first unserialize the size - m_receiveBuffer.unserialize(&size); + if (!m_receiveBuffer.unserialize32(&size)) { + return false; + } // create a new ByteStream that will hold the full buffer of data ByteStream buf(m_receiveBuffer.rawSize()); // then copy everything from the receive buffer into the rawdata diff --git a/VortexEngine/src/Menus/MenuList/Randomizer.cpp b/VortexEngine/src/Menus/MenuList/Randomizer.cpp index b0860ccd11..608cb3980e 100644 --- a/VortexEngine/src/Menus/MenuList/Randomizer.cpp +++ b/VortexEngine/src/Menus/MenuList/Randomizer.cpp @@ -40,7 +40,9 @@ bool Randomizer::init() ByteStream ledData; Pattern *pat = cur->getPattern(LED_MULTI); if (pat) { - pat->serialize(ledData); + if (!pat->serialize(ledData)) { + return false; + } } m_multiRandCtx.seed(ledData.recalcCRC()); } @@ -51,7 +53,9 @@ bool Randomizer::init() ByteStream ledData; Pattern *pat = cur->getPattern(l); if (pat) { - pat->serialize(ledData); + if (!pat->serialize(ledData)) { + return false; + } } m_singlesRandCtx[l].seed(ledData.recalcCRC()); } diff --git a/VortexEngine/src/Modes/Mode.cpp b/VortexEngine/src/Modes/Mode.cpp index 31630b1850..3d129d18b8 100644 --- a/VortexEngine/src/Modes/Mode.cpp +++ b/VortexEngine/src/Modes/Mode.cpp @@ -187,8 +187,12 @@ bool Mode::loadFromBuffer(ByteStream &modeBuffer) uint8_t major = 0; uint8_t minor = 0; // unserialize the vortex version - modeBuffer.unserialize(&major); - modeBuffer.unserialize(&minor); + if (!modeBuffer.unserialize8(&major)) { + return false; + } + if (!modeBuffer.unserialize8(&minor)) { + return false; + } // check the version for incompatibility if (!VortexEngine::checkVersion(major, minor)) { // incompatible version @@ -204,35 +208,43 @@ bool Mode::loadFromBuffer(ByteStream &modeBuffer) return true; } -void Mode::serialize(ByteStream &buffer, uint8_t numLeds) const +bool Mode::serialize(ByteStream &buffer, uint8_t numLeds) const { if (!numLeds) { numLeds = MODE_LEDCOUNT; } // serialize the number of leds - buffer.serialize(numLeds); + if (!buffer.serialize8(numLeds)) { + return false; + } // empty mode? if (!numLeds) { - return; + return true; } // serialize the flags ModeFlags flags = getFlags(); - buffer.serialize(flags); + if (!buffer.serialize8(flags)) { + return false; + } #if VORTEX_SLIM == 0 // serialiaze the multi led? if ((flags & MODE_FLAG_MULTI_LED) && m_multiPat) { // serialize the multi led - m_multiPat->serialize(buffer); + if (!m_multiPat->serialize(buffer)) { + return false; + } } #endif // if no single leds then just stop here if (!(flags & MODE_FLAG_SINGLE_LED)) { - return; + return true; } // if there are any sparse singles (spaces) then we need to // serialize an led map of which singles are set if (flags & MODE_FLAG_SPARSE_SINGLES) { - buffer.serialize((uint32_t)getSingleLedMap()); + if (!buffer.serialize32((uint32_t)getSingleLedMap())) { + return false; + } } // then iterate each single led and serialize it for (LedPos pos = LED_FIRST; pos < numLeds; ++pos) { @@ -241,12 +253,15 @@ void Mode::serialize(ByteStream &buffer, uint8_t numLeds) const continue; } // just serialize the pattern then colorset - entry->serialize(buffer); + if (!entry->serialize(buffer)) { + return false; + } // if they are all same single then only serialize one if (flags & MODE_FLAG_ALL_SAME_SINGLE) { break; } } + return true; } // this is a hairy function, but a bit of a necessary complexity @@ -255,7 +270,9 @@ bool Mode::unserialize(ByteStream &buffer) clearPattern(LED_ALL); uint8_t ledCount = LED_COUNT; // unserialize the number of leds - buffer.unserialize(&ledCount); + if (!buffer.unserialize8(&ledCount)) { + return false; + } #if FIXED_LED_COUNT == 0 // it's important that we only increase the led count if necessary // otherwise we may end up reducing our led count and only rendering @@ -272,7 +289,9 @@ bool Mode::unserialize(ByteStream &buffer) } // unserialize the flags value ModeFlags flags = 0; - buffer.unserialize(&flags); + if (!buffer.unserialize8(&flags)) { + return false; + } Pattern *firstPat = nullptr; // if there is a multi led pattern then unserialize it if (flags & MODE_FLAG_MULTI_LED) { @@ -291,6 +310,9 @@ bool Mode::unserialize(ByteStream &buffer) #else // otherwise in normal build actually unserialize it m_multiPat = PatternBuilder::unserialize(buffer); + if (!m_multiPat) { + return false; + } m_multiPat->init(); #endif } @@ -301,7 +323,9 @@ bool Mode::unserialize(ByteStream &buffer) // is there an led map to unserialize? if not default to all LedMap map = (1 << ledCount) - 1; if (flags & MODE_FLAG_SPARSE_SINGLES) { - buffer.unserialize((uint32_t *)&map); + if (!buffer.unserialize32((uint32_t *)&map)) { + return false; + } } // unserialize all singleled patterns into their positions MAP_FOREACH_LED(map) { @@ -319,6 +343,10 @@ bool Mode::unserialize(ByteStream &buffer) // otherwise unserialize the pattern like normal m_singlePats[pos] = PatternBuilder::unserialize(buffer); } + if (!m_singlePats[pos]) { + clearPattern(LED_ALL); + return false; + } m_singlePats[pos]->bind(pos); } // there is a few different possibilities here: diff --git a/VortexEngine/src/Modes/Mode.h b/VortexEngine/src/Modes/Mode.h index 9e75e2b9bb..e364483863 100644 --- a/VortexEngine/src/Modes/Mode.h +++ b/VortexEngine/src/Modes/Mode.h @@ -67,7 +67,7 @@ class Mode bool loadFromBuffer(ByteStream &saveBuffer); // save the mode to serial - void serialize(ByteStream &buffer, uint8_t numLeds = 0) const; + bool serialize(ByteStream &buffer, uint8_t numLeds = 0) const; // load the mode from serial (optional led count) bool unserialize(ByteStream &buffer); diff --git a/VortexEngine/src/Modes/Modes.cpp b/VortexEngine/src/Modes/Modes.cpp index 1544f41b5f..8d1fa63b9c 100644 --- a/VortexEngine/src/Modes/Modes.cpp +++ b/VortexEngine/src/Modes/Modes.cpp @@ -83,11 +83,11 @@ bool Modes::serializeSaveHeader(ByteStream &saveBuffer) } // NOTE: instead of global brightness the duo uses this to store the // startup mode ID. The duo doesn't offer a global brightness option - if (!saveBuffer.serialize(m_globalFlags)) { + if (!saveBuffer.serialize8(m_globalFlags)) { return false; } // serialize the global brightness - if (!saveBuffer.serialize((uint8_t)Leds::getBrightness())) { + if (!saveBuffer.serialize8((uint8_t)Leds::getBrightness())) { return false; } DEBUG_LOGF("Serialized all modes, uncompressed size: %u", saveBuffer.size()); @@ -101,8 +101,12 @@ bool Modes::unserializeSaveHeader(ByteStream &saveHeader) uint8_t major = 0; uint8_t minor = 0; // unserialize the vortex version - saveHeader.unserialize(&major); - saveHeader.unserialize(&minor); + if (!saveHeader.unserialize8(&major)) { + return false; + } + if (!saveHeader.unserialize8(&minor)) { + return false; + } // check the version for incompatibility if (!VortexEngine::checkVersion(major, minor)) { // incompatible version @@ -112,10 +116,14 @@ bool Modes::unserializeSaveHeader(ByteStream &saveHeader) // NOTE: instead of global brightness the duo uses this to store the // startup mode ID. The duo doesn't offer a global brightness option // unserialize the global brightness - saveHeader.unserialize(&m_globalFlags); + if (!saveHeader.unserialize8(&m_globalFlags)) { + return false; + } // unserialize the global brightness uint8_t brightness = 0; - saveHeader.unserialize(&brightness); + if (!saveHeader.unserialize8(&brightness)) { + return false; + } if (brightness) { Leds::setBrightness(brightness); } @@ -183,7 +191,9 @@ bool Modes::loadStorage() } // unserialize the number of modes next uint8_t numModes = 0; - headerBuffer.unserialize(&numModes); + if (!headerBuffer.unserialize8(&numModes)) { + return false; + } if (!numModes) { DEBUG_LOG("Did not find any modes"); // this kinda sucks whatever they had loaded is gone @@ -210,7 +220,7 @@ bool Modes::saveStorage() return false; } // serialize the number of modes - if (!headerBuffer.serialize(m_numModes)) { + if (!headerBuffer.serialize8(m_numModes)) { return false; } if (!Storage::write(0, headerBuffer)) { @@ -233,7 +243,9 @@ bool Modes::saveStorage() return false; } // serialize it into the target modes buffer - mode->serialize(modeBuffer); + if (!mode->serialize(modeBuffer)) { + return false; + } // just uninstansiate the mode after serializing ptr->uninstantiate(); // next mode @@ -255,7 +267,7 @@ bool Modes::saveStorage() bool Modes::serialize(ByteStream &modesBuffer) { // serialize the number of modes - if (!modesBuffer.serialize(m_numModes)) { + if (!modesBuffer.serialize8(m_numModes)) { return false; } // make sure the current mode is saved in case it has changed somehow @@ -273,7 +285,9 @@ bool Modes::serialize(ByteStream &modesBuffer) return false; } // serialize it into the target modes buffer - mode->serialize(modesBuffer); + if (!mode->serialize(modesBuffer)) { + return false; + } // just uninstansiate the mode after serializing ptr->uninstantiate(); // next mode @@ -296,7 +310,9 @@ bool Modes::unserialize(ByteStream &modesBuffer) clearModes(); // unserialize the number of modes next uint8_t numModes = 0; - modesBuffer.unserialize(&numModes); + if (!modesBuffer.unserialize8(&numModes)) { + return false; + } if (!numModes) { DEBUG_LOG("Did not find any modes"); // this kinda sucks whatever they had loaded is gone diff --git a/VortexEngine/src/Patterns/Multi/Sequencer/Sequence.cpp b/VortexEngine/src/Patterns/Multi/Sequencer/Sequence.cpp index 48733cade5..33a7239b92 100644 --- a/VortexEngine/src/Patterns/Multi/Sequencer/Sequence.cpp +++ b/VortexEngine/src/Patterns/Multi/Sequencer/Sequence.cpp @@ -39,19 +39,25 @@ PatternID PatternMap::operator[](LedPos index) const return m_patternMap[index]; } -void PatternMap::serialize(ByteStream &buffer) const +bool PatternMap::serialize(ByteStream &buffer) const { for (LedPos i = LED_FIRST; i < LED_COUNT; ++i) { // ensure the PatternID is interpreted as uint8_t - buffer.serialize((uint8_t)m_patternMap[i]); + if (!buffer.serialize8((uint8_t)m_patternMap[i])) { + return false; + } } + return true; } -void PatternMap::unserialize(ByteStream &buffer) +bool PatternMap::unserialize(ByteStream &buffer) { for (LedPos i = LED_FIRST; i < LED_COUNT; ++i) { - buffer.unserialize((uint8_t *)m_patternMap + i); + if (!buffer.unserialize8((uint8_t *)m_patternMap + i)) { + return false; + } } + return true; } ColorsetMap::ColorsetMap() : @@ -79,18 +85,24 @@ const Colorset &ColorsetMap::operator[](LedPos index) const return m_colorsetMap[index]; } -void ColorsetMap::serialize(ByteStream &buffer) const +bool ColorsetMap::serialize(ByteStream &buffer) const { for (LedPos i = LED_FIRST; i < LED_COUNT; ++i) { - m_colorsetMap[i].serialize(buffer); + if (!m_colorsetMap[i].serialize(buffer)) { + return false; + } } + return true; } -void ColorsetMap::unserialize(ByteStream &buffer) +bool ColorsetMap::unserialize(ByteStream &buffer) { for (LedPos i = LED_FIRST; i < LED_COUNT; ++i) { - m_colorsetMap[i].unserialize(buffer); + if (!m_colorsetMap[i].unserialize(buffer)) { + return false; + } } + return true; } // Make an array of sequence steps to create a sequenced pattern @@ -107,18 +119,32 @@ SequenceStep::SequenceStep(const SequenceStep &other) : { } -void SequenceStep::serialize(ByteStream &buffer) const +bool SequenceStep::serialize(ByteStream &buffer) const { - buffer.serialize(m_duration); - m_patternMap.serialize(buffer); - m_colorsetMap.serialize(buffer); + if (!buffer.serialize16(m_duration)) { + return false; + } + if (!m_patternMap.serialize(buffer)) { + return false; + } + if (!m_colorsetMap.serialize(buffer)) { + return false; + } + return true; } -void SequenceStep::unserialize(ByteStream &buffer) +bool SequenceStep::unserialize(ByteStream &buffer) { - buffer.unserialize(&m_duration); - m_patternMap.unserialize(buffer); - m_colorsetMap.unserialize(buffer); + if (!buffer.unserialize16(&m_duration)) { + return false; + } + if (!m_patternMap.unserialize(buffer)) { + return false; + } + if (!m_colorsetMap.unserialize(buffer)) { + return false; + } + return true; } Sequence::Sequence() : @@ -222,20 +248,30 @@ void Sequence::clear() m_numSteps = 0; } -void Sequence::serialize(ByteStream &buffer) const +bool Sequence::serialize(ByteStream &buffer) const { - buffer.serialize(m_numSteps); + if (!buffer.serialize8(m_numSteps)) { + return false; + } for (uint8_t i = 0; i < m_numSteps; ++i) { - m_sequenceSteps[i].serialize(buffer); + if (!m_sequenceSteps[i].serialize(buffer)) { + return false; + } } + return true; } -void Sequence::unserialize(ByteStream &buffer) +bool Sequence::unserialize(ByteStream &buffer) { - buffer.unserialize(&m_numSteps); + if (!buffer.unserialize8(&m_numSteps)) { + return false; + } for (uint8_t i = 0; i < m_numSteps; ++i) { - m_sequenceSteps[i].unserialize(buffer); + if (!m_sequenceSteps[i].unserialize(buffer)) { + return false; + } } + return true; } uint8_t Sequence::numSteps() const diff --git a/VortexEngine/src/Patterns/Multi/Sequencer/Sequence.h b/VortexEngine/src/Patterns/Multi/Sequencer/Sequence.h index 4ca766a694..37ddf75911 100644 --- a/VortexEngine/src/Patterns/Multi/Sequencer/Sequence.h +++ b/VortexEngine/src/Patterns/Multi/Sequencer/Sequence.h @@ -22,8 +22,8 @@ class PatternMap PatternID operator[](LedPos index) const; // serialize and unserialize a pattern map - void serialize(ByteStream &buffer) const; - void unserialize(ByteStream &buffer); + bool serialize(ByteStream &buffer) const; + bool unserialize(ByteStream &buffer); // public list of pattern IDs for each led PatternID m_patternMap[LED_COUNT]; @@ -41,8 +41,8 @@ class ColorsetMap const Colorset &operator[](LedPos index) const; // serialize and unserialize a colorset map - void serialize(ByteStream &buffer) const; - void unserialize(ByteStream &buffer); + bool serialize(ByteStream &buffer) const; + bool unserialize(ByteStream &buffer); // public list of pattern IDs for each led Colorset m_colorsetMap[LED_COUNT]; @@ -57,8 +57,8 @@ class SequenceStep SequenceStep(const SequenceStep &other); // serialize and unserialize a step in the sequencer - void serialize(ByteStream &buffer) const; - void unserialize(ByteStream &buffer); + bool serialize(ByteStream &buffer) const; + bool unserialize(ByteStream &buffer); // public members to allow for easy initialization of an array of SequenceSteps uint16_t m_duration; @@ -85,8 +85,8 @@ class Sequence uint8_t addStep(uint16_t duration, const PatternMap &patternMap, const ColorsetMap &colorsetMap = Colorset()); void clear(); - void serialize(ByteStream &buffer) const; - void unserialize(ByteStream &buffer); + bool serialize(ByteStream &buffer) const; + bool unserialize(ByteStream &buffer); uint8_t numSteps() const; const SequenceStep &operator[](uint8_t index) const; diff --git a/VortexEngine/src/Patterns/Pattern.cpp b/VortexEngine/src/Patterns/Pattern.cpp index 6078cc4ed1..768a9966f2 100644 --- a/VortexEngine/src/Patterns/Pattern.cpp +++ b/VortexEngine/src/Patterns/Pattern.cpp @@ -54,10 +54,14 @@ void Pattern::skip(uint32_t ticks) #endif // must override the serialize routine to save the pattern -void Pattern::serialize(ByteStream &buffer) const +bool Pattern::serialize(ByteStream &buffer) const { - buffer.serialize((uint8_t)m_patternID); - m_colorset.serialize(buffer); + if (!buffer.serialize8((uint8_t)m_patternID)) { + return false; + } + if (!m_colorset.serialize(buffer)) { + return false; + } PatternArgs args; getArgs(args); PatternArgs defaults = PatternBuilder::getDefaultArgs(m_patternID); @@ -68,16 +72,21 @@ void Pattern::serialize(ByteStream &buffer) const ARGMAP_SET(argmap, i); } } - args.serialize(buffer, argmap); + if (!args.serialize(buffer, argmap)) { + return false; + } + return true; } // must override unserialize to load patterns -void Pattern::unserialize(ByteStream &buffer) +bool Pattern::unserialize(ByteStream &buffer) { // don't unserialize the pattern ID because it is already // unserialized by the pattern builder to decide which pattern // to instantiate, instead only unserialize the colorset - m_colorset.unserialize(buffer); + if (!m_colorset.unserialize(buffer)) { + return false; + } // start with the default args for this pattern PatternArgs args = PatternBuilder::getDefaultArgs(m_patternID); // then unserialize any different args overtop of the defaults @@ -85,6 +94,7 @@ void Pattern::unserialize(ByteStream &buffer) // if any args were unserialized, set them setArgs(args); } + return true; } void Pattern::setArg(uint8_t index, uint8_t value) diff --git a/VortexEngine/src/Patterns/Pattern.h b/VortexEngine/src/Patterns/Pattern.h index e659abf885..0a3454ce04 100644 --- a/VortexEngine/src/Patterns/Pattern.h +++ b/VortexEngine/src/Patterns/Pattern.h @@ -76,8 +76,8 @@ class Pattern #endif // serialize and unserialize a pattern to a bytestream - void serialize(ByteStream &buffer) const; - void unserialize(ByteStream &buffer); + bool serialize(ByteStream &buffer) const; + bool unserialize(ByteStream &buffer); // get or set a single arg void setArg(uint8_t index, uint8_t value); diff --git a/VortexEngine/src/Patterns/PatternArgs.cpp b/VortexEngine/src/Patterns/PatternArgs.cpp index a26389dff8..8ea98aa93d 100644 --- a/VortexEngine/src/Patterns/PatternArgs.cpp +++ b/VortexEngine/src/Patterns/PatternArgs.cpp @@ -171,23 +171,32 @@ uint8_t PatternArgs::operator[](int index) const return args[index]; } -void PatternArgs::serialize(ByteStream &buffer, ArgMap argmap) const +bool PatternArgs::serialize(ByteStream &buffer, ArgMap argmap) const { - buffer.serialize(argmap); + if (!buffer.serialize8(argmap)) { + return false; + } for (uint8_t i = 0; i < MAX_ARGS; ++i) { if (ARGMAP_ISSET(argmap, i)) { - buffer.serialize(args[i]); + if (!buffer.serialize8(args[i])) { + return false; + } } } + return true; } ArgMap PatternArgs::unserialize(ByteStream &buffer) { ArgMap argmap = ARG_NONE; - buffer.unserialize(&argmap); + if (!buffer.unserialize8(&argmap)) { + return ARG_NONE; + } for (uint8_t i = 0; i < MAX_ARGS; ++i) { if (ARGMAP_ISSET(argmap, i)) { - buffer.unserialize(args + i); + if (!buffer.unserialize8(args + i)) { + break; + } } } return argmap; diff --git a/VortexEngine/src/Patterns/PatternArgs.h b/VortexEngine/src/Patterns/PatternArgs.h index 942aa9f050..f195c1916c 100644 --- a/VortexEngine/src/Patterns/PatternArgs.h +++ b/VortexEngine/src/Patterns/PatternArgs.h @@ -71,7 +71,7 @@ class PatternArgs uint8_t operator[](int index) const; // serialize the pattern args with a specific mapping of which args to store - void serialize(ByteStream &buffer, ArgMap argmap = ARG_ALL) const; + bool serialize(ByteStream &buffer, ArgMap argmap = ARG_ALL) const; // unserialize the pattern args and return the argmap of which args were loaded // NOTE: You should start with an instance of the default args before you unserialize ArgMap unserialize(ByteStream &buffer); diff --git a/VortexEngine/src/Patterns/PatternBuilder.cpp b/VortexEngine/src/Patterns/PatternBuilder.cpp index 0f0b15587d..3468945ed0 100644 --- a/VortexEngine/src/Patterns/PatternBuilder.cpp +++ b/VortexEngine/src/Patterns/PatternBuilder.cpp @@ -95,11 +95,18 @@ MultiLedPattern *PatternBuilder::makeMulti(PatternID id, const PatternArgs *args Pattern *PatternBuilder::unserialize(ByteStream &buffer) { - Pattern *pat = make((PatternID)buffer.unserialize8()); + PatternID id = PATTERN_NONE; + if (!buffer.unserialize8((uint8_t *)&id)) { + return nullptr; + } + Pattern *pat = make(id); if (!pat) { return nullptr; } - pat->unserialize(buffer); + if (!pat->unserialize(buffer)) { + delete pat; + return nullptr; + } return pat; } diff --git a/VortexEngine/src/Serial/ByteStream.cpp b/VortexEngine/src/Serial/ByteStream.cpp index 73089fcc75..be5abe30d7 100644 --- a/VortexEngine/src/Serial/ByteStream.cpp +++ b/VortexEngine/src/Serial/ByteStream.cpp @@ -338,7 +338,7 @@ bool ByteStream::isCRCDirty() const return (m_pData && (m_pData->flags & BUFFER_FLAG_DIRTY) != 0); } -bool ByteStream::serialize(uint8_t byte) +bool ByteStream::serialize8(uint8_t byte) { //DEBUG_LOGF("Serialize8(): %u", byte); if (!m_pData || (m_pData->size + sizeof(uint8_t)) > m_capacity) { @@ -354,7 +354,7 @@ bool ByteStream::serialize(uint8_t byte) return true; } -bool ByteStream::serialize(uint16_t bytes) +bool ByteStream::serialize16(uint16_t bytes) { //DEBUG_LOGF("Serialize16(): %u", bytes); if (!m_pData || (m_pData->size + sizeof(uint16_t)) > m_capacity) { @@ -370,7 +370,7 @@ bool ByteStream::serialize(uint16_t bytes) return true; } -bool ByteStream::serialize(uint32_t bytes) +bool ByteStream::serialize32(uint32_t bytes) { //DEBUG_LOGF("Serialize32(): %u", bytes); if (!m_pData || (m_pData->size + sizeof(uint32_t)) > m_capacity) { @@ -414,7 +414,7 @@ bool ByteStream::unserializerAtEnd() const } // unserialize data and walk the buffer that many bytes -bool ByteStream::unserialize(uint8_t *byte) +bool ByteStream::unserialize8(uint8_t *byte) { if (!m_pData || m_position >= m_pData->size || (m_pData->size - m_position) < sizeof(uint8_t)) { return false; @@ -425,7 +425,7 @@ bool ByteStream::unserialize(uint8_t *byte) return true; } -bool ByteStream::unserialize(uint16_t *bytes) +bool ByteStream::unserialize16(uint16_t *bytes) { if (!m_pData || m_position >= m_pData->size || (m_pData->size - m_position) < sizeof(uint16_t)) { return false; @@ -436,7 +436,7 @@ bool ByteStream::unserialize(uint16_t *bytes) return true; } -bool ByteStream::unserialize(uint32_t *bytes) +bool ByteStream::unserialize32(uint32_t *bytes) { if (!m_pData || m_position >= m_pData->size || (m_pData->size - m_position) < sizeof(uint32_t)) { return false; @@ -447,27 +447,6 @@ bool ByteStream::unserialize(uint32_t *bytes) return true; } -uint8_t ByteStream::unserialize8() -{ - uint8_t byte = 0; - unserialize(&byte); - return byte; -} - -uint16_t ByteStream::unserialize16() -{ - uint16_t bytes = 0; - unserialize(&bytes); - return bytes; -} - -uint32_t ByteStream::unserialize32() -{ - uint32_t bytes = 0; - unserialize(&bytes); - return bytes; -} - uint8_t ByteStream::peek8() const { if (!m_pData) { @@ -492,33 +471,6 @@ uint32_t ByteStream::peek32() const return *(uint32_t *)frontUnserializer(); } -// read the data from a flash storage -// overload += for appending buffer -ByteStream &ByteStream::operator+=(const ByteStream &rhs) -{ - append(rhs); - return *this; -} - -// also overload += for appending bytes -ByteStream &ByteStream::operator+=(const uint8_t &rhs) -{ - serialize(rhs); - return *this; -} - -ByteStream &ByteStream::operator+=(const uint16_t &rhs) -{ - serialize(rhs); - return *this; -} - -ByteStream &ByteStream::operator+=(const uint32_t &rhs) -{ - serialize(rhs); - return *this; -} - bool ByteStream::is_compressed() const { if (!m_pData) { diff --git a/VortexEngine/src/Serial/ByteStream.h b/VortexEngine/src/Serial/ByteStream.h index 16cff1bcd9..558cfc6e9f 100644 --- a/VortexEngine/src/Serial/ByteStream.h +++ b/VortexEngine/src/Serial/ByteStream.h @@ -78,9 +78,9 @@ class ByteStream bool isCRCDirty() const; // serialize a byte into the buffer - bool serialize(uint8_t byte); - bool serialize(uint16_t bytes); - bool serialize(uint32_t bytes); + bool serialize8(uint8_t byte); + bool serialize16(uint16_t bytes); + bool serialize32(uint32_t bytes); // reset the unserializer index void resetUnserializer(); @@ -90,26 +90,14 @@ class ByteStream bool unserializerAtEnd() const; // serialize a byte into the buffer - bool unserialize(uint8_t *byte); - bool unserialize(uint16_t *bytes); - bool unserialize(uint32_t *bytes); - - // same thing but via return value - uint8_t unserialize8(); - uint16_t unserialize16(); - uint32_t unserialize32(); + bool unserialize8(uint8_t *byte); + bool unserialize16(uint16_t *bytes); + bool unserialize32(uint32_t *bytes); uint8_t peek8() const; uint16_t peek16() const; uint32_t peek32() const; - // overload += for appending buffer - ByteStream &operator+=(const ByteStream &rhs); - // also overload += for appending bytes - ByteStream &operator+=(const uint8_t &rhs); - ByteStream &operator+=(const uint16_t &rhs); - ByteStream &operator+=(const uint32_t &rhs); - // overload [] for array access (no bounds check lol) uint8_t &operator[](uint32_t index) { return m_pData->buf[index]; } // overload (uint8_t *) for cast to buffer diff --git a/VortexEngine/src/Serial/Serial.cpp b/VortexEngine/src/Serial/Serial.cpp index 2d3c4bd343..a3433b79fd 100644 --- a/VortexEngine/src/Serial/Serial.cpp +++ b/VortexEngine/src/Serial/Serial.cpp @@ -127,7 +127,7 @@ void SerialComs::read(ByteStream &byteStream) #else byte = Serial.read(); #endif - byteStream.serialize(byte); + byteStream.serialize8(byte); } while (--amt > 0); #endif } diff --git a/VortexEngine/src/VortexEngine.cpp b/VortexEngine/src/VortexEngine.cpp index 970b1a6421..53b7f944b8 100644 --- a/VortexEngine/src/VortexEngine.cpp +++ b/VortexEngine/src/VortexEngine.cpp @@ -203,8 +203,13 @@ void VortexEngine::runMainLogic() bool VortexEngine::serializeVersion(ByteStream &stream) { // serialize the vortex version - return stream.serialize((uint8_t)VORTEX_VERSION_MAJOR) && - stream.serialize((uint8_t)VORTEX_VERSION_MINOR); + if (!stream.serialize8((uint8_t)VORTEX_VERSION_MAJOR)) { + return false; + } + if (!stream.serialize8((uint8_t)VORTEX_VERSION_MINOR)) { + return false; + } + return true; } bool VortexEngine::checkVersion(uint8_t major, uint8_t minor) From 6e57d56d8c75f87c381d05178900f269beeece7c Mon Sep 17 00:00:00 2001 From: Unreal-Dan <72595612+Unreal-Dan@users.noreply.github.com> Date: Sat, 27 Apr 2024 02:36:16 -0700 Subject: [PATCH 2/2] Separate save header and mode loading (#240) --- VortexEngine/VortexLib/VortexLib.cpp | 5 ++- VortexEngine/src/Modes/Modes.cpp | 60 +++++++++++++++++--------- VortexEngine/src/Modes/Modes.h | 21 ++++++--- VortexEngine/src/Serial/ByteStream.cpp | 2 +- VortexEngine/src/VortexEngine.cpp | 6 +++ 5 files changed, 66 insertions(+), 28 deletions(-) diff --git a/VortexEngine/VortexLib/VortexLib.cpp b/VortexEngine/VortexLib/VortexLib.cpp index 50817c5ff1..dfcfa06f47 100644 --- a/VortexEngine/VortexLib/VortexLib.cpp +++ b/VortexEngine/VortexLib/VortexLib.cpp @@ -659,8 +659,9 @@ bool Vortex::init(VortexCallbacks *callbacks) // init the engine VortexEngine::init(); - // clear the modes - //Modes::clearModes(); + // load the modes + // TODO: don't load modes here? separate api? + Modes::load(); // save and set undo buffer doSave(); diff --git a/VortexEngine/src/Modes/Modes.cpp b/VortexEngine/src/Modes/Modes.cpp index 8d1fa63b9c..95fb11b2fa 100644 --- a/VortexEngine/src/Modes/Modes.cpp +++ b/VortexEngine/src/Modes/Modes.cpp @@ -16,6 +16,7 @@ #include "../Log/Log.h" // static members +bool Modes::m_loaded = false; uint8_t Modes::m_curMode = 0; uint8_t Modes::m_numModes = 0; // the current instantiated mode and it's respective link @@ -34,15 +35,10 @@ bool Modes::init() test(); return true; #endif - // try to load the saved settings or set defaults - if (!loadStorage()) { - if (!setDefaults()) { - return false; - } - if (!saveStorage()) { - return false; - } - } + ByteStream headerBuffer; + Storage::read(0, headerBuffer); + unserializeSaveHeader(headerBuffer); + m_loaded = false; #ifdef VORTEX_LIB // enable the adv menus by default in vortex lib m_globalFlags |= MODES_FLAG_ADV_MENUS; @@ -55,6 +51,24 @@ void Modes::cleanup() clearModes(); } +bool Modes::load() +{ + if (m_loaded) { + return true; + } + // try to load the saved settings or set defaults + if (!loadStorage()) { + if (!setDefaults()) { + return false; + } + if (!saveStorage()) { + return false; + } + } + m_loaded = true; + return true; +} + void Modes::play() { if (!m_numModes) { @@ -210,6 +224,22 @@ bool Modes::loadStorage() return true; } +bool Modes::saveHeader() +{ + ByteStream headerBuffer(MAX_MODE_SIZE); + if (!serializeSaveHeader(headerBuffer)) { + return false; + } + // serialize the number of modes + if (!headerBuffer.serialize8(m_numModes)) { + return false; + } + if (!Storage::write(0, headerBuffer)) { + return false; + } + return true; +} + // NOTE: Flash storage is limited to about 10,000 writes so // use this function sparingly! bool Modes::saveStorage() @@ -672,17 +702,7 @@ bool Modes::setFlag(uint8_t flag, bool enable, bool save) m_globalFlags &= ~flag; } DEBUG_LOGF("Toggled instant on/off to %s", enable ? "on" : "off"); - return !save || saveStorage(); -} - -bool Modes::getFlag(uint8_t flag) -{ - return ((m_globalFlags & flag) != 0); -} - -void Modes::resetFlags() -{ - m_globalFlags = 0; + return !save || saveHeader(); } #ifdef VORTEX_LIB diff --git a/VortexEngine/src/Modes/Modes.h b/VortexEngine/src/Modes/Modes.h index 772e86fd81..9df35c990c 100644 --- a/VortexEngine/src/Modes/Modes.h +++ b/VortexEngine/src/Modes/Modes.h @@ -36,6 +36,9 @@ class Modes static bool init(); static void cleanup(); + // load modes so they are ready to play + static bool load(); + // play the current mode static void play(); @@ -43,6 +46,9 @@ class Modes static bool saveToBuffer(ByteStream &saveBuffer); static bool loadFromBuffer(ByteStream &saveBuffer); + // save the header to storage + static bool saveHeader(); + // full save/load to/from storage static bool loadStorage(); static bool saveStorage(); @@ -107,9 +113,10 @@ class Modes // set or get flags static bool setFlag(uint8_t flag, bool enable, bool save = true); - static bool getFlag(uint8_t flag); + static bool getFlag(uint8_t flag) { return ((m_globalFlags & flag) != 0); } + // reset flags to factory default (must save after) - static void resetFlags(); + static void resetFlags() { m_globalFlags = 0; } // inline functions to toggle the various flags static bool setOneClickMode(bool enable, bool save = true) { @@ -154,9 +161,6 @@ class Modes #endif private: - static bool serializeSaveHeader(ByteStream &saveBuffer); - static bool unserializeSaveHeader(ByteStream &saveBuffer); - // linked list of internal mode storage class ModeLink { public: @@ -204,6 +208,10 @@ class Modes ModeLink *m_prev; }; + // save load the savefile header from storage + static bool serializeSaveHeader(ByteStream &saveBuffer); + static bool unserializeSaveHeader(ByteStream &saveBuffer); + // fetch a link from the chain by index static ModeLink *getModeLink(uint32_t index); @@ -212,6 +220,9 @@ class Modes static Mode *initCurMode(bool force = false); static bool saveCurMode(); + // whether modes have been loaded + static bool m_loaded; + // the current mode we're on static uint8_t m_curMode; diff --git a/VortexEngine/src/Serial/ByteStream.cpp b/VortexEngine/src/Serial/ByteStream.cpp index be5abe30d7..f77ebae5e9 100644 --- a/VortexEngine/src/Serial/ByteStream.cpp +++ b/VortexEngine/src/Serial/ByteStream.cpp @@ -319,7 +319,7 @@ uint32_t ByteStream::recalcCRC(bool force) void ByteStream::sanity() { // to ensure size never exceeds the buffer capacity - if (m_pData->size > m_capacity) { + if (m_pData && m_pData->size > m_capacity) { m_pData->size = m_capacity; } } diff --git a/VortexEngine/src/VortexEngine.cpp b/VortexEngine/src/VortexEngine.cpp index 53b7f944b8..90daee6d18 100644 --- a/VortexEngine/src/VortexEngine.cpp +++ b/VortexEngine/src/VortexEngine.cpp @@ -172,6 +172,12 @@ void VortexEngine::runMainLogic() // the current tick uint32_t now = Time::getCurtime(); + // load modes if necessary + if (!Modes::load()) { + // don't do anything if modes couldn't load + return; + } + // if the menus are open and running then just return if (Menus::run()) { return;