blob: 57fb4a7293c5314187f59762d09d5b9cb18bc37e [file] [log] [blame]
#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/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/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/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/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 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;
}
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