| #include "rive/file.hpp" |
| #include "rive/runtime_header.hpp" |
| #include "rive/animation/animation.hpp" |
| #include "rive/core/field_types/core_color_type.hpp" |
| #include "rive/core/field_types/core_double_type.hpp" |
| #include "rive/core/field_types/core_string_type.hpp" |
| #include "rive/core/field_types/core_uint_type.hpp" |
| #include "rive/generated/core_registry.hpp" |
| #include "rive/importers/artboard_importer.hpp" |
| #include "rive/importers/backboard_importer.hpp" |
| #include "rive/importers/file_asset_importer.hpp" |
| #include "rive/importers/import_stack.hpp" |
| #include "rive/importers/keyed_object_importer.hpp" |
| #include "rive/importers/keyed_property_importer.hpp" |
| #include "rive/importers/linear_animation_importer.hpp" |
| #include "rive/importers/state_machine_importer.hpp" |
| #include "rive/importers/state_machine_listener_importer.hpp" |
| #include "rive/importers/state_machine_layer_importer.hpp" |
| #include "rive/importers/layer_state_importer.hpp" |
| #include "rive/importers/state_transition_importer.hpp" |
| #include "rive/animation/blend_state_transition.hpp" |
| #include "rive/animation/any_state.hpp" |
| #include "rive/animation/entry_state.hpp" |
| #include "rive/animation/exit_state.hpp" |
| #include "rive/animation/animation_state.hpp" |
| #include "rive/animation/blend_state_1d.hpp" |
| #include "rive/animation/blend_state_direct.hpp" |
| |
| // Default namespace for Rive Cpp code |
| using namespace rive; |
| |
| #if !defined(RIVE_FMT_U64) |
| #if defined(__ANDROID__) |
| #if INTPTR_MAX == INT64_MAX |
| #define RIVE_FMT_U64 "%lu" |
| #define RIVE_FMT_I64 "%ld" |
| #else |
| #define RIVE_FMT_U64 "%llu" |
| #define RIVE_FMT_I64 "%lld" |
| #endif |
| #elif defined(_WIN32) |
| #define RIVE_FMT_U64 "%lld" |
| #define RIVE_FMT_I64 "%llu" |
| #else |
| #include <inttypes.h> |
| #define RIVE_FMT_U64 "%" PRIu64 |
| #define RIVE_FMT_I64 "%" PRId64 |
| #endif |
| #endif |
| |
| // Import a single Rive runtime object. |
| // Used by the file importer. |
| static Core* readRuntimeObject(BinaryReader& reader, const RuntimeHeader& header) { |
| auto coreObjectKey = reader.readVarUintAs<int>(); |
| auto object = CoreRegistry::makeCoreInstance(coreObjectKey); |
| while (true) { |
| auto propertyKey = reader.readVarUintAs<uint16_t>(); |
| if (propertyKey == 0) { |
| // Terminator. https://media.giphy.com/media/7TtvTUMm9mp20/giphy.gif |
| break; |
| } |
| |
| if (reader.hasError()) { |
| delete object; |
| return nullptr; |
| } |
| if (object == nullptr || !object->deserialize(propertyKey, reader)) { |
| // We have an unknown object or property, first see if core knows |
| // the property type. |
| int id = CoreRegistry::propertyFieldId(propertyKey); |
| if (id == -1) { |
| // No, check if it's in toc. |
| id = header.propertyFieldId(propertyKey); |
| } |
| |
| if (id == -1) { |
| // Still couldn't find it, give up. |
| fprintf( |
| stderr, "Unknown property key %d, missing from property ToC.\n", propertyKey); |
| delete object; |
| return nullptr; |
| } |
| |
| switch (id) { |
| case CoreUintType::id: |
| CoreUintType::deserialize(reader); |
| break; |
| case CoreStringType::id: |
| CoreStringType::deserialize(reader); |
| break; |
| case CoreDoubleType::id: |
| CoreDoubleType::deserialize(reader); |
| break; |
| case CoreColorType::id: |
| CoreColorType::deserialize(reader); |
| break; |
| } |
| } |
| } |
| if (object == nullptr) { |
| // fprintf(stderr, |
| // "File contains an unknown object with coreType " RIVE_FMT_U64 |
| // ", which " "this runtime doesn't understand.\n", |
| // coreObjectKey); |
| return nullptr; |
| } |
| return object; |
| } |
| |
| File::File(Factory* factory, FileAssetResolver* assetResolver) : |
| m_Factory(factory), m_AssetResolver(assetResolver) { |
| assert(factory); |
| } |
| |
| File::~File() {} |
| |
| std::unique_ptr<File> File::import(Span<const uint8_t> bytes, |
| Factory* factory, |
| ImportResult* result, |
| FileAssetResolver* assetResolver) { |
| BinaryReader reader(bytes); |
| RuntimeHeader header; |
| if (!RuntimeHeader::read(reader, header)) { |
| fprintf(stderr, "Bad header\n"); |
| if (result) { |
| *result = ImportResult::malformed; |
| } |
| return nullptr; |
| } |
| if (header.majorVersion() != majorVersion) { |
| fprintf(stderr, |
| "Unsupported version %u.%u expected %u.%u.\n", |
| header.majorVersion(), |
| header.minorVersion(), |
| majorVersion, |
| minorVersion); |
| if (result) { |
| *result = ImportResult::unsupportedVersion; |
| } |
| return nullptr; |
| } |
| auto file = std::unique_ptr<File>(new File(factory, assetResolver)); |
| auto readResult = file->read(reader, header); |
| if (readResult != ImportResult::success) { |
| file.reset(nullptr); |
| } |
| if (result) { |
| *result = ImportResult::success; |
| } |
| return file; |
| } |
| |
| ImportResult File::read(BinaryReader& reader, const RuntimeHeader& header) { |
| ImportStack importStack; |
| while (!reader.reachedEnd()) { |
| auto object = readRuntimeObject(reader, header); |
| if (object == nullptr) { |
| importStack.readNullObject(); |
| continue; |
| } |
| if (object->import(importStack) == StatusCode::Ok) { |
| switch (object->coreType()) { |
| case Backboard::typeKey: |
| m_Backboard.reset(object->as<Backboard>()); |
| break; |
| case Artboard::typeKey: { |
| Artboard* ab = object->as<Artboard>(); |
| ab->m_Factory = m_Factory; |
| m_Artboards.push_back(std::unique_ptr<Artboard>(ab)); |
| } break; |
| case ImageAsset::typeKey: { |
| auto fa = object->as<FileAsset>(); |
| m_FileAssets.push_back(std::unique_ptr<FileAsset>(fa)); |
| } break; |
| } |
| } else { |
| fprintf(stderr, "Failed to import object of type %d\n", object->coreType()); |
| delete object; |
| continue; |
| } |
| ImportStackObject* stackObject = nullptr; |
| auto stackType = object->coreType(); |
| |
| switch (stackType) { |
| case Backboard::typeKey: |
| stackObject = new BackboardImporter(object->as<Backboard>()); |
| break; |
| case Artboard::typeKey: |
| stackObject = new ArtboardImporter(object->as<Artboard>()); |
| break; |
| case LinearAnimation::typeKey: |
| stackObject = new LinearAnimationImporter(object->as<LinearAnimation>()); |
| break; |
| case KeyedObject::typeKey: |
| stackObject = new KeyedObjectImporter(object->as<KeyedObject>()); |
| break; |
| case KeyedProperty::typeKey: { |
| auto importer = |
| importStack.latest<LinearAnimationImporter>(LinearAnimation::typeKey); |
| if (importer == nullptr) { |
| return ImportResult::malformed; |
| } |
| stackObject = |
| new KeyedPropertyImporter(importer->animation(), object->as<KeyedProperty>()); |
| break; |
| } |
| case StateMachine::typeKey: |
| stackObject = new StateMachineImporter(object->as<StateMachine>()); |
| break; |
| case StateMachineLayer::typeKey: { |
| auto artboardImporter = importStack.latest<ArtboardImporter>(ArtboardBase::typeKey); |
| if (artboardImporter == nullptr) { |
| return ImportResult::malformed; |
| } |
| |
| stackObject = new StateMachineLayerImporter(object->as<StateMachineLayer>(), |
| artboardImporter->artboard()); |
| |
| break; |
| } |
| case EntryState::typeKey: |
| case ExitState::typeKey: |
| case AnyState::typeKey: |
| case AnimationState::typeKey: |
| case BlendState1D::typeKey: |
| case BlendStateDirect::typeKey: |
| stackObject = new LayerStateImporter(object->as<LayerState>()); |
| stackType = LayerState::typeKey; |
| break; |
| case StateTransition::typeKey: |
| case BlendStateTransition::typeKey: |
| stackObject = new StateTransitionImporter(object->as<StateTransition>()); |
| stackType = StateTransition::typeKey; |
| break; |
| case StateMachineListener::typeKey: |
| stackObject = new StateMachineListenerImporter(object->as<StateMachineListener>()); |
| break; |
| case ImageAsset::typeKey: |
| stackObject = |
| new FileAssetImporter(object->as<FileAsset>(), m_AssetResolver, m_Factory); |
| stackType = FileAsset::typeKey; |
| break; |
| } |
| if (importStack.makeLatest(stackType, stackObject) != StatusCode::Ok) { |
| // Some previous stack item didn't resolve. |
| return ImportResult::malformed; |
| } |
| } |
| |
| return !reader.hasError() && importStack.resolve() == StatusCode::Ok ? ImportResult::success |
| : ImportResult::malformed; |
| } |
| |
| Artboard* File::artboard(std::string name) const { |
| for (const auto& artboard : m_Artboards) { |
| if (artboard->name() == name) { |
| return artboard.get(); |
| } |
| } |
| return nullptr; |
| } |
| |
| Artboard* File::artboard() const { |
| if (m_Artboards.empty()) { |
| return nullptr; |
| } |
| return m_Artboards[0].get(); |
| } |
| |
| Artboard* File::artboard(size_t index) const { |
| if (index >= m_Artboards.size()) { |
| return nullptr; |
| } |
| return m_Artboards[index].get(); |
| } |
| |
| std::string File::artboardNameAt(size_t index) const { |
| auto ab = this->artboard(index); |
| return ab ? ab->name() : ""; |
| } |
| |
| std::unique_ptr<ArtboardInstance> File::artboardDefault() const { |
| auto ab = this->artboard(); |
| return ab ? ab->instance() : nullptr; |
| } |
| |
| std::unique_ptr<ArtboardInstance> File::artboardAt(size_t index) const { |
| auto ab = this->artboard(index); |
| return ab ? ab->instance() : nullptr; |
| } |
| |
| std::unique_ptr<ArtboardInstance> File::artboardNamed(std::string name) const { |
| auto ab = this->artboard(name); |
| return ab ? ab->instance() : nullptr; |
| } |