Add rivinfo tool
diff --git a/.gitignore b/.gitignore index 563e7e5..72dd777 100644 --- a/.gitignore +++ b/.gitignore
@@ -31,9 +31,6 @@ *.out *.app -# test builds -dev/test/build/bin/* - # aot snapshots dev/bin/* @@ -57,6 +54,8 @@ # Build directories build/bin +dev/test/build/bin +rivinfo/build/macosx # Skia dependencies skia/dependencies/skia
diff --git a/rivinfo/build.sh b/rivinfo/build.sh new file mode 100755 index 0000000..e0d0eb5 --- /dev/null +++ b/rivinfo/build.sh
@@ -0,0 +1,26 @@ +#!/bin/bash + +# dir=$(pwd) + +# cd ../renderer +# ./build.sh $@ + +# cd $dir + +cd build + +OPTION=$1 + +if [ "$OPTION" = 'help' ]; then + echo build.sh - build debug library + echo build.sh clean - clean the build + echo build.sh release - build release library +elif [ "$OPTION" = "clean" ]; then + echo Cleaning project ... + # TODO: fix premake5 clean to bubble the clean command to dependent projects + premake5 gmake && make clean +elif [ "$OPTION" = "release" ]; then + premake5 gmake && make config=release -j7 +else + premake5 gmake && make -j7 +fi
diff --git a/rivinfo/build/premake5.lua b/rivinfo/build/premake5.lua new file mode 100644 index 0000000..0c69b56 --- /dev/null +++ b/rivinfo/build/premake5.lua
@@ -0,0 +1,73 @@ +workspace "rive" +configurations {"debug", "release"} + +project "rivinfo" + kind "ConsoleApp" + language "C++" + cppdialect "C++17" + targetdir "%{cfg.system}/bin/%{cfg.buildcfg}" + objdir "%{cfg.system}/obj/%{cfg.buildcfg}" + includedirs { + "../../include", + "../../test", + "/usr/local/include", + "/usr/include", + } + + if os.host() == 'macosx' then + links { + "Cocoa.framework", + "CoreFoundation.framework", + "IOKit.framework", + "Security.framework", + "bz2", + "iconv", + "lzma", + "rive", + "z", -- lib av format + } + else + links { + "m", + "rive", + "z", + "dl", + } + end + + libdirs { + "../../build/%{cfg.system}/bin/%{cfg.buildcfg}", + "/usr/local/lib", + "/usr/lib", + } + + files { + "../**.cpp", + "../../test/no_op_factory.cpp", + } + + buildoptions {"-Wall", "-fno-rtti", "-g"} + + filter "configurations:debug" + defines {"DEBUG"} + symbols "On" + + filter "configurations:release" + defines {"RELEASE"} + defines {"NDEBUG"} + optimize "On" + +-- Clean Function -- +newaction { + trigger = "clean", + description = "clean the build", + execute = function() + print("clean the build...") + os.rmdir("./bin") + os.rmdir("./obj") + os.remove("Makefile") + -- no wildcards in os.remove, so use shell + os.execute("rm *.make") + print("build cleaned") + end +}
diff --git a/rivinfo/main.cpp b/rivinfo/main.cpp new file mode 100644 index 0000000..17b70e5 --- /dev/null +++ b/rivinfo/main.cpp
@@ -0,0 +1,168 @@ +/* + * Copyright 2022 Rive + */ + +#include "rive/artboard.hpp" +#include "rive/file.hpp" +#include "rive/animation/linear_animation_instance.hpp" +#include "rive/animation/state_machine_instance.hpp" +#include "rive/animation/state_machine_input_instance.hpp" +#include "no_op_factory.hpp" + +class JSoner { + std::vector<bool> m_IsArray; + + void tab() { + for (int i = 0; i < m_IsArray.size(); ++i) { + printf("\t"); + } + } + void add_c(const char key[], char c) { + this->tab(); + if (key) { + printf("\"%s\": %c\n", key, c); + } else { + printf("%c\n", c); + } + } + +public: + JSoner() {} + ~JSoner() { + while (!m_IsArray.empty()) { + this->pop(); + } + } + + void add(const char key[], const char value[]) { + this->tab(); + printf("\"%s\": \"%s\"\n", key, value); + } + void pushArray(const char key[] = nullptr) { + this->add_c(key, '['); + m_IsArray.push_back(true); + } + void pushStruct(const char key[] = nullptr) { + this->add_c(key, '{'); + m_IsArray.push_back(false); + } + void pop() { + assert(!m_IsArray.empty()); + char c = m_IsArray.front() ? ']' : '}'; + m_IsArray.pop_back(); + + this->tab(); + printf("%c\n", c); + } + + void add(const char key[], int value) { + this->add(key, std::to_string(value).c_str()); + } +}; + +////////////////////////////////////////////////// + +static void dump(JSoner& js, rive::LinearAnimationInstance* anim) { + js.pushStruct(); + js.add("name", anim->name().c_str()); + js.add("duration", std::to_string(anim->durationSeconds()).c_str()); + js.add("loop", std::to_string(anim->loopValue()).c_str()); + js.pop(); +} + +static void dump(JSoner& js, rive::StateMachineInstance* smi) { + js.pushStruct(); + js.add("name", smi->name().c_str()); + if (auto count = smi->inputCount()) { + js.pushArray("inputs"); + for (auto i = 0; i < count; ++i) { + auto inp = smi->input(i); + js.add("name", inp->name().c_str()); + } + js.pop(); + } + js.pop(); +} + +static void dump(JSoner& js, rive::ArtboardInstance* abi) { + js.pushStruct(); + js.add("name", abi->name().c_str()); + if (auto count = abi->animationCount()) { + js.pushArray("animations"); + for (size_t i = 0; i < count; ++i) { + dump(js, abi->animationAt(i).get()); + } + js.pop(); + } + if (auto count = abi->stateMachineCount()) { + js.pushArray("machines"); + for (size_t i = 0; i < count; ++i) { + dump(js, abi->stateMachineAt(i).get()); + } + js.pop(); + } + js.pop(); +} + +static void dump(JSoner& js, rive::File* file) { + auto count = file->artboardCount(); + js.pushArray("artboards"); + for (size_t i = 0; i < count; ++i) { + dump(js, file->artboardAt(i).get()); + } + js.pop(); +} + +static std::unique_ptr<rive::File> open_file(const char name[]) { + FILE* f = fopen(name, "rb"); + if (!f) { + return nullptr; + } + + fseek(f, 0, SEEK_END); + auto length = ftell(f); + fseek(f, 0, SEEK_SET); + + std::vector<uint8_t> bytes(length); + + if (fread(bytes.data(), 1, length, f) != length) { + printf("Failed to read file into bytes array\n"); + return nullptr; + } + + static rive::NoOpFactory gFactory; + return rive::File::import(rive::toSpan(bytes), &gFactory); +} + +static bool is_arg(const char arg[], const char target[], const char alt[] = nullptr) { + return !strcmp(arg, target) || (arg && !strcmp(arg, alt)); +} + +int main(int argc, const char* argv[]) { + const char* filename = nullptr; + + for (int i = 1; i < argc; ++i) { + if (is_arg(argv[i], "--file", "-f")) { + filename = argv[++i]; + continue; + } + printf("Unrecognized argument %s\n", argv[i]); + return 1; + } + + if (!filename) { + printf("Need --file filename\n"); + return 1; + } + + auto file = open_file(filename); + if (!file) { + printf("Can't open %s\n", filename); + return 1; + } + + JSoner js; + js.pushStruct(); + dump(js, file.get()); + return 0; +}