| #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/bindable_property_importer.hpp" | |
| #include "rive/importers/data_converter_group_importer.hpp" | |
| #include "rive/importers/enum_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/importers/state_machine_layer_component_importer.hpp" | |
| #include "rive/importers/transition_viewmodel_condition_importer.hpp" | |
| #include "rive/importers/viewmodel_importer.hpp" | |
| #include "rive/importers/viewmodel_instance_importer.hpp" | |
| #include "rive/importers/viewmodel_instance_list_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" | |
| #include "rive/animation/transition_property_viewmodel_comparator.hpp" | |
| #include "rive/data_bind/bindable_property.hpp" | |
| #include "rive/data_bind/bindable_property_number.hpp" | |
| #include "rive/data_bind/bindable_property_string.hpp" | |
| #include "rive/data_bind/bindable_property_color.hpp" | |
| #include "rive/data_bind/bindable_property_enum.hpp" | |
| #include "rive/data_bind/bindable_property_boolean.hpp" | |
| #include "rive/data_bind/converters/data_converter_group.hpp" | |
| #include "rive/assets/file_asset.hpp" | |
| #include "rive/assets/audio_asset.hpp" | |
| #include "rive/assets/file_asset_contents.hpp" | |
| #include "rive/viewmodel/viewmodel.hpp" | |
| #include "rive/viewmodel/data_enum.hpp" | |
| #include "rive/viewmodel/viewmodel_instance.hpp" | |
| #include "rive/viewmodel/viewmodel_instance_list.hpp" | |
| #include "rive/viewmodel/viewmodel_instance_viewmodel.hpp" | |
| #include "rive/viewmodel/viewmodel_instance_number.hpp" | |
| #include "rive/viewmodel/viewmodel_instance_string.hpp" | |
| #include "rive/viewmodel/viewmodel_property_viewmodel.hpp" | |
| #include "rive/viewmodel/viewmodel_property_string.hpp" | |
| #include "rive/viewmodel/viewmodel_property_number.hpp" | |
| #include "rive/viewmodel/viewmodel_property_enum.hpp" | |
| #include "rive/viewmodel/viewmodel_property_list.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, FileAssetLoader* assetLoader) : | |
| m_factory(factory), m_assetLoader(assetLoader) | |
| { | |
| assert(factory); | |
| } | |
| File::~File() | |
| { | |
| for (auto artboard : m_artboards) | |
| { | |
| delete artboard; | |
| } | |
| // Assets delete after artboards as they reference them. | |
| for (auto asset : m_fileAssets) | |
| { | |
| delete asset; | |
| } | |
| delete m_backboard; | |
| } | |
| std::unique_ptr<File> File::import(Span<const uint8_t> bytes, | |
| Factory* factory, | |
| ImportResult* result, | |
| FileAssetLoader* assetLoader) | |
| { | |
| 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 = rivestd::make_unique<File>(factory, assetLoader); | |
| auto readResult = file->read(reader, header); | |
| if (result) | |
| { | |
| *result = readResult; | |
| } | |
| if (readResult != ImportResult::success) | |
| { | |
| file.reset(nullptr); | |
| } | |
| return file; | |
| } | |
| ImportResult File::read(BinaryReader& reader, const RuntimeHeader& header) | |
| { | |
| ImportStack importStack; | |
| // TODO: @hernan consider moving this to a special importer. It's not that | |
| // simple because Core doesn't have a typeKey, so it should be treated as | |
| // a special case. In any case, it's not that bad having it here for now. | |
| Core* lastBindableObject; | |
| while (!reader.reachedEnd()) | |
| { | |
| auto object = readRuntimeObject(reader, header); | |
| if (object == nullptr) | |
| { | |
| importStack.readNullObject(); | |
| continue; | |
| } | |
| if (!object->is<DataBind>()) | |
| { | |
| lastBindableObject = object; | |
| } | |
| else if (lastBindableObject != nullptr) | |
| { | |
| object->as<DataBind>()->target(lastBindableObject); | |
| } | |
| if (object->import(importStack) == StatusCode::Ok) | |
| { | |
| switch (object->coreType()) | |
| { | |
| case Backboard::typeKey: | |
| m_backboard = object->as<Backboard>(); | |
| break; | |
| case Artboard::typeKey: | |
| { | |
| Artboard* ab = object->as<Artboard>(); | |
| ab->m_Factory = m_factory; | |
| m_artboards.push_back(ab); | |
| } | |
| break; | |
| case ImageAsset::typeKey: | |
| case FontAsset::typeKey: | |
| case AudioAsset::typeKey: | |
| { | |
| auto fa = object->as<FileAsset>(); | |
| m_fileAssets.push_back(fa); | |
| } | |
| break; | |
| case ViewModel::typeKey: | |
| { | |
| auto vmc = object->as<ViewModel>(); | |
| m_ViewModels.push_back(vmc); | |
| break; | |
| } | |
| case DataEnum::typeKey: | |
| { | |
| auto de = object->as<DataEnum>(); | |
| m_Enums.push_back(de); | |
| break; | |
| } | |
| case ViewModelPropertyEnum::typeKey: | |
| { | |
| auto vme = object->as<ViewModelPropertyEnum>(); | |
| vme->dataEnum(m_Enums[vme->enumId()]); | |
| } | |
| break; | |
| } | |
| } | |
| else | |
| { | |
| fprintf(stderr, "Failed to import object of type %d\n", object->coreType()); | |
| delete object; | |
| continue; | |
| } | |
| std::unique_ptr<ImportStackObject> stackObject = nullptr; | |
| auto stackType = object->coreType(); | |
| switch (stackType) | |
| { | |
| case Backboard::typeKey: | |
| stackObject = rivestd::make_unique<BackboardImporter>(object->as<Backboard>()); | |
| break; | |
| case Artboard::typeKey: | |
| stackObject = rivestd::make_unique<ArtboardImporter>(object->as<Artboard>()); | |
| break; | |
| case DataEnum::typeKey: | |
| stackObject = rivestd::make_unique<EnumImporter>(object->as<DataEnum>()); | |
| break; | |
| case LinearAnimation::typeKey: | |
| stackObject = | |
| rivestd::make_unique<LinearAnimationImporter>(object->as<LinearAnimation>()); | |
| break; | |
| case KeyedObject::typeKey: | |
| stackObject = rivestd::make_unique<KeyedObjectImporter>(object->as<KeyedObject>()); | |
| break; | |
| case KeyedProperty::typeKey: | |
| { | |
| auto importer = | |
| importStack.latest<LinearAnimationImporter>(LinearAnimation::typeKey); | |
| if (importer == nullptr) | |
| { | |
| return ImportResult::malformed; | |
| } | |
| stackObject = | |
| rivestd::make_unique<KeyedPropertyImporter>(importer->animation(), | |
| object->as<KeyedProperty>()); | |
| break; | |
| } | |
| case StateMachine::typeKey: | |
| stackObject = | |
| rivestd::make_unique<StateMachineImporter>(object->as<StateMachine>()); | |
| break; | |
| case StateMachineLayer::typeKey: | |
| { | |
| auto artboardImporter = importStack.latest<ArtboardImporter>(ArtboardBase::typeKey); | |
| if (artboardImporter == nullptr) | |
| { | |
| return ImportResult::malformed; | |
| } | |
| stackObject = | |
| rivestd::make_unique<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 = rivestd::make_unique<LayerStateImporter>(object->as<LayerState>()); | |
| stackType = LayerState::typeKey; | |
| break; | |
| case StateTransition::typeKey: | |
| case BlendStateTransition::typeKey: | |
| stackObject = | |
| rivestd::make_unique<StateTransitionImporter>(object->as<StateTransition>()); | |
| stackType = StateTransition::typeKey; | |
| break; | |
| case StateMachineListener::typeKey: | |
| stackObject = rivestd::make_unique<StateMachineListenerImporter>( | |
| object->as<StateMachineListener>()); | |
| break; | |
| case ImageAsset::typeKey: | |
| case FontAsset::typeKey: | |
| case AudioAsset::typeKey: | |
| stackObject = rivestd::make_unique<FileAssetImporter>(object->as<FileAsset>(), | |
| m_assetLoader, | |
| m_factory); | |
| stackType = FileAsset::typeKey; | |
| break; | |
| case ViewModel::typeKey: | |
| stackObject = rivestd::make_unique<ViewModelImporter>(object->as<ViewModel>()); | |
| stackType = ViewModel::typeKey; | |
| break; | |
| case ViewModelInstance::typeKey: | |
| stackObject = rivestd::make_unique<ViewModelInstanceImporter>( | |
| object->as<ViewModelInstance>()); | |
| stackType = ViewModelInstance::typeKey; | |
| break; | |
| case ViewModelInstanceList::typeKey: | |
| stackObject = rivestd::make_unique<ViewModelInstanceListImporter>( | |
| object->as<ViewModelInstanceList>()); | |
| stackType = ViewModelInstanceList::typeKey; | |
| break; | |
| case TransitionViewModelCondition::typeKey: | |
| case TransitionArtboardCondition::typeKey: | |
| stackObject = rivestd::make_unique<TransitionViewModelConditionImporter>( | |
| object->as<TransitionViewModelCondition>()); | |
| stackType = TransitionViewModelCondition::typeKey; | |
| break; | |
| case BindablePropertyNumber::typeKey: | |
| case BindablePropertyString::typeKey: | |
| case BindablePropertyColor::typeKey: | |
| case BindablePropertyEnum::typeKey: | |
| case BindablePropertyBoolean::typeKey: | |
| stackObject = | |
| rivestd::make_unique<BindablePropertyImporter>(object->as<BindableProperty>()); | |
| stackType = BindablePropertyBase::typeKey; | |
| break; | |
| case DataConverterGroupBase::typeKey: | |
| stackObject = rivestd::make_unique<DataConverterGroupImporter>( | |
| object->as<DataConverterGroup>()); | |
| stackType = DataConverterGroupBase::typeKey; | |
| break; | |
| } | |
| if (importStack.makeLatest(stackType, std::move(stackObject)) != StatusCode::Ok) | |
| { | |
| // Some previous stack item didn't resolve. | |
| return ImportResult::malformed; | |
| } | |
| if (object->is<StateMachineLayerComponent>() && | |
| importStack.makeLatest(StateMachineLayerComponent::typeKey, | |
| rivestd::make_unique<StateMachineLayerComponentImporter>( | |
| object->as<StateMachineLayerComponent>())) != StatusCode::Ok) | |
| { | |
| 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; | |
| } | |
| } | |
| return nullptr; | |
| } | |
| Artboard* File::artboard() const | |
| { | |
| if (m_artboards.empty()) | |
| { | |
| return nullptr; | |
| } | |
| return m_artboards[0]; | |
| } | |
| Artboard* File::artboard(size_t index) const | |
| { | |
| if (index >= m_artboards.size()) | |
| { | |
| return nullptr; | |
| } | |
| return m_artboards[index]; | |
| } | |
| 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; | |
| } | |
| void File::completeViewModelInstance(ViewModelInstance* viewModelInstance) | |
| { | |
| auto viewModel = m_ViewModels[viewModelInstance->viewModelId()]; | |
| auto propertyValues = viewModelInstance->propertyValues(); | |
| for (auto value : propertyValues) | |
| { | |
| if (value->is<ViewModelInstanceViewModel>()) | |
| { | |
| auto property = viewModel->property(value->viewModelPropertyId()); | |
| if (property->is<ViewModelPropertyViewModel>()) | |
| { | |
| auto valueViewModel = value->as<ViewModelInstanceViewModel>(); | |
| auto propertViewModel = property->as<ViewModelPropertyViewModel>(); | |
| auto viewModelReference = m_ViewModels[propertViewModel->viewModelReferenceId()]; | |
| auto viewModelInstance = | |
| viewModelReference->instance(valueViewModel->propertyValue()); | |
| if (viewModelInstance != nullptr) | |
| { | |
| valueViewModel->referenceViewModelInstance( | |
| copyViewModelInstance(viewModelInstance)); | |
| } | |
| } | |
| } | |
| else if (value->is<ViewModelInstanceList>()) | |
| { | |
| auto viewModelList = value->as<ViewModelInstanceList>(); | |
| for (auto listItem : viewModelList->listItems()) | |
| { | |
| auto viewModel = m_ViewModels[listItem->viewModelId()]; | |
| auto viewModelInstance = viewModel->instance(listItem->viewModelInstanceId()); | |
| listItem->viewModelInstance(copyViewModelInstance(viewModelInstance)); | |
| if (listItem->artboardId() < m_artboards.size()) | |
| { | |
| listItem->artboard(m_artboards[listItem->artboardId()]); | |
| } | |
| } | |
| } | |
| value->viewModelProperty(viewModel->property(value->viewModelPropertyId())); | |
| } | |
| } | |
| ViewModelInstance* File::copyViewModelInstance(ViewModelInstance* viewModelInstance) | |
| { | |
| auto copy = viewModelInstance->clone()->as<ViewModelInstance>(); | |
| completeViewModelInstance(copy); | |
| return copy; | |
| } | |
| ViewModelInstance* File::createViewModelInstance(std::string name) | |
| { | |
| for (auto viewModel : m_ViewModels) | |
| { | |
| if (viewModel->is<ViewModel>()) | |
| { | |
| if (viewModel->name() == name) | |
| { | |
| return createViewModelInstance(viewModel); | |
| } | |
| } | |
| } | |
| return nullptr; | |
| } | |
| ViewModelInstance* File::createViewModelInstance(std::string name, std::string instanceName) | |
| { | |
| for (auto viewModel : m_ViewModels) | |
| { | |
| if (viewModel->is<ViewModel>()) | |
| { | |
| if (viewModel->name() == name) | |
| { | |
| auto instance = viewModel->instance(instanceName); | |
| if (instance != nullptr) | |
| { | |
| return copyViewModelInstance(instance); | |
| } | |
| } | |
| } | |
| } | |
| return nullptr; | |
| } | |
| ViewModelInstance* File::createViewModelInstance(ViewModel* viewModel) | |
| { | |
| if (viewModel != nullptr) | |
| { | |
| auto viewModelInstance = viewModel->defaultInstance(); | |
| return copyViewModelInstance(viewModelInstance); | |
| } | |
| return nullptr; | |
| } | |
| ViewModelInstance* File::createViewModelInstance(Artboard* artboard) | |
| { | |
| if ((size_t)artboard->viewModelId() < m_ViewModels.size()) | |
| { | |
| auto viewModel = m_ViewModels[artboard->viewModelId()]; | |
| if (viewModel != nullptr) | |
| { | |
| return createViewModelInstance(viewModel); | |
| } | |
| } | |
| return nullptr; | |
| } | |
| ViewModelInstanceListItem* File::viewModelInstanceListItem(ViewModelInstance* viewModelInstance) | |
| { | |
| // Search for an implicit artboard linked to the viewModel. | |
| // It will return the first one it finds, but there could be more. | |
| // We should decide if we want to be more restrictive and only return | |
| // an artboard if one and only one is found. | |
| for (auto artboard : m_artboards) | |
| { | |
| if (artboard->viewModelId() == viewModelInstance->viewModelId()) | |
| { | |
| return viewModelInstanceListItem(viewModelInstance, artboard); | |
| } | |
| } | |
| return nullptr; | |
| } | |
| ViewModelInstanceListItem* File::viewModelInstanceListItem(ViewModelInstance* viewModelInstance, | |
| Artboard* artboard) | |
| { | |
| auto viewModelInstanceListItem = new ViewModelInstanceListItem(); | |
| viewModelInstanceListItem->viewModelInstance(viewModelInstance); | |
| viewModelInstanceListItem->artboard(artboard); | |
| return viewModelInstanceListItem; | |
| } | |
| ViewModel* File::viewModel(std::string name) | |
| { | |
| for (auto viewModel : m_ViewModels) | |
| { | |
| if (viewModel->name() == name) | |
| { | |
| return viewModel; | |
| } | |
| } | |
| return nullptr; | |
| } | |
| const std::vector<FileAsset*>& File::assets() const { return m_fileAssets; } | |
| #ifdef WITH_RIVE_TOOLS | |
| const std::vector<uint8_t> File::stripAssets(Span<const uint8_t> bytes, | |
| std::set<uint16_t> typeKeys, | |
| ImportResult* result) | |
| { | |
| std::vector<uint8_t> strippedData; | |
| strippedData.reserve(bytes.size()); | |
| BinaryReader reader(bytes); | |
| RuntimeHeader header; | |
| if (!RuntimeHeader::read(reader, header)) | |
| { | |
| if (result) | |
| { | |
| *result = ImportResult::malformed; | |
| } | |
| } | |
| else if (header.majorVersion() != majorVersion) | |
| { | |
| if (result) | |
| { | |
| *result = ImportResult::unsupportedVersion; | |
| } | |
| } | |
| else | |
| { | |
| strippedData.insert(strippedData.end(), bytes.data(), reader.position()); | |
| const uint8_t* from = reader.position(); | |
| const uint8_t* to = reader.position(); | |
| uint16_t lastAssetType = 0; | |
| while (!reader.reachedEnd()) | |
| { | |
| auto object = readRuntimeObject(reader, header); | |
| if (object == nullptr) | |
| { | |
| continue; | |
| } | |
| if (object->is<FileAssetBase>()) | |
| { | |
| lastAssetType = object->coreType(); | |
| } | |
| if (object->is<FileAssetContents>() && typeKeys.find(lastAssetType) != typeKeys.end()) | |
| { | |
| if (from != to) | |
| { | |
| strippedData.insert(strippedData.end(), from, to); | |
| } | |
| from = reader.position(); | |
| } | |
| delete object; | |
| to = reader.position(); | |
| } | |
| if (from != to) | |
| { | |
| strippedData.insert(strippedData.end(), from, to); | |
| } | |
| *result = ImportResult::success; | |
| } | |
| return strippedData; | |
| } | |
| #endif |