1pub mod byte;
24pub(crate) mod deserialize;
25pub(crate) mod serialize;
26pub(crate) mod tables;
27
28use bitflags::bitflags;
29use byte::{ByteReader, ByteReaderImpl};
30
31#[cfg(not(feature = "tagging"))]
32mod tagging_stub;
33
34#[cfg(feature = "tagging")]
35pub(crate) use crate::tagging::*;
36#[cfg(not(feature = "tagging"))]
37pub(crate) use tagging_stub::*;
38
39use crate::{String, Vec};
40use core::marker::PhantomData;
41
42bitflags! {
43 #[non_exhaustive]
44 #[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
45 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
46 pub struct PixmapTableConfigurationFlags: u8 {
48 #[doc = include_str!("../../res/snippets/pixmap_table/configurations/flag/use_constant_width.md")]
49 const ConstantWidth = 0b00000001;
50 #[doc = include_str!("../../res/snippets/pixmap_table/configurations/flag/use_constant_height.md")]
51 const ConstantHeight = 0b00000010;
52 #[doc = include_str!("../../res/snippets/pixmap_table/configurations/flag/use_constant_bits_per_pixel.md")]
53 const ConstantBitsPerPixel = 0b00000100;
54 }
55
56 #[non_exhaustive]
57 #[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
58 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
59 pub struct PixmapTableLinkFlags: u8 {
61 #[doc = include_str!("../../res/snippets/pixmap_table/links/flag/link_color_tables.md")]
62 const LinkColorTables = 0b00000001;
63 }
64
65 #[non_exhaustive]
66 #[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
67 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
68 pub struct CharacterTableModifierFlags: u8 {
70 #[doc = include_str!("../../res/snippets/character_table/modifiers/brief/use_advance_x.md")]
71 #[doc = include_str!("../../res/snippets/character_table/modifiers/details/use_advance_x.md")]
72 const UseAdvanceX = 0b00000001;
73 #[doc = include_str!("../../res/snippets/character_table/modifiers/brief/use_pixmap_index.md")]
74 #[doc = include_str!("../../res/snippets/character_table/modifiers/details/use_pixmap_index.md")]
75 const UsePixmapIndex = 0b00000010;
76 #[doc = include_str!("../../res/snippets/character_table/modifiers/brief/use_pixmap_table_index.md")]
77 #[doc = include_str!("../../res/snippets/character_table/modifiers/details/use_pixmap_table_index.md")]
78 const UsePixmapTableIndex = 0b00000100;
79 }
80
81 #[non_exhaustive]
82 #[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
83 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
84 pub struct CharacterTableLinkFlags: u8 {
86 #[doc = include_str!("../../res/snippets/character_table/links/flag/link_pixmap_tables.md")]
87 const LinkPixmapTables = 0b00000001;
88 }
89
90 #[non_exhaustive]
91 #[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
92 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
93 pub struct CharacterTableConfigurationFlags: u8 {
95 #[doc = include_str!("../../res/snippets/character_table/configurations/flag/use_constant_code_point_count.md")]
96 const ConstantCodePointCount = 0b00000001;
97 }
98
99 #[non_exhaustive]
100 #[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
101 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
102 pub struct ColorTableModifierFlags: u8 {
104 #[doc = include_str!("../../res/snippets/color_table/modifiers/brief/use_color_type.md")]
105 #[doc = include_str!("../../res/snippets/color_table/modifiers/details/use_color_type.md")]
106 const UseColorType = 0b00000001;
107 }
108
109 #[non_exhaustive]
110 #[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
111 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
112 pub struct ColorTableConfigurationFlags: u8 {
114 #[doc = include_str!("../../res/snippets/color_table/configurations/flag/use_constant_alpha.md")]
115 const ConstantAlpha = 0b00000001;
116 }
117
118 #[non_exhaustive]
119 #[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
120 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
121 pub struct FontTableLinkFlags: u8 {
123 #[doc = include_str!("../../res/snippets/font_table/links/flag/link_character_tables.md")]
124 const LinkCharacterTables = 0b00000001;
125 }
126
127 #[non_exhaustive]
128 #[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
129 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
130 #[doc = include_str!("../../res/snippets/data_types/FontType.md")]
131 pub struct FontType: u8 {
132 #[doc = include_str!("../../res/snippets/data_types/FontType/Bold.md")]
133 const Bold = 0b00000001;
134 #[doc = include_str!("../../res/snippets/data_types/FontType/Italic.md")]
135 const Italic = 0b00000010;
136 }
137}
138
139#[repr(u8)]
140#[non_exhaustive]
141#[derive(Default, Debug, Clone, Copy)]
142#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
143#[doc = include_str!("../../res/snippets/data_types/Version.md")]
144pub enum Version {
145 #[default]
146 #[doc = include_str!("../../res/snippets/data_types/Version/FV0.md")]
147 FV0 = 0b00000000,
148}
149
150impl core::fmt::Display for Version {
151 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
152 let version = *self as u8;
153 write!(f, "FV{:b}", version)
154 }
155}
156
157#[non_exhaustive]
158#[derive(Default, Debug, Clone)]
159#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
160pub struct Layout {
162 pub version: Version,
164
165 pub compact: bool,
167
168 pub character_tables: Vec<CharacterTable>,
170 pub color_tables: Vec<ColorTable>,
172 pub pixmap_tables: Vec<PixmapTable>,
174 pub font_tables: Vec<FontTable>,
176}
177
178#[non_exhaustive]
179#[derive(Default, Debug, Clone)]
180#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
181#[doc = include_str!("../../res/snippets/pixmap_table/brief.md")]
182pub struct PixmapTable {
183 pub configuration_flags: PixmapTableConfigurationFlags,
185 #[doc = include_str!("../../res/snippets/pixmap_table/configurations/condition/constant_width.md")]
186 #[doc = include_str!("../../res/snippets/pixmap_table/configurations/brief/constant_width.md")]
187 pub constant_width: Option<u8>,
188 #[doc = include_str!("../../res/snippets/pixmap_table/configurations/condition/constant_height.md")]
189 #[doc = include_str!("../../res/snippets/pixmap_table/configurations/brief/constant_height.md")]
190 pub constant_height: Option<u8>,
191 #[doc = include_str!("../../res/snippets/pixmap_table/configurations/condition/constant_bits_per_pixel.md")]
192 #[doc = include_str!("../../res/snippets/pixmap_table/configurations/brief/constant_bits_per_pixel.md")]
193 pub constant_bits_per_pixel: Option<u8>,
194
195 pub link_flags: PixmapTableLinkFlags,
197 #[doc = include_str!("../../res/snippets/pixmap_table/links/condition/color_tables.md")]
198 #[doc = include_str!("../../res/snippets/pixmap_table/links/brief/color_tables.md")]
199 pub color_table_indexes: Option<Vec<u8>>,
200
201 pub pixmaps: Vec<Pixmap>,
203}
204
205#[non_exhaustive]
206#[derive(Default, Debug, Clone)]
207#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
208pub struct Pixmap {
210 #[doc = include_str!("../../res/snippets/pixmap_table/records/condition/custom_width.md")]
211 #[doc = include_str!("../../res/snippets/pixmap_table/records/brief/custom_width.md")]
212 pub custom_width: Option<u8>,
213 #[doc = include_str!("../../res/snippets/pixmap_table/records/condition/custom_height.md")]
214 #[doc = include_str!("../../res/snippets/pixmap_table/records/brief/custom_height.md")]
215 pub custom_height: Option<u8>,
216 #[doc = include_str!("../../res/snippets/pixmap_table/records/condition/custom_bits_per_pixel.md")]
217 #[doc = include_str!("../../res/snippets/pixmap_table/records/brief/custom_bits_per_pixel.md")]
218 pub custom_bits_per_pixel: Option<u8>,
219 #[doc = include_str!("../../res/snippets/pixmap_table/records/condition/data.md")]
220 #[doc = include_str!("../../res/snippets/pixmap_table/records/brief/data.md")]
221 pub data: Vec<u8>,
222}
223
224#[non_exhaustive]
225#[derive(Default, Debug, Clone)]
226#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
227#[doc = include_str!("../../res/snippets/character_table/brief.md")]
228pub struct CharacterTable {
229 pub modifier_flags: CharacterTableModifierFlags,
231
232 pub configuration_flags: CharacterTableConfigurationFlags,
234 #[doc = include_str!("../../res/snippets/character_table/configurations/condition/constant_code_point_count.md")]
235 #[doc = include_str!("../../res/snippets/character_table/configurations/brief/constant_code_point_count.md")]
236 pub constant_code_point_count: Option<u8>,
237
238 pub link_flags: CharacterTableLinkFlags,
240 #[doc = include_str!("../../res/snippets/character_table/links/condition/pixmap_tables.md")]
241 #[doc = include_str!("../../res/snippets/character_table/links/brief/pixmap_tables.md")]
242 pub pixmap_table_indexes: Option<Vec<u8>>,
243
244 pub characters: Vec<Character>,
246}
247
248#[non_exhaustive]
249#[derive(Default, Debug, Clone)]
250#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
251pub struct Character {
253 #[doc = include_str!("../../res/snippets/character_table/records/condition/advance_x.md")]
254 #[doc = include_str!("../../res/snippets/character_table/records/brief/advance_x.md")]
255 pub advance_x: Option<u8>,
256 #[doc = include_str!("../../res/snippets/character_table/records/condition/pixmap_index.md")]
257 #[doc = include_str!("../../res/snippets/character_table/records/brief/pixmap_index.md")]
258 pub pixmap_index: Option<u8>,
259 #[doc = include_str!("../../res/snippets/character_table/records/condition/pixmap_table_index.md")]
260 #[doc = include_str!("../../res/snippets/character_table/records/brief/pixmap_table_index.md")]
261 pub pixmap_table_index: Option<u8>,
262
263 #[doc = include_str!("../../res/snippets/character_table/records/condition/code_points.md")]
264 #[doc = include_str!("../../res/snippets/character_table/records/brief/code_points.md")]
265 #[doc = "`spf` handles optional null characters automatically."]
266 pub code_points: String,
267}
268
269#[non_exhaustive]
270#[derive(Default, Debug, Clone)]
271#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
272#[doc = include_str!("../../res/snippets/color_table/brief.md")]
273pub struct ColorTable {
274 pub modifier_flags: ColorTableModifierFlags,
276
277 pub configuration_flags: ColorTableConfigurationFlags,
279 #[doc = include_str!("../../res/snippets/color_table/configurations/condition/constant_alpha.md")]
280 #[doc = include_str!("../../res/snippets/color_table/configurations/brief/constant_alpha.md")]
281 pub constant_alpha: Option<u8>,
282
283 pub colors: Vec<Color>,
285}
286
287#[repr(u8)]
288#[non_exhaustive]
289#[derive(Default, Debug, Clone, Copy)]
290#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
291#[doc = include_str!("../../res/snippets/data_types/ColorType.md")]
292pub enum ColorType {
293 #[default]
294 #[doc = include_str!("../../res/snippets/data_types/ColorType/Dynamic.md")]
295 Dynamic,
296 #[doc = include_str!("../../res/snippets/data_types/ColorType/Absolute.md")]
297 Absolute,
298}
299
300#[non_exhaustive]
301#[derive(Default, Debug, Clone)]
302#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
303pub struct Color {
305 #[doc = include_str!("../../res/snippets/color_table/records/condition/color_type.md")]
306 #[doc = include_str!("../../res/snippets/color_table/records/brief/color_type.md")]
307 pub color_type: Option<ColorType>,
308 #[doc = include_str!("../../res/snippets/color_table/records/condition/custom_alpha.md")]
309 #[doc = include_str!("../../res/snippets/color_table/records/brief/custom_alpha.md")]
310 pub custom_alpha: Option<u8>,
311 #[doc = include_str!("../../res/snippets/color_table/records/condition/red.md")]
312 #[doc = include_str!("../../res/snippets/color_table/records/brief/red.md")]
313 pub red: u8,
314 #[doc = include_str!("../../res/snippets/color_table/records/condition/green.md")]
315 #[doc = include_str!("../../res/snippets/color_table/records/brief/green.md")]
316 pub green: u8,
317 #[doc = include_str!("../../res/snippets/color_table/records/condition/blue.md")]
318 #[doc = include_str!("../../res/snippets/color_table/records/brief/blue.md")]
319 pub blue: u8,
320}
321
322#[non_exhaustive]
323#[derive(Default, Debug, Clone)]
324#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
325#[doc = include_str!("../../res/snippets/font_table/brief.md")]
326pub struct FontTable {
327 pub link_flags: FontTableLinkFlags,
329 #[doc = include_str!("../../res/snippets/font_table/links/condition/character_tables.md")]
330 #[doc = include_str!("../../res/snippets/font_table/links/brief/character_tables.md")]
331 pub character_table_indexes: Option<Vec<u8>>,
332
333 pub fonts: Vec<Font>,
335}
336
337#[non_exhaustive]
338#[derive(Default, Debug, Clone)]
339#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
340pub struct Font {
342 #[doc = include_str!("../../res/snippets/font_table/records/condition/name.md")]
343 #[doc = include_str!("../../res/snippets/font_table/records/brief/name.md")]
344 pub name: String,
345 #[doc = include_str!("../../res/snippets/font_table/records/condition/author.md")]
346 #[doc = include_str!("../../res/snippets/font_table/records/brief/author.md")]
347 pub author: String,
348 #[doc = include_str!("../../res/snippets/font_table/records/condition/version.md")]
349 #[doc = include_str!("../../res/snippets/font_table/records/brief/version.md")]
350 pub version: u8,
351 #[doc = include_str!("../../res/snippets/font_table/records/condition/font_type.md")]
352 #[doc = include_str!("../../res/snippets/font_table/records/brief/font_type.md")]
353 pub font_type: FontType,
354 #[doc = include_str!("../../res/snippets/font_table/records/condition/linked_character_table_indexes.md")]
355 #[doc = include_str!("../../res/snippets/font_table/records/brief/linked_character_table_indexes.md")]
356 pub linked_character_table_indexes: Vec<u8>,
357}
358
359#[repr(u8)]
360#[non_exhaustive]
361#[rustfmt::skip]
362enum TableIdentifier {
363 Character = 0b00000001,
364 Pixmap = 0b00000010,
365 Color = 0b00000011,
366 Font = 0b00000100,
367}
368
369impl TryFrom<u8> for TableIdentifier {
370 type Error = DeserializeError;
371
372 fn try_from(value: u8) -> Result<Self, Self::Error> {
373 match value {
374 0b00000001 => Ok(TableIdentifier::Character),
375 0b00000010 => Ok(TableIdentifier::Pixmap),
376 0b00000011 => Ok(TableIdentifier::Color),
377 0b00000100 => Ok(TableIdentifier::Font),
378 _ => Err(DeserializeError::UnsupportedTableIdentifier),
379 }
380 }
381}
382
383impl TryFrom<u8> for Version {
384 type Error = DeserializeError;
385
386 fn try_from(value: u8) -> Result<Self, Self::Error> {
387 match value {
388 0b00000000 => Ok(Version::FV0),
389 _ => Err(DeserializeError::UnsupportedVersion),
390 }
391 }
392}
393
394impl TryFrom<u8> for ColorType {
395 type Error = DeserializeError;
396
397 fn try_from(value: u8) -> Result<Self, Self::Error> {
398 match value {
399 0 => Ok(ColorType::Dynamic),
400 1 => Ok(ColorType::Absolute),
401 _ => Err(DeserializeError::UnsupportedColorType),
402 }
403 }
404}
405
406impl TryFrom<u8> for FontType {
407 type Error = DeserializeError;
408
409 fn try_from(value: u8) -> Result<Self, Self::Error> {
410 FontType::from_bits(value).ok_or(DeserializeError::UnsupportedFontType)
411 }
412}
413
414#[non_exhaustive]
415#[derive(Debug)]
416pub enum DeserializeError {
418 #[doc = include_str!("../../res/snippets/errors/unexpected_end_of_file.md")]
419 UnexpectedEndOfFile,
420 #[doc = include_str!("../../res/snippets/errors/invalid_signature.md")]
421 InvalidSignature,
422 #[doc = include_str!("../../res/snippets/errors/unsupported_version.md")]
423 UnsupportedVersion,
424 #[doc = include_str!("../../res/snippets/errors/unsupported_color_type.md")]
425 UnsupportedColorType,
426 #[doc = include_str!("../../res/snippets/errors/unsupported_table_identifier.md")]
427 UnsupportedTableIdentifier,
428 #[doc = include_str!("../../res/snippets/errors/unsupported_font_type.md")]
429 UnsupportedFontType,
430}
431
432#[non_exhaustive]
433#[derive(Debug)]
434pub enum SerializeError {
436 #[doc = include_str!("../../res/snippets/errors/static_vector_too_large.md")]
437 StaticVectorTooLarge,
438 #[doc = include_str!("../../res/snippets/errors/invalid_pixmap_data.md")]
439 InvalidPixmapData,
440}
441
442pub(crate) trait Table: Sized {
443 fn deserialize<R: ByteReader, T: TagWriter>(
444 engine: &mut DeserializeEngine<R, T>,
445 ) -> Result<Self, DeserializeError>;
446 fn serialize<T: TagWriter>(
447 &self,
448 engine: &mut SerializeEngine<T>,
449 ) -> Result<(), SerializeError>;
450}
451
452pub struct DeserializeEngine<'a, R: ByteReader = ByteReaderImpl<'a>, T: TagWriter = TagWriterNoOp> {
454 bytes: R,
455 pub layout: Layout,
457 #[cfg(feature = "tagging")]
458 pub tags: T,
460 #[cfg(feature = "tagging")]
461 tagging_data: TaggingData,
462 _phantom: PhantomData<T>,
463 _phantom2: &'a PhantomData<R>,
464}
465
466#[non_exhaustive]
467#[derive(Default)]
468pub(crate) struct TaggingData {
469 current_table_index: u8,
470 current_record_index: u8,
471}
472
473pub struct SerializeEngine<'a, T: TagWriter = TagWriterNoOp> {
475 bytes: byte::ByteWriter,
476 pub layout: &'a Layout,
478 #[cfg(feature = "tagging")]
479 pub tags: T,
481 #[cfg(feature = "tagging")]
482 tagging_data: TaggingData,
483 _phantom: PhantomData<T>,
484}
485
486pub(crate) fn deserialize_layout<R: ByteReader, T: TagWriter>(
487 engine: &mut DeserializeEngine<R, T>,
488) -> Result<(), DeserializeError> {
489 deserialize::next_signature(engine)?;
490 deserialize::next_version(engine)?;
491 deserialize::next_header(engine)?;
492
493 while engine.bytes.index() < engine.bytes.len() - 1 {
494 match engine.bytes.next().try_into()? {
495 TableIdentifier::Character => {
496 #[cfg(feature = "tagging")]
497 {
498 engine.tagging_data.current_table_index =
499 engine.layout.character_tables.len() as u8;
500 }
501 let table = CharacterTable::deserialize(engine)?;
502 engine.layout.character_tables.push(table);
503 }
504 TableIdentifier::Pixmap => {
505 #[cfg(feature = "tagging")]
506 {
507 engine.tagging_data.current_table_index =
508 engine.layout.pixmap_tables.len() as u8;
509 }
510 let table = PixmapTable::deserialize(engine)?;
511 engine.layout.pixmap_tables.push(table);
512 }
513 TableIdentifier::Color => {
514 #[cfg(feature = "tagging")]
515 {
516 engine.tagging_data.current_table_index =
517 engine.layout.color_tables.len() as u8;
518 }
519 let table = ColorTable::deserialize(engine)?;
520 engine.layout.color_tables.push(table);
521 }
522 TableIdentifier::Font => {
523 #[cfg(feature = "tagging")]
524 {
525 engine.tagging_data.current_table_index = engine.layout.font_tables.len() as u8;
526 }
527 let table = FontTable::deserialize(engine)?;
528 engine.layout.font_tables.push(table);
529 }
530 };
531 }
532 Ok(())
533}
534
535pub fn deserialize_with_engine<R: ByteReader, T: TagWriter>(
537 engine: &mut DeserializeEngine<R, T>,
538) -> Result<(), DeserializeError> {
539 deserialize_layout(engine)?;
540 Ok(())
541}
542
543pub fn layout_from_data(buffer: &[u8]) -> Result<Layout, DeserializeError> {
546 let mut engine = DeserializeEngine::from_data(buffer);
547 deserialize_with_engine(&mut engine)?;
548 Ok(engine.layout)
549}
550
551pub(crate) fn serialize_layout<T: TagWriter>(
552 engine: &mut SerializeEngine<T>,
553) -> Result<(), SerializeError> {
554 serialize::push_signature(engine);
555 serialize::push_version(engine);
556 serialize::push_header(engine);
557
558 for (index, character_table) in engine.layout.character_tables.iter().enumerate() {
559 #[cfg(feature = "tagging")]
560 {
561 engine.tagging_data.current_table_index = index as u8;
562 }
563 character_table.serialize(engine)?;
564 }
565 for (index, pixmap_table) in engine.layout.pixmap_tables.iter().enumerate() {
566 #[cfg(feature = "tagging")]
567 {
568 engine.tagging_data.current_table_index = index as u8;
569 }
570 pixmap_table.serialize(engine)?;
571 }
572 for (index, color_table) in engine.layout.color_tables.iter().enumerate() {
573 #[cfg(feature = "tagging")]
574 {
575 engine.tagging_data.current_table_index = index as u8;
576 }
577 color_table.serialize(engine)?;
578 }
579 for (index, font_table) in engine.layout.font_tables.iter().enumerate() {
580 #[cfg(feature = "tagging")]
581 {
582 engine.tagging_data.current_table_index = index as u8;
583 }
584 font_table.serialize(engine)?;
585 }
586
587 Ok(())
588}
589
590pub fn serialize_with_engine<T: TagWriter>(
592 engine: &mut SerializeEngine<T>,
593) -> Result<(), SerializeError> {
594 serialize_layout(engine)?;
595 Ok(())
596}
597
598pub fn layout_to_data(layout: &Layout) -> Result<Vec<u8>, SerializeError> {
601 let mut engine = SerializeEngine::from_layout(layout);
602 serialize_with_engine(&mut engine)?;
603 Ok(engine.data_owned())
604}