blob: c39ddf34d7e769556b3f006c875e682996cf527c [file] [log] [blame]
// Copyright 2021 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use std::{any::TypeId, rc::Rc};
use crate::{
animation::{KeyedObject, KeyedProperty, LinearAnimation},
artboard::Artboard,
backboard::Backboard,
component::Component,
core::{self, BinaryReader, Core, Object},
importers::{
ArtboardImporter, ImportStack, ImportStackObject, KeyedObjectImporter,
KeyedPropertyImporter, LinearAnimationImporter,
},
runtime_header::RuntimeHeader,
status_code::StatusCode,
};
/// Major version number supported by the runtime.
const MAJOR_VERSION: u32 = 7;
/// Minor version number supported by the runtime.
const _MINOR_VERSION: u32 = 0;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ImportError {
/// Indicates that the Rive file is not supported by this runtime.
UnsupportedVersion,
/// Indicates that the there is a formatting problem in the file itself.
Malformed,
}
fn read_runtime_object(
reader: &mut BinaryReader<'_>,
header: &RuntimeHeader,
objects: &mut Vec<Rc<dyn Core>>,
) -> Option<(Object, TypeId)> {
let id = core::get_type_id(reader.read_var_u64()? as u16)?;
let (core, object) = <dyn Core>::from_type_id(id)?;
objects.push(Rc::clone(&core));
loop {
let property_key = reader.read_var_u64()? as u16;
if property_key == 0 {
break;
}
if !core.write(property_key, reader) {
// todo!("try to find core property keys");
match header.property_file_id(property_key as u32)? {
0 => {
reader.read_var_u64()?;
}
1 => {
reader.read_string()?;
}
2 => {
reader.read_f32()?;
}
3 => {
reader.read_u32()?;
}
_ => return None,
}
}
}
Some((object, id))
}
#[derive(Debug)]
pub struct File {
backboard: Object<Backboard>,
artboards: Vec<Object<Artboard>>,
// TODO(https://fxbug.dev/42165549)
#[allow(unused)]
objects: Vec<Rc<dyn Core>>,
}
impl File {
pub fn import(reader: &mut BinaryReader<'_>) -> Result<Self, ImportError> {
let header = RuntimeHeader::read(reader).ok_or(ImportError::Malformed)?;
if header.major_version() != MAJOR_VERSION {
return Err(ImportError::UnsupportedVersion);
}
Self::read(reader, &header).ok_or(ImportError::Malformed)
}
fn read(reader: &mut BinaryReader<'_>, header: &RuntimeHeader) -> Option<Self> {
let mut objects = Vec::new();
let mut import_stack = ImportStack::default();
let mut backboard_option = None;
let mut artboards = Vec::new();
while !reader.reached_end() {
let (object, id) = read_runtime_object(reader, header, &mut objects)?;
let stack_object: Option<Box<dyn ImportStackObject>> =
if let Some(artboard) = object.try_cast::<Artboard>() {
Some(Box::new(ArtboardImporter::new(artboard)))
} else if let Some(animation) = object.try_cast::<LinearAnimation>() {
Some(Box::new(LinearAnimationImporter::new(animation)))
} else if let Some(keyed_object) = object.try_cast::<KeyedObject>() {
Some(Box::new(KeyedObjectImporter::new(keyed_object)))
} else if let Some(keyed_property) = object.try_cast::<KeyedProperty>() {
let importer = import_stack
.latest::<LinearAnimationImporter>(TypeId::of::<LinearAnimation>())?;
Some(Box::new(KeyedPropertyImporter::new(importer.animation(), keyed_property)))
} else {
None
};
if import_stack.make_latest(id, stack_object) != StatusCode::Ok {
return None;
}
if object.as_ref().import(object.clone(), &import_stack) == StatusCode::Ok {
if let Some(backboard) = object.try_cast::<Backboard>() {
backboard_option = Some(backboard);
}
if let Some(artboard) = object.try_cast::<Artboard>() {
artboards.push(artboard);
}
}
}
if import_stack.resolve() != StatusCode::Ok {
return None;
}
Some(Self { backboard: backboard_option?, artboards, objects })
}
pub fn backboard(&self) -> Object<Backboard> {
self.backboard.clone()
}
pub fn artboard(&self) -> Option<Object<Artboard>> {
self.artboards.get(0).cloned()
}
pub fn get_artboard(&self, name: &str) -> Option<Object<Artboard>> {
self.artboards
.iter()
.find(|artboard| artboard.cast::<Component>().as_ref().name() == name)
.cloned()
}
pub fn artboards(&self) -> impl Iterator<Item = &Object<Artboard>> {
self.artboards.iter()
}
}