diff --git a/Makefile b/Makefile index 9833544349..2dd51adf9a 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: all install build upload clean +.PHONY: all install build upload clean compute_version ARDUINO_CLI = ./bin/arduino-cli --verbose BOARD = esp32:esp32:XIAO_ESP32C3 @@ -10,19 +10,6 @@ CONFIG_FILE = $(HOME)/.arduino15/arduino-cli.yaml # The branch/tag suffix for this device BRANCH_SUFFIX=s -# Fetch tags, determine version numbers based on the latest tag, and slice off the branch suffix -VORTEX_VERSION_MAJOR ?= $(shell git fetch --depth=1 origin +refs/tags/*:refs/tags/* &> /dev/null && git tag --list "*$(BRANCH_SUFFIX)" | sort -V | tail -n1 | cut -d. -f1) -VORTEX_VERSION_MINOR ?= $(shell git tag --list "*$(BRANCH_SUFFIX)" | sort -V | tail -n1 | sed 's/$(BRANCH_SUFFIX)$$//' | cut -d. -f2) -VORTEX_BUILD_NUMBER ?= $(shell git rev-list --count HEAD) - -# If no tags are found, default to 0.1.0 -VORTEX_VERSION_MAJOR := $(if $(VORTEX_VERSION_MAJOR),$(VORTEX_VERSION_MAJOR),0) -VORTEX_VERSION_MINOR := $(if $(VORTEX_VERSION_MINOR),$(VORTEX_VERSION_MINOR),1) -VORTEX_BUILD_NUMBER := $(if $(VORTEX_BUILD_NUMBER),$(VORTEX_BUILD_NUMBER),0) - -# Combine into a full version number -VORTEX_VERSION_NUMBER := $(VORTEX_VERSION_MAJOR).$(VORTEX_VERSION_MINOR).$(VORTEX_BUILD_NUMBER) - DEFINES=\ -D VORTEX_VERSION_MAJOR=$(VORTEX_VERSION_MAJOR) \ -D VORTEX_VERSION_MINOR=$(VORTEX_VERSION_MINOR) \ @@ -50,7 +37,7 @@ install: $(ARDUINO_CLI) core install esp32:esp32 --config-file $(CONFIG_FILE) $(ARDUINO_CLI) lib install FastLED -build: +build: compute_version $(ARDUINO_CLI) compile --fqbn $(BOARD) $(PROJECT_NAME) \ --config-file $(CONFIG_FILE) \ --build-path $(BUILD_PATH) \ @@ -67,3 +54,13 @@ core-list: clean: rm -rf $(BUILD_PATH) +# calculate the version number of the build +compute_version: + $(eval LATEST_TAG ?= $(shell git fetch --depth=1 origin +refs/tags/*:refs/tags/* &> /dev/null && git tag --list "*$(BRANCH_SUFFIX)" | sort -V | tail -n1)) + $(eval VORTEX_VERSION_MAJOR ?= $(shell echo $(LATEST_TAG) | cut -d. -f1)) + $(eval VORTEX_VERSION_MINOR ?= $(shell echo $(LATEST_TAG) | sed 's/$(BRANCH_SUFFIX)$$//' | cut -d. -f2)) + $(eval VORTEX_BUILD_NUMBER ?= $(shell git rev-list --count $(LATEST_TAG)..HEAD)) + $(eval VORTEX_VERSION_MAJOR := $(if $(VORTEX_VERSION_MAJOR),$(VORTEX_VERSION_MAJOR),0)) + $(eval VORTEX_VERSION_MINOR := $(if $(VORTEX_VERSION_MINOR),$(VORTEX_VERSION_MINOR),1)) + $(eval VORTEX_BUILD_NUMBER := $(if $(VORTEX_BUILD_NUMBER),$(VORTEX_BUILD_NUMBER),0)) + $(eval VORTEX_VERSION_NUMBER := $(VORTEX_VERSION_MAJOR).$(VORTEX_VERSION_MINOR).$(VORTEX_BUILD_NUMBER)) diff --git a/README.md b/README.md index 38ce7e492d..0fb3f4b72f 100644 --- a/README.md +++ b/README.md @@ -20,30 +20,37 @@ This branch holds the master firmware that forms the foundation for all other br ### Desktop (Dynamic Desktop Library) ![Desktop](https://github.com/StoneOrbits/VortexEngine/actions/workflows/desktop_build.yml/badge.svg?branch=desktop) +![Desktop Version](https://img.shields.io/endpoint?url=https://vortex.community/downloads/json/desktop/library?badge=true) This is a sophisticated enhancement of the core to turn Vortex into a powerful desktop library that can be used to design various desktop applications like the Vortex Editor. ### Orbit (Vortex Classic Orbit) ![Orbit](https://github.com/StoneOrbits/VortexEngine/actions/workflows/orbit_build.yml/badge.svg?branch=orbit) +![Orbit Version](https://img.shields.io/endpoint?url=https://vortex.community/downloads/json/orbit/firmware?badge=true) The firmware specifically designed for the Vortex Classic Orbit resides here. ### Handle (Vortex Omega Handles) ![Handle](https://github.com/StoneOrbits/VortexEngine/actions/workflows/handle_build.yml/badge.svg?branch=handle) +![Handle Version](https://img.shields.io/endpoint?url=https://vortex.community/downloads/json/handle/firmware?badge=true) This branch hosts the firmware designed for the Vortex Omega Handles. ### Gloves (Vortex Wired Gloveset) ![Gloves](https://github.com/StoneOrbits/VortexEngine/actions/workflows/gloves_build.yml/badge.svg?branch=gloves) +![Gloves Version](https://img.shields.io/endpoint?url=https://vortex.community/downloads/json/gloves/firmware?badge=true) Here, you'll find the firmware for the Vortex Wired Gloveset. ### Duo (Vortex Duo Modular Chip) ![Duo](https://github.com/StoneOrbits/VortexEngine/actions/workflows/duo_build.yml/badge.svg?branch=duo) +![Duo Version](https://img.shields.io/endpoint?url=https://vortex.community/downloads/json/duo/firmware?badge=true) This branch contains the firmware for the Vortex Duo Modular Chip. ### Chromadeck (Vortex Chromadeck Mode Bank) ![Chromadeck](https://github.com/StoneOrbits/VortexEngine/actions/workflows/chromadeck_build.yml/badge.svg?branch=chromadeck) +![Chromadeck Version](https://img.shields.io/endpoint?url=https://vortex.community/downloads/json/chromadeck/firmware?badge=true) This branch contains the firmware for the Vortex Chromadeck dynamic storage and transfer tool. ### Spark (Spark Orbit and Handle) ![Spark](https://github.com/StoneOrbits/VortexEngine/actions/workflows/spark_build.yml/badge.svg?branch=spark) +![Spark Version](https://img.shields.io/endpoint?url=https://vortex.community/downloads/json/spark/firmware?badge=true) This branch contains the firmware for the Vortex Spark firmware, this powers multiple devices: the Spark Orbit and Spark Handle. To access a device's firmware, simply switch to its corresponding branch. diff --git a/VortexEngine/VortexCLI/Makefile b/VortexEngine/VortexCLI/Makefile index 5b4dc85bf4..9f3599ccc2 100644 --- a/VortexEngine/VortexCLI/Makefile +++ b/VortexEngine/VortexCLI/Makefile @@ -2,7 +2,7 @@ .SUFFIXES: # List all make targets which are not filenames -.PHONY: all tests clean wasm +.PHONY: all tests clean wasm compute_version # compiler tool definitions ifdef WASM @@ -19,9 +19,16 @@ RANLIB=ranlib CFLAGS=-O2 -g -Wall +# The branch/tag suffix for this device +BRANCH_SUFFIX=s + # compiler defines DEFINES=\ - -D VORTEX_LIB + -D VORTEX_LIB \ + -D VORTEX_VERSION_MAJOR=$(VORTEX_VERSION_MAJOR) \ + -D VORTEX_VERSION_MINOR=$(VORTEX_VERSION_MINOR) \ + -D VORTEX_BUILD_NUMBER=$(VORTEX_BUILD_NUMBER) \ + -D VORTEX_VERSION_NUMBER=$(VORTEX_VERSION_NUMBER) # compiler include paths INCLUDES=\ @@ -107,8 +114,8 @@ wasm: FORCE env WASM=1 $(MAKE) # target for vortex lib -vortex: $(DEPS) - $(CC) $(CFLAGS) $^ -o $@ $(LLIBS) +vortex: compute_version $(DEPS) + $(CC) $(CFLAGS) $(DEPS) -o $@ $(LLIBS) # catch-all make target to generate .o and .d files %.o: %.cpp @@ -129,6 +136,17 @@ clean: @$(RM) $(DFILES) $(OBJS) $(TARGETS) $(TESTS) vortex.wasm *.txt FlashStorage.flash $(MAKE) -C ../VortexLib clean +# calculate the version number of the build +compute_version: + $(eval LATEST_TAG ?= $(shell git fetch --depth=1 origin +refs/tags/*:refs/tags/* &> /dev/null && git tag --list "*$(BRANCH_SUFFIX)" | sort -V | tail -n1)) + $(eval VORTEX_VERSION_MAJOR ?= $(shell echo $(LATEST_TAG) | cut -d. -f1)) + $(eval VORTEX_VERSION_MINOR ?= $(shell echo $(LATEST_TAG) | sed 's/$(BRANCH_SUFFIX)$$//' | cut -d. -f2)) + $(eval VORTEX_BUILD_NUMBER ?= $(shell git rev-list --count $(LATEST_TAG)..HEAD)) + $(eval VORTEX_VERSION_MAJOR := $(if $(VORTEX_VERSION_MAJOR),$(VORTEX_VERSION_MAJOR),0)) + $(eval VORTEX_VERSION_MINOR := $(if $(VORTEX_VERSION_MINOR),$(VORTEX_VERSION_MINOR),1)) + $(eval VORTEX_BUILD_NUMBER := $(if $(VORTEX_BUILD_NUMBER),$(VORTEX_BUILD_NUMBER),0)) + $(eval VORTEX_VERSION_NUMBER := $(VORTEX_VERSION_MAJOR).$(VORTEX_VERSION_MINOR).$(VORTEX_BUILD_NUMBER)) + # Now include our target dependency files # the hyphen means ignore non-existent files -include $(DFILES) diff --git a/VortexEngine/VortexCLI/VortexCLI.cpp b/VortexEngine/VortexCLI/VortexCLI.cpp index 346cb96c39..89ca527e04 100644 --- a/VortexEngine/VortexCLI/VortexCLI.cpp +++ b/VortexEngine/VortexCLI/VortexCLI.cpp @@ -485,7 +485,7 @@ bool VortexCLI::init(int argc, char *argv[]) } // do the vortex init/setup - Vortex::init(); + Vortex::initEx(); // configure the vortex engine as the parameters dictate Vortex::setInstantTimestep(m_noTimestep); @@ -632,10 +632,10 @@ void VortexCLI::cleanup() } if (m_jsonMode & JSON_MODE_WRITE_STDOUT) { // dump the current save in json format - Vortex::dumpJson(nullptr, m_jsonPretty); + Vortex::printJson(m_jsonPretty); } if (m_jsonMode & JSON_MODE_WRITE_FILE) { - Vortex::dumpJson(m_jsonOutFile.c_str(), m_jsonPretty); + Vortex::printJsonToFile(m_jsonOutFile.c_str(), m_jsonPretty); printf("Wrote JSON to file [%s]\n", m_jsonOutFile.c_str()); } if (m_writeSaveFile.length() > 0) { diff --git a/VortexEngine/VortexLib/Makefile b/VortexEngine/VortexLib/Makefile index 80d0337572..d51ed0f74c 100644 --- a/VortexEngine/VortexLib/Makefile +++ b/VortexEngine/VortexLib/Makefile @@ -2,7 +2,7 @@ .SUFFIXES: # List all make targets which are not filenames -.PHONY: all tests clean wasm +.PHONY: all tests clean wasm compute_version # compiler tool definitions ifdef WASM @@ -24,17 +24,8 @@ ifndef WASM CFLAGS += -g endif -# Determine the current version based on Git tags -VORTEX_VERSION_MAJOR ?= $(shell git fetch --depth=1 origin +refs/tags/*:refs/tags/* &> /dev/null && git tag --list | grep -E "^[[:digit:]]+\.[[:digit:]]+\$$" | sort -V | tail -n1 | cut -d. -f1) -VORTEX_VERSION_MINOR ?= $(shell git tag --list | grep -E "^[[:digit:]]+\.[[:digit:]]+\$$" | sort -V | tail -n1 | cut -d. -f2) -VORTEX_BUILD_NUMBER ?= $(shell git rev-list --count HEAD) - -# If no tags are found, default to 0.1.0 -VORTEX_VERSION_MAJOR := $(if $(VORTEX_VERSION_MAJOR),$(VORTEX_VERSION_MAJOR),0) -VORTEX_VERSION_MINOR := $(if $(VORTEX_VERSION_MINOR),$(VORTEX_VERSION_MINOR),1) -VORTEX_BUILD_NUMBER := $(if $(VORTEX_BUILD_NUMBER),$(VORTEX_BUILD_NUMBER),0) - -VORTEX_VERSION_NUMBER ?= $(VORTEX_VERSION_MAJOR).$(VORTEX_VERSION_MINOR).$(VORTEX_BUILD_NUMBER) +# The branch/tag suffix for this device +BRANCH_SUFFIX=s # compiler defines DEFINES=\ @@ -132,11 +123,11 @@ wasm: FORCE env WASM=1 $(MAKE) # target for vortex lib -vortex.a: $(DEPS) - $(AR) $@ $^ +vortex.a: compute_version $(DEPS) + $(AR) $@ $(DEPS) -VortexLib.js: $(DEPS) - $(CC) $(LDFLAGS) $^ -o $@ $(LLIBS) +VortexLib.js: compute_version $(DEPS) + $(CC) $(LDFLAGS) $(DEPS) -o $@ $(LLIBS) # catch-all make target to generate .o and .d files %.o: %.cpp @@ -156,6 +147,17 @@ FORCE: clean: @$(RM) $(DFILES) $(OBJS) $(TARGETS) $(TESTS) VortexLib.js vortex.wasm +# calculate the version number of the build +compute_version: + $(eval LATEST_TAG ?= $(shell git fetch --depth=1 origin +refs/tags/*:refs/tags/* &> /dev/null && git tag --list "*$(BRANCH_SUFFIX)" | sort -V | tail -n1)) + $(eval VORTEX_VERSION_MAJOR ?= $(shell echo $(LATEST_TAG) | cut -d. -f1)) + $(eval VORTEX_VERSION_MINOR ?= $(shell echo $(LATEST_TAG) | sed 's/$(BRANCH_SUFFIX)$$//' | cut -d. -f2)) + $(eval VORTEX_BUILD_NUMBER ?= $(shell git rev-list --count $(LATEST_TAG)..HEAD)) + $(eval VORTEX_VERSION_MAJOR := $(if $(VORTEX_VERSION_MAJOR),$(VORTEX_VERSION_MAJOR),0)) + $(eval VORTEX_VERSION_MINOR := $(if $(VORTEX_VERSION_MINOR),$(VORTEX_VERSION_MINOR),1)) + $(eval VORTEX_BUILD_NUMBER := $(if $(VORTEX_BUILD_NUMBER),$(VORTEX_BUILD_NUMBER),0)) + $(eval VORTEX_VERSION_NUMBER := $(VORTEX_VERSION_MAJOR).$(VORTEX_VERSION_MINOR).$(VORTEX_BUILD_NUMBER)) + # Now include our target dependency files # the hyphen means ignore non-existent files -include $(DFILES) diff --git a/VortexEngine/VortexLib/VortexLib.cpp b/VortexEngine/VortexLib/VortexLib.cpp index dfcfa06f47..c492b7f335 100644 --- a/VortexEngine/VortexLib/VortexLib.cpp +++ b/VortexEngine/VortexLib/VortexLib.cpp @@ -51,30 +51,29 @@ class VortexWASMCallbacks : public VortexCallbacks { } }; -static void init_wasm() +static void init_vortex() { - Vortex::init(); + Vortex::initEx(); } -static void cleanup_wasm() +static void cleanup_vortex() { Vortex::cleanup(); } // This wraps Vortex::tick but returns an array of led colors for the tick -val tick_wasm() { +val tick_vortex() { + // first run a tick Vortex::tick(); - + // then extract the color that was produced by the tick val ledArray = val::array(); - for (int i = 0; i < led_count; ++i) { + for (uint32_t i = 0; i < led_count; ++i) { val color = val::object(); color.set("red", leds[i].red); color.set("green", leds[i].green); color.set("blue", leds[i].blue); - ledArray.set(i, color); } - return ledArray; } @@ -106,14 +105,33 @@ emscripten::val getRawDataArray(const ByteStream &byteStream) return rawDataArray; } +bool createByteStreamFromData(val bytesArray, ByteStream &stream) +{ + // Convert val to std::vector + std::vector data = vecFromJSArray(bytesArray); + + // Assuming size is part of the data or passed separately + size_t size = data.size(); + if (!stream.rawInit(data.data(), size)) { + return true; + } + return false; +} + +// js is dumb and has issues doing this conversion I guess +PatternID intToPatternID(int val) +{ + return (PatternID)val; +} + EMSCRIPTEN_BINDINGS(Vortex) { // vector register_vector("VectorString"); // basic control functions - function("Init", &init_wasm); - function("Cleanup", &cleanup_wasm); - function("Tick", &tick_wasm); + function("Init", &init_vortex); + function("Cleanup", &cleanup_vortex); + function("Tick", &tick_vortex); // Bind the HSVColor class class_("HSVColor") @@ -154,6 +172,7 @@ EMSCRIPTEN_BINDINGS(Vortex) { .constructor() // member functions + .function("rawInit", &ByteStream::rawInit, allow_raw_pointer()) .function("init", &ByteStream::init, allow_raw_pointer()) .function("clear", &ByteStream::clear) .function("shrink", &ByteStream::shrink) @@ -196,35 +215,21 @@ EMSCRIPTEN_BINDINGS(Vortex) { .value("LED_3", LedPos::LED_3) .value("LED_4", LedPos::LED_4) .value("LED_5", LedPos::LED_5) - .value("LED_6", LedPos::LED_6) - .value("LED_7", LedPos::LED_7) - .value("LED_8", LedPos::LED_8) - .value("LED_9", LedPos::LED_9) -#if FIXED_LED_COUNT == 1 .value("LED_COUNT", LedPos::LED_COUNT) .value("LED_LAST", LedPos::LED_LAST) .value("LED_ALL", LedPos::LED_ALL) .value("LED_MULTI", LedPos::LED_MULTI) .value("LED_ALL_SINGLE", LedPos::LED_ALL_SINGLE) .value("LED_ANY", LedPos::LED_ANY); - // If you decide to uncomment and use LED_EVENS and LED_ODDS in the future - // .value("LED_EVENS", LedPos::LED_EVENS) - // .value("LED_ODDS", LedPos::LED_ODDS) -#else - ; // terminate the previous one - - // Binding dynamic values from Leds class - class_("Leds") - .class_function("ledCount", &Leds::ledCount) - .class_function("ledLast", &Leds::ledLast) - .class_function("ledMulti", &Leds::ledMulti) - .class_function("ledAllSingle", &Leds::ledAllSingle) - .class_function("ledAny", &Leds::ledAny); -#endif + + function("intToPatternID", &intToPatternID); + function("isMultiLedPatternID", &isMultiLedPatternID); + function("isSingleLedPatternID", &isSingleLedPatternID); enum_("PatternID") // Meta Constants .value("PATTERN_NONE", PatternID::PATTERN_NONE) + // single led patterns .value("PATTERN_STROBE", PatternID::PATTERN_STROBE) .value("PATTERN_HYPERSTROBE", PatternID::PATTERN_HYPERSTROBE) @@ -280,7 +285,17 @@ EMSCRIPTEN_BINDINGS(Vortex) { .value("PATTERN_BOUNCE", PatternID::PATTERN_BOUNCE) .value("PATTERN_SPLITSTROBIE", PatternID::PATTERN_SPLITSTROBIE) .value("PATTERN_BACKSTROBE", PatternID::PATTERN_BACKSTROBE) - .value("PATTERN_VORTEX", PatternID::PATTERN_VORTEX); + .value("PATTERN_VORTEX", PatternID::PATTERN_VORTEX) + .value("PATTERN_COUNT", PatternID::PATTERN_COUNT); + // meta constants + //.value("PATTERN_FIRST", PatternID::PATTERN_FIRST) + //.value("PATTERN_SINGLE_FIRST", PatternID::PATTERN_SINGLE_FIRST) + //.value("PATTERN_MULTI_FIRST", PatternID::PATTERN_MULTI_FIRST) + //.value("PATTERN_SINGLE_LAST", PatternID::PATTERN_SINGLE_LAST) + //.value("PATTERN_SINGLE_COUNT", PatternID::PATTERN_SINGLE_COUNT) + //.value("PATTERN_MULTI_LAST", PatternID::PATTERN_MULTI_LAST) + //.value("PATTERN_MULTI_COUNT", PatternID::PATTERN_MULTI_COUNT) + //.value("PATTERN_LAST", PatternID::PATTERN_LAST) enum_("MenuEntryID") .value("MENU_NONE", MenuEntryID::MENU_NONE) @@ -351,6 +366,14 @@ EMSCRIPTEN_BINDINGS(Vortex) { .function("init", select_overload(&PatternArgs::init)) .function("init", select_overload(&PatternArgs::init)) .function("init", select_overload(&PatternArgs::init)) + .function("addArgs", select_overload(&PatternArgs::addArgs)) + .function("addArgs", select_overload(&PatternArgs::addArgs)) + .function("addArgs", select_overload(&PatternArgs::addArgs)) + .function("addArgs", select_overload(&PatternArgs::addArgs)) + .function("addArgs", select_overload(&PatternArgs::addArgs)) + .function("addArgs", select_overload(&PatternArgs::addArgs)) + .function("addArgs", select_overload(&PatternArgs::addArgs)) + .function("addArgs", select_overload(&PatternArgs::addArgs)) .property("arg1", &PatternArgs::arg1) .property("arg2", &PatternArgs::arg2) .property("arg3", &PatternArgs::arg3) @@ -385,17 +408,17 @@ EMSCRIPTEN_BINDINGS(Vortex) { .class_function("dupe", &PatternBuilder::dupe, allow_raw_pointers()) .class_function("makeSingle", &PatternBuilder::makeSingle, allow_raw_pointers()) .class_function("makeMulti", &PatternBuilder::makeMulti, allow_raw_pointers()) - //.class_function("unserialize", &PatternBuilder::unserialize) + //.function("unserialize", &PatternBuilder::unserialize) .class_function("getDefaultArgs", &PatternBuilder::getDefaultArgs) .class_function("numDefaultArgs", &PatternBuilder::numDefaultArgs); class_("Mode") - .constructor<>() + //.constructor<>() // overloading only works with param count not typing //.constructor() //.constructor() //.constructor() - .constructor() + //.constructor() .function("copyFrom", select_overload(&Mode::operator=)) .function("equals", &Mode::operator==) .function("notEquals", &Mode::operator!=) @@ -406,6 +429,7 @@ EMSCRIPTEN_BINDINGS(Vortex) { .function("serialize", &Mode::serialize) .function("unserialize", &Mode::unserialize) .function("equalsMode", &Mode::equals, allow_raw_pointer()) + //.function("setLedCount", &Mode::setLedCount) .function("getLedCount", &Mode::getLedCount) .function("getColorset", select_overload(&Mode::getColorset)) .function("getPatternID", &Mode::getPatternID) @@ -417,7 +441,35 @@ EMSCRIPTEN_BINDINGS(Vortex) { .function("clearPattern", &Mode::clearPattern) .function("clearColorset", &Mode::clearColorset) .function("setArg", &Mode::setArg) - .function("getArg", &Mode::getArg); + .function("getArg", &Mode::getArg) + .function("hasMultiLed", &Mode::hasMultiLed) + .function("hasSingleLed", &Mode::hasSingleLed) + .function("hasSameSingleLed", &Mode::hasSameSingleLed) + .function("hasSparseSingleLed", &Mode::hasSparseSingleLed) + .function("isEmpty", &Mode::isEmpty) + //.function("getSingleLedMap", &Mode::getSingleLedMap) + .function("isMultiLed", &Mode::isMultiLed); + + class_("Menus") + .class_function("init", &Menus::init) + .class_function("cleanup", &Menus::cleanup) + .class_function("run", &Menus::run) + .class_function("openMenuSelection", &Menus::openMenuSelection) + .class_function("openMenu", &Menus::openMenu) + .class_function("showSelection", &Menus::showSelection) + .class_function("checkOpen", &Menus::checkOpen) + .class_function("checkInMenu", &Menus::checkInMenu) + .class_function("curMenu", &Menus::curMenu, allow_raw_pointers()) + .class_function("curMenuID", &Menus::curMenuID); + + class_("Menu") + .function("init", &Menu::init) + .function("run", &Menu::run) + .function("onLedSelected", &Menu::onLedSelected) + .function("onShortClick", &Menu::onShortClick) + .function("onLongClick", &Menu::onLongClick) + .function("leaveMenu", &Menu::leaveMenu); + //.function("setTargetLeds", &Menu::setTargetLeds); class_("Modes") .class_function("init", &Modes::init) @@ -444,6 +496,8 @@ EMSCRIPTEN_BINDINGS(Vortex) { .class_function("clearModes", &Modes::clearModes) .class_function("setStartupMode", &Modes::setStartupMode) .class_function("startupMode", &Modes::startupMode) + //.class_function("initCurMode", &Modes::initCurMode, allow_raw_pointers()) + //.class_function("saveCurMode", &Modes::saveCurMode) .class_function("setFlag", &Modes::setFlag) .class_function("getFlag", &Modes::getFlag) .class_function("resetFlags", &Modes::resetFlags) @@ -457,6 +511,7 @@ EMSCRIPTEN_BINDINGS(Vortex) { .class_function("keychainModeEnabled", &Modes::keychainModeEnabled); class_("Vortex") + .class_function("init", select_overload(&Vortex::init)) .class_function("setInstantTimestep", &Vortex::setInstantTimestep) .class_function("shortClick", &Vortex::shortClick) .class_function("longClick", &Vortex::longClick) @@ -475,7 +530,7 @@ EMSCRIPTEN_BINDINGS(Vortex) { .class_function("quitClick", &Vortex::quitClick) .class_function("IRDeliver", &Vortex::IRDeliver) .class_function("VLDeliver", &Vortex::VLDeliver) - //.class_function("getStorageStats", &Vortex::getStorageStats) + //.function("getStorageStats", &Vortex::getStorageStats) .class_function("loadStorage", &Vortex::loadStorage) .class_function("openRandomizer", &Vortex::openRandomizer) .class_function("openColorSelect", &Vortex::openColorSelect) @@ -484,23 +539,29 @@ EMSCRIPTEN_BINDINGS(Vortex) { .class_function("openFactoryReset", &Vortex::openFactoryReset) .class_function("openModeSharing", &Vortex::openModeSharing) .class_function("openEditorConnection", &Vortex::openEditorConnection) + //.class_function("clearMenuTargetLeds", &Vortex::clearMenuTargetLeds) + //.class_function("setMenuTargetLeds", &Vortex::setMenuTargetLeds) + //.class_function("addMenuTargetLeds", &Vortex::addMenuTargetLeds) .class_function("getModes", &Vortex::getModes) .class_function("setModes", &Vortex::setModes) .class_function("getCurMode", &Vortex::getCurMode) + .class_function("matchLedCount", &Vortex::matchLedCount) + .class_function("checkLedCount", &Vortex::checkLedCount) + .class_function("setLedCount", &Vortex::setLedCount) .class_function("curModeIndex", &Vortex::curModeIndex) .class_function("numModes", &Vortex::numModes) .class_function("numLedsInMode", &Vortex::numLedsInMode) - //.class_function("addNewMode", select_overload(&Vortex::addNewMode)) + .class_function("addNewMode", select_overload(&Vortex::addNewMode)) .class_function("addNewMode", select_overload(&Vortex::addNewMode)) .class_function("setCurMode", &Vortex::setCurMode) .class_function("nextMode", &Vortex::nextMode) .class_function("delCurMode", &Vortex::delCurMode) .class_function("shiftCurMode", &Vortex::shiftCurMode) - //.class_function("setPattern", &Vortex::setPattern) + //.function("setPattern", &Vortex::setPattern) .class_function("getPatternID", &Vortex::getPatternID) .class_function("getPatternName", &Vortex::getPatternName) .class_function("getModeName", &Vortex::getModeName) - //.class_function("setPatternAt", &Vortex::setPatternAt) + //.function("setPatternAt", &Vortex::setPatternAt) .class_function("getColorset", &Vortex::getColorset) .class_function("setColorset", &Vortex::setColorset) .class_function("getPatternArgs", &Vortex::getPatternArgs) @@ -517,7 +578,7 @@ EMSCRIPTEN_BINDINGS(Vortex) { .class_function("setTickrate", &Vortex::setTickrate) .class_function("getTickrate", &Vortex::getTickrate) .class_function("enableUndo", &Vortex::enableUndo) - //.class_function("vcallbacks", &Vortex::vcallbacks) + //.function("vcallbacks", &Vortex::vcallbacks) .class_function("doCommand", &Vortex::doCommand) .class_function("setSleepEnabled", &Vortex::setSleepEnabled) .class_function("sleepEnabled", &Vortex::sleepEnabled) @@ -532,12 +593,18 @@ EMSCRIPTEN_BINDINGS(Vortex) { .class_function("storageEnabled", &Vortex::storageEnabled) .class_function("setStorageFilename", &Vortex::setStorageFilename) .class_function("getStorageFilename", &Vortex::getStorageFilename) + .class_function("printModeJson", &Vortex::printModeJson) + .class_function("parseModeJson", &Vortex::parseModeJson) + .class_function("printPatternJson", &Vortex::printPatternJson) + .class_function("parsePatternJson", &Vortex::parsePatternJson) + .class_function("printJson", &Vortex::printJson) + .class_function("parseJson", &Vortex::parseJson) .class_function("setLockEnabled", &Vortex::setLockEnabled) .class_function("lockEnabled", &Vortex::lockEnabled); function("getDataArray", &getDataArray); function("getRawDataArray", &getRawDataArray); - + function("createByteStreamFromData", &createByteStreamFromData); } #endif @@ -580,6 +647,7 @@ bool Vortex::m_lockstepEnabled = false; bool Vortex::m_storageEnabled = false; bool Vortex::m_sleepEnabled = true; bool Vortex::m_lockEnabled = true; +Random Vortex::m_randCtx; #ifdef _WIN32 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) @@ -615,6 +683,16 @@ Vortex::Vortex() if (!m_storedCallbacks) { // error! out of memory } +#ifdef _WIN32 + m_randCtx.seed((uint32_t)(time(NULL) ^ GetTickCount64())); +#else + struct timespec ts; + unsigned long theTick = 0U; + clock_gettime(CLOCK_MONOTONIC, &ts); + theTick = ts.tv_nsec / 1000000; + theTick += ts.tv_sec * 1000; + m_randCtx.seed(time(NULL) ^ theTick); +#endif } Vortex::~Vortex() @@ -662,6 +740,8 @@ bool Vortex::init(VortexCallbacks *callbacks) // load the modes // TODO: don't load modes here? separate api? Modes::load(); + // clear the modes + //Modes::clearModes(); // save and set undo buffer doSave(); @@ -1043,43 +1123,71 @@ void Vortex::loadStorage() Modes::loadStorage(); } -void Vortex::openRandomizer() +void Vortex::openRandomizer(bool advanced) { - Menus::openMenu(MENU_RANDOMIZER); + Menus::openMenu(MENU_RANDOMIZER, advanced); } -void Vortex::openColorSelect() +void Vortex::openColorSelect(bool advanced) { - Menus::openMenu(MENU_COLOR_SELECT); + Menus::openMenu(MENU_COLOR_SELECT, advanced); } -void Vortex::openPatternSelect() +void Vortex::openPatternSelect(bool advanced) { - Menus::openMenu(MENU_PATTERN_SELECT); + Menus::openMenu(MENU_PATTERN_SELECT, advanced); } -void Vortex::openGlobalBrightness() +void Vortex::openGlobalBrightness(bool advanced) { - Menus::openMenu(MENU_GLOBAL_BRIGHTNESS); + Menus::openMenu(MENU_GLOBAL_BRIGHTNESS, advanced); } -void Vortex::openFactoryReset() +void Vortex::openFactoryReset(bool advanced) { - Menus::openMenu(MENU_FACTORY_RESET); + Menus::openMenu(MENU_FACTORY_RESET, advanced); } -void Vortex::openModeSharing() +void Vortex::openModeSharing(bool advanced) { - Menus::openMenu(MENU_MODE_SHARING); + Menus::openMenu(MENU_MODE_SHARING, advanced); } -void Vortex::openEditorConnection() +void Vortex::openEditorConnection(bool advanced) { #if ENABLE_EDITOR_CONNECTION == 1 - Menus::openMenu(MENU_EDITOR_CONNECTION); + Menus::openMenu(MENU_EDITOR_CONNECTION, advanced); #endif } +//void Vortex::clearMenuTargetLeds() +//{ +// Menu *cur = Menus::curMenu(); +// if (!cur) { +// return; +// } +// cur->setTargetLeds(MAP_LED_NONE); +//} +// +//void Vortex::setMenuTargetLeds(LedMap targetLeds) +//{ +// Menu *cur = Menus::curMenu(); +// if (!cur) { +// return; +// } +// cur->setTargetLeds(targetLeds); +//} +// +//void Vortex::addMenuTargetLeds(LedPos pos) +//{ +// Menu *cur = Menus::curMenu(); +// if (!cur) { +// return; +// } +// LedMap curMap = cur->getTargetLeds(); +// cur->setTargetLeds((LedMap)(curMap | MAP_LED(pos))); +//} + bool Vortex::getModes(ByteStream &outStream) { // now serialize all the modes @@ -1092,12 +1200,63 @@ bool Vortex::setModes(ByteStream &stream, bool save) Modes::clearModes(); // now unserialize the stream of data that was read if (!Modes::loadFromBuffer(stream)) { - //printf("Unserialize failed\n"); return false; } return !save || doSave(); } +bool Vortex::matchLedCount(ByteStream &stream, bool vtxMode) +{ + if (!stream.decompress()) { + return false; + } + // reset the unserializer index before unserializing anything + stream.resetUnserializer(); + uint8_t major = 0; + uint8_t minor = 0; + // unserialize the vortex version + stream.unserialize8(&major); + stream.unserialize8(&minor); + // unserialize the global brightness + if (!vtxMode) { + uint8_t flags; + stream.unserialize8(&flags); + uint8_t brightness = 0; + stream.unserialize8(&brightness); + uint8_t numModes = 0; + stream.unserialize8(&numModes); + } + uint8_t ledCount = 0; + stream.unserialize8(&ledCount); + // put the unserializer back where it was for the next thing + stream.resetUnserializer(); + return setLedCount(ledCount); +} + +bool Vortex::checkLedCount() +{ + Mode *mode = Modes::curMode(); + if (!mode) { + return false; + } + uint8_t numLeds = mode->getLedCount(); + if (numLeds != LED_COUNT) { + //Leds::setLedCount(numLeds); + return false; + } + return true; +} + +uint8_t Vortex::setLedCount(uint8_t count) +{ + return true; +} + +uint8_t Vortex::getLedCount() +{ + return LED_COUNT; +} + bool Vortex::getCurMode(ByteStream &outStream) { Mode *pMode = Modes::curMode(); @@ -1131,19 +1290,23 @@ uint32_t Vortex::numLedsInMode() return pMode->getLedCount(); } -bool Vortex::addNewMode(Random *pRandCtx, bool save) +bool Vortex::addMode(const Mode *mode, bool save) { - Colorset set; - Random ctx; - if (!pRandCtx) { - pRandCtx = &ctx; + if (!Modes::addMode(mode)) { + return false; } - set.randomize(*pRandCtx); + return !save || doSave(); +} + +bool Vortex::addNewMode(bool save) +{ + Colorset set; + set.randomize(m_randCtx); // create a random pattern ID from all patterns PatternID randomPattern; do { // continuously re-randomize the pattern so we don't get solids - randomPattern = (PatternID)pRandCtx->next16(PATTERN_FIRST, PATTERN_COUNT); + randomPattern = (PatternID)m_randCtx.next16(PATTERN_FIRST, PATTERN_SINGLE_LAST); } while (randomPattern == PATTERN_SOLID); if (!Modes::addMode(randomPattern, nullptr, &set)) { return false; @@ -1825,11 +1988,11 @@ Mode *Vortex::modeFromJson(const json &modeJson) return nullptr; } -#if FIXED_LED_COUNT == 0 if (modeJson.contains("num_leds") && modeJson["num_leds"].is_number_unsigned()) { - mode->setLedCount(modeJson["num_leds"].get()); + if (modeJson["num_leds"].get() != LED_COUNT) { + // error? + } } -#endif // Extract and set multiPattern if (modeJson.contains("multi_pat") && modeJson["multi_pat"].is_object()) { @@ -1840,6 +2003,7 @@ Mode *Vortex::modeFromJson(const json &modeJson) multiPattern->getArgs(args); Colorset set = multiPattern->getColorset(); mode->setPattern(multiPattern->getPatternID(), LED_MULTI, &args, &set); + delete multiPattern; } } @@ -1854,11 +2018,25 @@ Mode *Vortex::modeFromJson(const json &modeJson) pattern->getArgs(args); Colorset set = pattern->getColorset(); mode->setPattern(pattern->getPatternID(), pos++, &args, &set); + delete pattern; } } } } + // fallback to load 1-led pattern data as the mode if multi_pat and single_pats are missing + if (!modeJson.contains("multi_pat") && !modeJson.contains("single_pats")) { + Pattern *pattern = patternFromJson(modeJson); + if (!pattern) { + return mode; + } + PatternArgs args; + pattern->getArgs(args); + Colorset set = pattern->getColorset(); + mode->setPattern(pattern->getPatternID(), LED_FIRST, &args, &set); + delete pattern; + } + return mode; } @@ -1909,7 +2087,7 @@ Pattern *Vortex::patternFromJson(const json &patternJson) } // Validate the pattern ID - if (id <= PATTERN_FIRST || id >= PATTERN_COUNT) { + if (id >= PATTERN_COUNT) { return nullptr; } @@ -1951,7 +2129,47 @@ Pattern *Vortex::patternFromJson(const json &patternJson) return pattern; } -json Vortex::saveJson() +json Vortex::saveModeToJson() +{ + json saveJson; + Mode *cur = Modes::curMode(); + if (!cur) { + return nullptr; + } + return modeToJson(cur); +} + +bool Vortex::loadModeFromJson(const json &js) +{ + if (js.is_null()) { + return false; + } + + // TODO: reintroduce version to vtxmode format + //uint8_t major = 0; + //uint8_t minor = 0; + //if (js.contains("version_major") && js["version_major"].is_number_unsigned()) { + // major = js["version_major"].get(); + //} + + //if (js.contains("version_minor") && js["version_minor"].is_number_unsigned()) { + // minor = js["version_minor"].get(); + //} + + //if (!VortexEngine::checkVersion(major, minor)) { + // return false; + //} + + Mode *mode = modeFromJson(js); + if (!mode) { + return false; + } + bool rv = Modes::addMode(mode); + delete mode; + return rv; +} + +json Vortex::saveToJson() { json saveJson; @@ -1966,13 +2184,7 @@ json Vortex::saveJson() json modesArray = json::array(); Modes::setCurMode(0); for (uint8_t i = 0; i < numModes; ++i) { - Mode *cur = Modes::curMode(); - if (cur) { - json modeJson = modeToJson(cur); - modesArray.push_back(modeJson); - } else { - modesArray.push_back(nullptr); - } + modesArray.push_back(saveModeToJson()); Modes::nextMode(); } saveJson["modes"] = modesArray; @@ -1980,7 +2192,7 @@ json Vortex::saveJson() return saveJson; } -bool Vortex::loadJson(const json& js) +bool Vortex::loadFromJson(const json& js) { if (js.is_null()) { return false; @@ -2015,58 +2227,139 @@ bool Vortex::loadJson(const json& js) num_modes = js["num_modes"].get(); } - if (js.contains("modes") && js["modes"].is_array()) { - Modes::clearModes(); - for (const auto &modeValue : js["modes"]) { - if (!modeValue.is_null() && modeValue.is_object()) { - Mode *mode = modeFromJson(modeValue); - if (mode) { - Modes::addMode(mode); - } - } + if (!js.contains("modes") || !js["modes"].is_array()) { + return false; + } + + Modes::clearModes(); + for (const auto &modeValue : js["modes"]) { + if (modeValue.is_null() || !modeValue.is_object()) { + continue; + } + if (!loadModeFromJson(modeValue)) { + // error? + return false; } } return Modes::numModes() == num_modes; } -// dump the json to output -void Vortex::dumpJson(const char *filename, bool pretty) +// print/parse the current mode json +std::string Vortex::printModeJson(bool pretty) { - json json = saveJson(); - std::string jsonStr = pretty ? json.dump(4) : json.dump(); + std::string jsonStr = "{}"; + Mode *cur = Modes::curMode(); + if (!cur) { + return jsonStr; + } + json modeJs = modeToJson(cur); + return pretty ? modeJs.dump(4) : modeJs.dump(); +} - if (filename) { - std::ofstream file(filename); - if (file.is_open()) { - file << jsonStr; - } - } else { - std::cout << jsonStr << std::endl; +bool Vortex::parseModeJson(const std::string &jsonStr) +{ + json jsonObj; + try { + jsonObj = json::parse(jsonStr); + } catch (json::parse_error &e) { + std::cerr << "JSON Parse Error: " << e.what() << std::endl; + return false; + } + Mode *tempMode = modeFromJson(jsonObj); + if (!tempMode) { + return false; + } + // update the current mode with the parsed json mode + bool result = Modes::updateCurMode(tempMode); + delete tempMode; + return result; +} + +// print/parse a pattern of the current mode json +std::string Vortex::printPatternJson(LedPos pos, bool pretty) +{ + std::string jsonStr = "{}"; + Mode *cur = Modes::curMode(); + if (!cur) { + return jsonStr; + } + Pattern *pat = cur->getPattern(pos); + if (!pat) { + return jsonStr; + } + json patternJs = patternToJson(pat); + return pretty ? patternJs.dump(4) : patternJs.dump(); +} + +bool Vortex::parsePatternJson(LedPos pos, const std::string &jsonStr) +{ + Mode *cur = Modes::curMode(); + if (!cur) { + return false; + } + json jsonObj; + try { + jsonObj = json::parse(jsonStr); + } catch (json::parse_error &e) { + std::cerr << "JSON Parse Error: " << e.what() << std::endl; + return false; + } + Pattern *tempPat = patternFromJson(jsonObj); + if (!tempPat) { + return false; } + // update the current mode with the parsed json mode + PatternID patID = tempPat->getPatternID(); + PatternArgs args; + tempPat->getArgs(args); + Colorset set = tempPat->getColorset(); + delete tempPat; + return cur->setPattern(patID, pos, &args, &set); +} + +// dump the json to output +std::string Vortex::printJson(bool pretty) +{ + json json = saveToJson(); + return pretty ? json.dump(4) : json.dump(); } bool Vortex::parseJson(const std::string &jsonStr) { try { json jsonObj = json::parse(jsonStr); - return loadJson(jsonObj); + return loadFromJson(jsonObj); } catch (json::parse_error &e) { std::cerr << "JSON Parse Error: " << e.what() << std::endl; return false; } } +bool Vortex::printJsonToFile(const std::string &filename, bool pretty) +{ + std::ofstream file(filename); + if (!file.is_open()) { + return false; + } + std::string jsonStr = printJson(pretty); + if (!jsonStr.length()) { + return false; + } + file << jsonStr; + file.close(); + return true; +} + bool Vortex::parseJsonFromFile(const std::string &filename) { std::ifstream file(filename); if (!file.is_open()) { return false; } - try { json jsonObj = json::parse(file); - return loadJson(jsonObj); + return loadFromJson(jsonObj); } catch (json::parse_error &e) { std::cerr << "JSON Parse Error: " << e.what() << std::endl; return false; diff --git a/VortexEngine/VortexLib/VortexLib.h b/VortexEngine/VortexLib/VortexLib.h index 52f0b5ef58..dd58ccef30 100644 --- a/VortexEngine/VortexLib/VortexLib.h +++ b/VortexEngine/VortexLib/VortexLib.h @@ -4,7 +4,9 @@ #include #endif +#include "VortexEngine.h" #include "Patterns/Patterns.h" +#include "Random/Random.h" #include "Leds/LedTypes.h" #include "json.hpp" @@ -31,7 +33,6 @@ // the Vortex class provides all kinds of APIs for controlling and manipulating // the engine while it is running. // - class VortexCallbacks { public: @@ -67,7 +68,6 @@ class PatternArgs; class ByteStream; class Colorset; class Pattern; -class Random; class Button; class Mode; @@ -78,16 +78,20 @@ using json = nlohmann::json; // the vortex engine as much as possible class Vortex { - Vortex(); // internal initializer static bool init(VortexCallbacks *callbacks); + public: // needs to be public for wasm build idk the binding doesn't work otherwise + Vortex(); ~Vortex(); - // public initializer, you must provide a derivation of the class VortexCallbacks + // simple initialization nothing special + static void init() { initEx(); } + + // extended initialization, provide a callbacks class to receive events template - static T *init() + static T *initEx() { if (!std::is_base_of()) { return nullptr; @@ -156,24 +160,38 @@ class Vortex static void loadStorage(); // open various menus on the core (if they exist!) - static void openRandomizer(); - static void openColorSelect(); - static void openPatternSelect(); - static void openGlobalBrightness(); - static void openFactoryReset(); - static void openModeSharing(); - static void openEditorConnection(); + static void openRandomizer(bool advanced = false); + static void openColorSelect(bool advanced = false); + static void openPatternSelect(bool advanced = false); + static void openGlobalBrightness(bool advanced = false); + static void openFactoryReset(bool advanced = false); + static void openModeSharing(bool advanced = false); + static void openEditorConnection(bool advanced = false); + + // set the target leds for the open menu + //void clearMenuTargetLeds(); + //void setMenuTargetLeds(LedMap targetLeds); + //void addMenuTargetLeds(LedPos pos); // convert modes to/from a bytestream static bool getModes(ByteStream &outStream); static bool setModes(ByteStream &stream, bool save = true); static bool getCurMode(ByteStream &stream); + // match the ledcount of the savefile in the stream, vtxMode = true + // to indicate it is a .vtxmode file or not + static bool matchLedCount(ByteStream &stream, bool vtxMode); + // TODO: do we need this? + static bool checkLedCount(); + static uint8_t setLedCount(uint8_t ledCount); + static uint8_t getLedCount(); + // functions to operate on the current mode selection static uint32_t curModeIndex(); static uint32_t numModes(); static uint32_t numLedsInMode(); - static bool addNewMode(Random *pRandCtx = nullptr, bool save = true); + static bool addMode(const Mode *mode, bool save = true); + static bool addNewMode(bool save = true); static bool addNewMode(ByteStream &stream, bool save = true); static bool setCurMode(uint32_t index, bool save = true); static bool nextMode(bool save = true); @@ -183,8 +201,8 @@ class Vortex // functions to operate on the current Mode static bool setPattern(PatternID id, const PatternArgs *args = nullptr, const Colorset *set = nullptr, bool save = true); - static PatternID getPatternID(LedPos pos = LED_ANY); - static std::string getPatternName(LedPos pos = LED_ANY); + static PatternID getPatternID(LedPos pos); + static std::string getPatternName(LedPos pos); static std::string getModeName(); static bool setPatternAt(LedPos pos, PatternID id, const PatternArgs *args = nullptr, const Colorset *set = nullptr, @@ -258,29 +276,43 @@ class Vortex // convert a mode to/from a json object static json modeToJson(const Mode *mode); - static Mode *modeFromJson(const json& modeJson); - + static Mode *modeFromJson(const json &modeJson); // convert a pattern to/from a json object static json patternToJson(const Pattern *pattern); - static Pattern *patternFromJson(const json& patternJson); - + static Pattern *patternFromJson(const json &patternJson); + // save current mode to json or load a mode by json + static json saveModeToJson(); + static bool loadModeFromJson(const json &modeJson); // save/load the engine storage to/from raw json object - static json saveJson(); - static bool loadJson(const json& json); - - // dump/parse the json to/from string - static void dumpJson(const char *filename = nullptr, bool pretty = false); + static json saveToJson(); + static bool loadFromJson(const json &json); + + // print/parse the current mode json + static std::string printModeJson(bool pretty = false); + static bool parseModeJson(const std::string &json); + // print/parse a pattern of the current mode json + static std::string printPatternJson(LedPos pos, bool pretty = false); + static bool parsePatternJson(LedPos pos, const std::string &json); + // print/parse the json from a string + static std::string printJson(bool pretty = false); static bool parseJson(const std::string &json); + // print/parse the json from a string in a file + static bool printJsonToFile(const std::string &filename, bool pretty = false); static bool parseJsonFromFile(const std::string &filename); // save and add undo buffer static bool doSave(); static bool applyUndo(); -private: - // the last command to have been executed - static char m_lastCommand; +#ifdef WASM + //// pointer to the led array and led count in the engine + //RGBColor *leds() { return m_leds; } + //int ledCount() { return m_led_count; } + //// initialize + //void initWasm(int led_count, RGBColor *leds) { m_led_count = led_count; m_leds = leds; } +#endif +private: // internal function to handle numeric values in commands static void handleNumber(char c); @@ -354,6 +386,11 @@ class Vortex static FILE *m_consoleHandle; #if LOG_TO_FILE == 1 static FILE *m_logHandle; +#endif +#ifdef WASM + //// pointer to the led array and led count in the engine + //RGBColor *m_leds; + //int m_led_count; #endif // queue of button events, deque so can push to front and back static std::deque m_buttonEventQueue; @@ -376,4 +413,8 @@ class Vortex static bool m_sleepEnabled; // whether lock is enabled static bool m_lockEnabled; + // the last command to have been executed + static char m_lastCommand; + // internal random ctx for stuff + static Random m_randCtx; }; diff --git a/VortexEngine/src/Leds/LedTypes.h b/VortexEngine/src/Leds/LedTypes.h index 4671b53402..2be91ef5d4 100644 --- a/VortexEngine/src/Leds/LedTypes.h +++ b/VortexEngine/src/Leds/LedTypes.h @@ -67,29 +67,32 @@ enum Pair : uint8_t PAIR_0 = PAIR_FIRST, PAIR_1, PAIR_2, - PAIR_3, - PAIR_4, PAIR_COUNT, PAIR_LAST = (PAIR_COUNT - 1), }; +// Compile-time check on the number of pairs and leds +static_assert(LED_COUNT == (PAIR_COUNT * 2), "Incorrect number of Pairs for Leds! Adjust the Led enum or Pair enum to match"); + // map other leds for multi compatibility #define LED_6 LED_0 #define LED_7 LED_1 #define LED_8 LED_2 #define LED_9 LED_3 +#define PAIR_3 PAIR_0 +#define PAIR_4 PAIR_1 // check if an led is even or odd -#define isEven(pos) ((pos % 2) == 0) -#define isOdd(pos) ((pos % 2) != 0) +#define isEven(pos) (pos < 3) +#define isOdd(pos) (pos >= 3) // convert a pair to even or odd led position -#define pairEven(pair) (LedPos)((uint32_t)pair * 2) -#define pairOdd(pair) (LedPos)(((uint32_t)pair * 2) + 1) +#define pairEven(pair) (LedPos)((uint32_t)pair) +#define pairOdd(pair) (LedPos)(((uint32_t)pair) + 3) // convert an led position to a pair -#define ledToPair(pos) (Pair)((uint32_t)pos / 2) +#define ledToPair(pos) (Pair)((uint32_t)pos >= 3 ? 1 : 0) // LedMap is a bitmap of leds, used for expressing whether to turn certain leds on // or off with a single integer diff --git a/VortexEngine/src/Menus/MenuList/EditorConnection.cpp b/VortexEngine/src/Menus/MenuList/EditorConnection.cpp index c52418dee4..c8dade7503 100644 --- a/VortexEngine/src/Menus/MenuList/EditorConnection.cpp +++ b/VortexEngine/src/Menus/MenuList/EditorConnection.cpp @@ -87,6 +87,7 @@ Menu::MenuAction EditorConnection::run() // operate on the state of the editor connection switch (m_state) { case STATE_DISCONNECTED: + default: // not connected yet so check for connections if (!SerialComs::isConnected()) { if (!SerialComs::checkSerial()) { diff --git a/VortexEngine/src/Modes/Modes.cpp b/VortexEngine/src/Modes/Modes.cpp index 35ccea1751..a81a6d72db 100644 --- a/VortexEngine/src/Modes/Modes.cpp +++ b/VortexEngine/src/Modes/Modes.cpp @@ -704,7 +704,36 @@ 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 || saveHeader(); + if (!save) { + // if save is not requested then just return here + return true; + } + // otherwise need to update the global flags field of the save header in storage + ByteStream headerBuffer; + // read out the storage header so we can update the flag field + if (!Storage::read(0, headerBuffer) || !headerBuffer.size()) { + // if cannot read the save header then just save it normally + return saveHeader(); + } + // layout of the save header, this struct is never really used anywhere else + // except here the actual layout of the save header is dictated by + // saveHeader() and serializeSaveHeader() + struct SaveHeader { + uint8_t vMajor; + uint8_t vMinor; + uint8_t globalFlags; + uint8_t brightness; + uint8_t numModes; + }; + // data cannot be NULL since size is non zero + SaveHeader *pHeader = (SaveHeader *)headerBuffer.data(); + // update the global flags field in the header + pHeader->globalFlags = m_globalFlags; + // need to force the crc to recalc since we modified the data, just mark the + // CRC as dirty and Storage::write() will re-calculate the CRC if it's dirty + headerBuffer.setCRCDirty(); + // write the save header back to storage + return Storage::write(0, headerBuffer); } #ifdef VORTEX_LIB diff --git a/VortexEngine/src/Serial/ByteStream.cpp b/VortexEngine/src/Serial/ByteStream.cpp index f77ebae5e9..56ae424044 100644 --- a/VortexEngine/src/Serial/ByteStream.cpp +++ b/VortexEngine/src/Serial/ByteStream.cpp @@ -338,6 +338,15 @@ bool ByteStream::isCRCDirty() const return (m_pData && (m_pData->flags & BUFFER_FLAG_DIRTY) != 0); } +void ByteStream::setCRCDirty() +{ + if (!m_pData) { + return; + } + // set the dirty flag + m_pData->flags |= BUFFER_FLAG_DIRTY; +} + bool ByteStream::serialize8(uint8_t byte) { //DEBUG_LOGF("Serialize8(): %u", byte); @@ -350,7 +359,7 @@ bool ByteStream::serialize8(uint8_t byte) // walk forward m_pData->size += sizeof(uint8_t); // dirty the crc - m_pData->flags |= BUFFER_FLAG_DIRTY; + setCRCDirty(); return true; } @@ -366,7 +375,7 @@ bool ByteStream::serialize16(uint16_t bytes) // walk forward m_pData->size += sizeof(uint16_t); // dirty the crc - m_pData->flags |= BUFFER_FLAG_DIRTY; + setCRCDirty(); return true; } @@ -382,7 +391,7 @@ bool ByteStream::serialize32(uint32_t bytes) // walk forward m_pData->size += sizeof(uint32_t); // dirty the crc - m_pData->flags |= BUFFER_FLAG_DIRTY; + setCRCDirty(); return true; } diff --git a/VortexEngine/src/Serial/ByteStream.h b/VortexEngine/src/Serial/ByteStream.h index 558cfc6e9f..937901f570 100644 --- a/VortexEngine/src/Serial/ByteStream.h +++ b/VortexEngine/src/Serial/ByteStream.h @@ -76,6 +76,8 @@ class ByteStream // check whether the data in the buffer has changed since the // crc has been re-calculated, if it is dirty, call recalcCRC bool isCRCDirty() const; + // mark the CRC as dirty (manually modified the buffer or something) + void setCRCDirty(); // serialize a byte into the buffer bool serialize8(uint8_t byte);