Skip to main content

csp_format/
cmt.rs

1//! "cmt " documents: CSP's binary tree format for animation track mixers
2//! (`Track.TrackActionMixer` and `TrackActionMixer2`).
3//!
4//! A document is `"cmt "`, a four-character version (`0100` or `0110`),
5//! `"binc"`, a CRC-32 (little endian) of everything after it, then a string
6//! table and one root node. All numbers are little endian. Version `0110`
7//! puts a 12-byte header before each node (16, then the offsets of the
8//! attribute count and of the child count from the node's start).
9//!
10//! The string table starts with the type names of the version (see
11//! [`TYPES_0100`] / [`TYPES_0110`]), followed by every other string in the
12//! order a pre-order walk first uses it: node name, string values,
13//! attribute names and values, then the children. [`Document::to_bytes`]
14//! rebuilds the table that way, so documents CSP wrote are reproduced byte
15//! for byte. See `docs/spec/08-animation.md`.
16
17use crate::binary::{Reader, Writer};
18use crate::error::{Error, Result, bail};
19
20/// Type names of version `0100` (single precision arrays and pairs).
21pub const TYPES_0100: [&str; 22] = [
22    "null",
23    "Byte",
24    "SByte",
25    "UInt16",
26    "Int16",
27    "UInt32",
28    "Int32",
29    "Single",
30    "Double",
31    "String",
32    "Float2",
33    "Float3",
34    "Quat",
35    "Matrix44",
36    "Single[]",
37    "Byte[]",
38    "Int32[]",
39    "String[]",
40    "Float2[]",
41    "Float3[]",
42    "Quat[]",
43    "Matrix44[]",
44];
45
46/// Type names of version `0110` (double precision arrays and pairs).
47pub const TYPES_0110: [&str; 22] = [
48    "null",
49    "Byte",
50    "SByte",
51    "UInt16",
52    "Int16",
53    "UInt32",
54    "Int32",
55    "Single",
56    "Double",
57    "String",
58    "Double2",
59    "Double3",
60    "Quat",
61    "Matrix44",
62    "Double[]",
63    "Byte[]",
64    "Int32[]",
65    "String[]",
66    "Double2[]",
67    "Double3[]",
68    "Quat[]",
69    "Matrix44[]",
70];
71
72const MAGIC: &[u8; 4] = b"cmt ";
73const BINARY: &[u8; 4] = b"binc";
74/// First word of a `0110` node header.
75const NODE_HEADER: u32 = 16;
76
77/// A node's value. `Quat` and `Matrix44` have not been seen in a file and
78/// are refused rather than guessed.
79#[derive(Debug, Clone, PartialEq, serde::Serialize)]
80pub enum Value {
81    /// No value.
82    Null,
83    /// `Byte`.
84    Byte(u8),
85    /// `SByte`.
86    SByte(i8),
87    /// `UInt16`.
88    UInt16(u16),
89    /// `Int16`.
90    Int16(i16),
91    /// `UInt32`.
92    UInt32(u32),
93    /// `Int32`.
94    Int32(i32),
95    /// `Single` (f32).
96    Single(f32),
97    /// `Double` (f64).
98    Double(f64),
99    /// `String` (UTF-8, at most 255 bytes, kept in the string table).
100    String(String),
101    /// `Float2`.
102    Float2([f32; 2]),
103    /// `Float3`.
104    Float3([f32; 3]),
105    /// `Double2`.
106    Double2([f64; 2]),
107    /// `Double3`.
108    Double3([f64; 3]),
109    /// `SingleArray`.
110    SingleArray(Vec<f32>),
111    /// `DoubleArray`.
112    DoubleArray(Vec<f64>),
113    /// `ByteArray`.
114    ByteArray(Vec<u8>),
115    /// `Int32Array`.
116    Int32Array(Vec<i32>),
117    /// `StringArray`.
118    StringArray(Vec<String>),
119    /// `Float2Array`.
120    Float2Array(Vec<[f32; 2]>),
121    /// `Float3Array`.
122    Float3Array(Vec<[f32; 3]>),
123    /// `Double2Array`.
124    Double2Array(Vec<[f64; 2]>),
125    /// `Double3Array`.
126    Double3Array(Vec<[f64; 3]>),
127}
128
129impl Value {
130    /// The type name stored in the document (`"null"` for no value).
131    pub fn type_name(&self) -> &'static str {
132        match self {
133            Value::Null => "null",
134            Value::Byte(_) => "Byte",
135            Value::SByte(_) => "SByte",
136            Value::UInt16(_) => "UInt16",
137            Value::Int16(_) => "Int16",
138            Value::UInt32(_) => "UInt32",
139            Value::Int32(_) => "Int32",
140            Value::Single(_) => "Single",
141            Value::Double(_) => "Double",
142            Value::String(_) => "String",
143            Value::Float2(_) => "Float2",
144            Value::Float3(_) => "Float3",
145            Value::Double2(_) => "Double2",
146            Value::Double3(_) => "Double3",
147            Value::SingleArray(_) => "Single[]",
148            Value::DoubleArray(_) => "Double[]",
149            Value::ByteArray(_) => "Byte[]",
150            Value::Int32Array(_) => "Int32[]",
151            Value::StringArray(_) => "String[]",
152            Value::Float2Array(_) => "Float2[]",
153            Value::Float3Array(_) => "Float3[]",
154            Value::Double2Array(_) => "Double2[]",
155            Value::Double3Array(_) => "Double3[]",
156        }
157    }
158
159    /// A number (any scalar numeric type).
160    pub fn as_f64(&self) -> Option<f64> {
161        Some(match self {
162            Value::Byte(v) => *v as f64,
163            Value::SByte(v) => *v as f64,
164            Value::UInt16(v) => *v as f64,
165            Value::Int16(v) => *v as f64,
166            Value::UInt32(v) => *v as f64,
167            Value::Int32(v) => *v as f64,
168            Value::Single(v) => *v as f64,
169            Value::Double(v) => *v,
170            _ => return None,
171        })
172    }
173
174    /// A pair (`Float2` / `Double2`).
175    pub fn as_pair(&self) -> Option<[f64; 2]> {
176        match self {
177            Value::Float2([x, y]) => Some([*x as f64, *y as f64]),
178            Value::Double2(pair) => Some(*pair),
179            _ => None,
180        }
181    }
182
183    /// A numeric array (`Single[]`, `Double[]`, `Byte[]` or `Int32[]`).
184    pub fn as_numbers(&self) -> Option<Vec<f64>> {
185        match self {
186            Value::SingleArray(v) => Some(v.iter().map(|x| *x as f64).collect()),
187            Value::DoubleArray(v) => Some(v.clone()),
188            Value::ByteArray(v) => Some(v.iter().map(|x| *x as f64).collect()),
189            Value::Int32Array(v) => Some(v.iter().map(|x| *x as f64).collect()),
190            _ => None,
191        }
192    }
193
194    /// The strings of a `StringArray`.
195    pub fn as_strings(&self) -> Option<&[String]> {
196        match self {
197            Value::StringArray(v) => Some(v),
198            _ => None,
199        }
200    }
201
202    /// The text of a `String`.
203    pub fn as_str(&self) -> Option<&str> {
204        match self {
205            Value::String(v) => Some(v),
206            _ => None,
207        }
208    }
209
210    /// The strings this value adds to the string table, in order.
211    fn strings(&self) -> Vec<&str> {
212        match self {
213            Value::String(v) => vec![v.as_str()],
214            Value::StringArray(v) => v.iter().map(String::as_str).collect(),
215            _ => Vec::new(),
216        }
217    }
218}
219
220/// One node: a name, a value, attributes (name, value) and children.
221#[derive(Debug, Clone, PartialEq, serde::Serialize)]
222pub struct Node {
223    /// Node name.
224    pub name: String,
225    /// Node value.
226    pub value: Value,
227    /// Attributes (name, value), in stored order.
228    pub attributes: Vec<(String, String)>,
229    /// Child nodes, in stored order.
230    pub children: Vec<Node>,
231}
232
233impl Node {
234    /// A node without attributes or children.
235    pub fn new(name: impl Into<String>, value: Value) -> Self {
236        Node { name: name.into(), value, attributes: Vec::new(), children: Vec::new() }
237    }
238
239    /// The value of an attribute.
240    pub fn attribute(&self, name: &str) -> Option<&str> {
241        self.attributes.iter().find(|(key, _)| key == name).map(|(_, value)| value.as_str())
242    }
243
244    /// The first child with this name.
245    pub fn child(&self, name: &str) -> Option<&Node> {
246        self.children.iter().find(|child| child.name == name)
247    }
248
249    /// The first child with this name, mutably.
250    pub fn child_mut(&mut self, name: &str) -> Option<&mut Node> {
251        self.children.iter_mut().find(|child| child.name == name)
252    }
253
254    /// Follow a path of child names.
255    pub fn find(&self, path: &[&str]) -> Option<&Node> {
256        path.iter().try_fold(self, |node, name| node.child(name))
257    }
258
259    /// Follow a path of child names, mutably.
260    pub fn find_mut(&mut self, path: &[&str]) -> Option<&mut Node> {
261        path.iter().try_fold(self, |node, name| node.child_mut(name))
262    }
263
264    /// Indented text, one node per line (for inspection).
265    pub fn dump(&self) -> String {
266        let mut out = String::new();
267        self.dump_into(0, &mut out);
268        out
269    }
270
271    fn dump_into(&self, depth: usize, out: &mut String) {
272        use std::fmt::Write;
273        let _ = write!(out, "{}{}", "  ".repeat(depth), self.name);
274        if self.value != Value::Null {
275            let _ = write!(out, " : {} = {}", self.value.type_name(), value_text(&self.value));
276        }
277        if !self.attributes.is_empty() {
278            let attributes: Vec<String> = self.attributes.iter().map(|(k, v)| format!("{k}={v:?}")).collect();
279            let _ = write!(out, " [{}]", attributes.join(" "));
280        }
281        out.push('\n');
282        for child in &self.children {
283            child.dump_into(depth + 1, out);
284        }
285    }
286}
287
288fn value_text(value: &Value) -> String {
289    match value {
290        Value::String(v) => format!("{v:?}"),
291        Value::Byte(v) => v.to_string(),
292        Value::SByte(v) => v.to_string(),
293        Value::UInt16(v) => v.to_string(),
294        Value::Int16(v) => v.to_string(),
295        Value::UInt32(v) => v.to_string(),
296        Value::Int32(v) => v.to_string(),
297        Value::Single(v) => v.to_string(),
298        Value::Double(v) => v.to_string(),
299        other => serde_json::to_value(other)
300            .ok()
301            .and_then(|json| json.as_object().and_then(|o| o.values().next().cloned()))
302            .map(|inner| inner.to_string())
303            .unwrap_or_default(),
304    }
305}
306
307/// A whole document.
308#[derive(Debug, Clone, PartialEq, serde::Serialize)]
309pub struct Document {
310    /// `0100` or `0110`.
311    pub version: String,
312    /// The root node.
313    pub root: Node,
314}
315
316impl Document {
317    /// Parses a "cmt " document (checks the magic, the version and the CRC).
318    pub fn parse(data: &[u8]) -> Result<Self> {
319        let mut reader = Reader::new(data);
320        if reader.bytes(4)? != MAGIC {
321            bail!("Not a cmt document");
322        }
323        let version = String::from_utf8_lossy(reader.bytes(4)?).into_owned();
324        if version != "0100" && version != "0110" {
325            bail!("Unknown cmt version {version:?}");
326        }
327        if reader.bytes(4)? != BINARY {
328            bail!("cmt document is not binary");
329        }
330        let crc = reader.le_u32()?;
331        if crc != crc32(&data[16..]) {
332            bail!("cmt document checksum does not match");
333        }
334        let count = reader.le_u32()? as usize;
335        let mut strings = Vec::with_capacity(count.min(data.len()));
336        for _ in 0..count {
337            let length = reader.u8()? as usize;
338            let text =
339                std::str::from_utf8(reader.bytes(length)?).map_err(|_| Error::format("cmt string is not UTF-8"))?;
340            strings.push(text.to_owned());
341        }
342        let types = types(&version);
343        if strings.len() < types.len() || strings.iter().zip(types).any(|(a, b)| a != b) {
344            bail!("cmt string table does not start with the {version} type names");
345        }
346        let parser = Parser { strings: &strings, headers: version == "0110" };
347        let root = parser.node(&mut reader, 0)?;
348        if !reader.is_empty() {
349            bail!("{} bytes after the cmt root node", reader.remaining());
350        }
351        Ok(Document { version, root })
352    }
353
354    /// The document bytes with a new CRC; an unchanged document gives the same bytes.
355    pub fn to_bytes(&self) -> Result<Vec<u8>> {
356        let types = types(&self.version);
357        let mut strings: Vec<String> = types.iter().map(|t| (*t).to_owned()).collect();
358        collect_strings(&self.root, &mut strings);
359        let index = |text: &str| -> u32 { strings.iter().position(|s| s == text).expect("collected") as u32 };
360        let mut body = Writer::new();
361        body.le_u32(strings.len() as u32);
362        for text in &strings {
363            let length = u8::try_from(text.len()).map_err(|_| Error::format("cmt string longer than 255 bytes"))?;
364            body.u8(length).bytes(text.as_bytes());
365        }
366        write_node(&self.root, &mut body, &index, types, self.version == "0110")?;
367        let body = body.into_inner();
368        let mut out = Writer::new();
369        out.bytes(MAGIC).bytes(self.version.as_bytes()).bytes(BINARY).le_u32(crc32(&body)).bytes(&body);
370        Ok(out.into_inner())
371    }
372
373    /// The `ActionNodeClip` node of a track mixer.
374    pub fn action_clip(&self) -> Option<&Node> {
375        self.root.find(&["General", "ActionNodeClip"])
376    }
377
378    /// The `ActionNodeClip` node of a track mixer, mutably.
379    pub fn action_clip_mut(&mut self) -> Option<&mut Node> {
380        self.root.find_mut(&["General", "ActionNodeClip"])
381    }
382}
383
384fn types(version: &str) -> &'static [&'static str; 22] {
385    if version == "0110" { &TYPES_0110 } else { &TYPES_0100 }
386}
387
388fn collect_strings(node: &Node, strings: &mut Vec<String>) {
389    let mut add = |text: &str| {
390        if !strings.iter().any(|s| s == text) {
391            strings.push(text.to_owned());
392        }
393    };
394    add(&node.name);
395    for text in node.value.strings() {
396        add(text);
397    }
398    for (name, value) in &node.attributes {
399        add(name);
400        add(value);
401    }
402    for child in &node.children {
403        collect_strings(child, strings);
404    }
405}
406
407struct Parser<'a> {
408    strings: &'a [String],
409    headers: bool,
410}
411
412impl Parser<'_> {
413    fn string(&self, index: u32) -> Result<String> {
414        self.strings.get(index as usize).cloned().ok_or_else(|| Error::format(format!("cmt string {index} missing")))
415    }
416
417    fn node(&self, reader: &mut Reader, depth: usize) -> Result<Node> {
418        if depth > 64 {
419            bail!("cmt tree too deep");
420        }
421        let start = reader.position();
422        let offsets = if self.headers {
423            let first = reader.le_u32()?;
424            if first != NODE_HEADER {
425                bail!("cmt node header {first}, expected {NODE_HEADER}");
426            }
427            Some((reader.le_u32()? as usize, reader.le_u32()? as usize))
428        } else {
429            None
430        };
431        let name = self.string(reader.le_u32()?)?;
432        let type_index = reader.le_u32()? as usize;
433        let type_name =
434            *TYPES_0100.get(type_index).ok_or_else(|| Error::format(format!("cmt type {type_index} unknown")))?;
435        let value = self.value(reader, type_index, type_name)?;
436        if let Some((attributes, _)) = offsets
437            && reader.position() != start + attributes
438        {
439            bail!("cmt node {name:?}: attributes at {}, header says {attributes}", reader.position() - start);
440        }
441        let count = reader.le_u32()? as usize;
442        let mut attribute_list = Vec::with_capacity(count.min(reader.remaining() / 8));
443        for _ in 0..count {
444            attribute_list.push((self.string(reader.le_u32()?)?, self.string(reader.le_u32()?)?));
445        }
446        if let Some((_, children)) = offsets
447            && reader.position() != start + children
448        {
449            bail!("cmt node {name:?}: children at {}, header says {children}", reader.position() - start);
450        }
451        let count = reader.le_u32()? as usize;
452        let mut children = Vec::with_capacity(count.min(reader.remaining() / 12));
453        for _ in 0..count {
454            children.push(self.node(reader, depth + 1)?);
455        }
456        Ok(Node { name, value, attributes: attribute_list, children })
457    }
458
459    fn value(&self, reader: &mut Reader, type_index: usize, name0100: &str) -> Result<Value> {
460        // Index 7 and 8 are Single and Double in both versions; 10, 11, 14,
461        // 18 and 19 are single precision in 0100 and double in 0110.
462        let double = self.headers;
463        let pair = |r: &mut Reader| -> Result<[f64; 2]> { Ok([r.le_f64()?, r.le_f64()?]) };
464        let triple = |r: &mut Reader| -> Result<[f64; 3]> { Ok([r.le_f64()?, r.le_f64()?, r.le_f64()?]) };
465        let fpair = |r: &mut Reader| -> Result<[f32; 2]> { Ok([r.le_f32()?, r.le_f32()?]) };
466        let ftriple = |r: &mut Reader| -> Result<[f32; 3]> { Ok([r.le_f32()?, r.le_f32()?, r.le_f32()?]) };
467        let count = |r: &mut Reader, size: usize| -> Result<usize> {
468            let n = r.le_u32()? as usize;
469            if n.saturating_mul(size) > r.remaining() {
470                bail!("cmt array of {n} does not fit");
471            }
472            Ok(n)
473        };
474        Ok(match type_index {
475            0 => Value::Null,
476            1 => Value::Byte(reader.u8()?),
477            2 => Value::SByte(reader.u8()? as i8),
478            3 => Value::UInt16(reader.le_u16()?),
479            4 => Value::Int16(reader.le_u16()? as i16),
480            5 => Value::UInt32(reader.le_u32()?),
481            6 => Value::Int32(reader.le_i32()?),
482            7 => Value::Single(reader.le_f32()?),
483            8 => Value::Double(reader.le_f64()?),
484            9 => Value::String(self.string(reader.le_u32()?)?),
485            10 if double => Value::Double2(pair(reader)?),
486            10 => Value::Float2(fpair(reader)?),
487            11 if double => Value::Double3(triple(reader)?),
488            11 => Value::Float3(ftriple(reader)?),
489            14 if double => {
490                let n = count(reader, 8)?;
491                Value::DoubleArray((0..n).map(|_| reader.le_f64()).collect::<Result<_>>()?)
492            }
493            14 => {
494                let n = count(reader, 4)?;
495                Value::SingleArray((0..n).map(|_| reader.le_f32()).collect::<Result<_>>()?)
496            }
497            15 => {
498                let n = count(reader, 1)?;
499                Value::ByteArray(reader.bytes(n)?.to_vec())
500            }
501            16 => {
502                let n = count(reader, 4)?;
503                Value::Int32Array((0..n).map(|_| reader.le_i32()).collect::<Result<_>>()?)
504            }
505            17 => {
506                let n = count(reader, 4)?;
507                Value::StringArray((0..n).map(|_| self.string(reader.le_u32()?)).collect::<Result<_>>()?)
508            }
509            18 if double => {
510                let n = count(reader, 16)?;
511                Value::Double2Array((0..n).map(|_| pair(reader)).collect::<Result<_>>()?)
512            }
513            18 => {
514                let n = count(reader, 8)?;
515                Value::Float2Array((0..n).map(|_| fpair(reader)).collect::<Result<_>>()?)
516            }
517            19 if double => {
518                let n = count(reader, 24)?;
519                Value::Double3Array((0..n).map(|_| triple(reader)).collect::<Result<_>>()?)
520            }
521            19 => {
522                let n = count(reader, 12)?;
523                Value::Float3Array((0..n).map(|_| ftriple(reader)).collect::<Result<_>>()?)
524            }
525            _ => bail!("cmt value type {name0100} is not supported"),
526        })
527    }
528}
529
530fn write_node(
531    node: &Node,
532    out: &mut Writer,
533    index: &dyn Fn(&str) -> u32,
534    types: &[&str; 22],
535    headers: bool,
536) -> Result<()> {
537    let type_name = node.value.type_name();
538    let type_index = types
539        .iter()
540        .position(|t| *t == type_name)
541        .ok_or_else(|| Error::format(format!("cmt type {type_name} does not exist in this version")))?;
542    let mut value = Writer::new();
543    match &node.value {
544        Value::Null => {}
545        Value::Byte(v) => {
546            value.u8(*v);
547        }
548        Value::SByte(v) => {
549            value.u8(*v as u8);
550        }
551        Value::UInt16(v) => {
552            value.le_u16(*v);
553        }
554        Value::Int16(v) => {
555            value.le_u16(*v as u16);
556        }
557        Value::UInt32(v) => {
558            value.le_u32(*v);
559        }
560        Value::Int32(v) => {
561            value.le_i32(*v);
562        }
563        Value::Single(v) => {
564            value.le_f32(*v);
565        }
566        Value::Double(v) => {
567            value.le_f64(*v);
568        }
569        Value::String(v) => {
570            value.le_u32(index(v));
571        }
572        Value::Float2(v) => v.iter().for_each(|x| {
573            value.le_f32(*x);
574        }),
575        Value::Float3(v) => v.iter().for_each(|x| {
576            value.le_f32(*x);
577        }),
578        Value::Double2(v) => v.iter().for_each(|x| {
579            value.le_f64(*x);
580        }),
581        Value::Double3(v) => v.iter().for_each(|x| {
582            value.le_f64(*x);
583        }),
584        Value::SingleArray(v) => {
585            value.le_u32(v.len() as u32);
586            v.iter().for_each(|x| {
587                value.le_f32(*x);
588            });
589        }
590        Value::DoubleArray(v) => {
591            value.le_u32(v.len() as u32);
592            v.iter().for_each(|x| {
593                value.le_f64(*x);
594            });
595        }
596        Value::ByteArray(v) => {
597            value.le_u32(v.len() as u32).bytes(v);
598        }
599        Value::Int32Array(v) => {
600            value.le_u32(v.len() as u32);
601            v.iter().for_each(|x| {
602                value.le_i32(*x);
603            });
604        }
605        Value::StringArray(v) => {
606            value.le_u32(v.len() as u32);
607            v.iter().for_each(|x| {
608                value.le_u32(index(x));
609            });
610        }
611        Value::Float2Array(v) => {
612            value.le_u32(v.len() as u32);
613            v.iter().flatten().for_each(|x| {
614                value.le_f32(*x);
615            });
616        }
617        Value::Float3Array(v) => {
618            value.le_u32(v.len() as u32);
619            v.iter().flatten().for_each(|x| {
620                value.le_f32(*x);
621            });
622        }
623        Value::Double2Array(v) => {
624            value.le_u32(v.len() as u32);
625            v.iter().flatten().for_each(|x| {
626                value.le_f64(*x);
627            });
628        }
629        Value::Double3Array(v) => {
630            value.le_u32(v.len() as u32);
631            v.iter().flatten().for_each(|x| {
632                value.le_f64(*x);
633            });
634        }
635    }
636    let value = value.into_inner();
637    if headers {
638        let attributes = 12 + 8 + value.len();
639        let children = attributes + 4 + 8 * node.attributes.len();
640        out.le_u32(NODE_HEADER).le_u32(attributes as u32).le_u32(children as u32);
641    }
642    out.le_u32(index(&node.name)).le_u32(type_index as u32).bytes(&value);
643    out.le_u32(node.attributes.len() as u32);
644    for (name, value) in &node.attributes {
645        out.le_u32(index(name)).le_u32(index(value));
646    }
647    out.le_u32(node.children.len() as u32);
648    for child in &node.children {
649        write_node(child, out, index, types, headers)?;
650    }
651    Ok(())
652}
653
654/// CRC-32 (zlib's) of `data`.
655pub fn crc32(data: &[u8]) -> u32 {
656    let mut crc = flate2::Crc::new();
657    crc.update(data);
658    crc.sum()
659}
660
661/// Decode an external chunk payload holding a zlib-compressed document:
662/// u64 big endian (bytes that follow), u32 little endian (zlib stream
663/// length), the stream.
664pub fn unpack(payload: &[u8]) -> Result<Vec<u8>> {
665    let mut reader = Reader::new(payload);
666    let rest = reader.be_u64()?;
667    let length = reader.le_u32()? as usize;
668    if rest as usize != payload.len() - 8 || length != payload.len() - 12 {
669        bail!("cmt chunk lengths {rest}/{length} do not match its size {}", payload.len());
670    }
671    let mut out = Vec::new();
672    std::io::Read::read_to_end(&mut flate2::read::ZlibDecoder::new(&payload[12..]), &mut out)?;
673    Ok(out)
674}
675
676/// The external chunk payload of a document's bytes, compressed as CSP does.
677pub fn pack(document: &[u8]) -> Result<Vec<u8>> {
678    let stream = crate::zlib::compress(document)?;
679    let mut out = Writer::new();
680    out.be_u64(stream.len() as u64 + 4).le_u32(stream.len() as u32).bytes(&stream);
681    Ok(out.into_inner())
682}
683
684#[cfg(test)]
685mod tests {
686    use super::*;
687
688    fn sample(version: &str) -> Document {
689        let double = version == "0110";
690        let numbers = |v: &[f64]| {
691            if double {
692                Value::DoubleArray(v.to_vec())
693            } else {
694                Value::SingleArray(v.iter().map(|x| *x as f32).collect())
695            }
696        };
697        let mut curve = Node::new("FCurve", Value::Null);
698        curve.attributes.push(("Type".into(), "Opacity".into()));
699        curve.children = vec![
700            Node::new("Frame", numbers(&[10.0, 90.0])),
701            Node::new("Value", numbers(&[89.84375, 30.078125])),
702            Node::new("Interp", Value::StringArray(vec!["Constant".into(), "Default".into()])),
703            Node::new("ReviseConstant", Value::ByteArray(vec![1, 1])),
704        ];
705        let mut param = Node::new("Param", Value::Double(30.0));
706        param.attributes.push(("Name".into(), "Opacity".into()));
707        let mut anim = Node::new("AnimInfo", Value::Null);
708        anim.children = vec![param, curve];
709        let mut clip = Node::new("ActionNodeClip", Value::Null);
710        clip.attributes.push(("Name".into(), String::new()));
711        let mut time = Node::new("TimeClip", Value::Null);
712        time.children = vec![Node::new("Start", Value::Double(10.0)), Node::new("End", Value::Double(145.0))];
713        clip.children = vec![time, Node::new("Blend", Value::Byte(0)), anim];
714        let mut general = Node::new("General", Value::Null);
715        general.attributes.push(("Version".into(), if double { "2.1.0" } else { "2.0.0" }.into()));
716        general.children = vec![clip];
717        let mut info = Node::new("Version-Information", Value::Null);
718        info.children = vec![
719            Node::new("Software", Value::String("CLIP STUDIO".into())),
720            Node::new("Version", Value::String("2.1.0".into())),
721        ];
722        let mut root = Node::new("celsysdocument", Value::Null);
723        root.attributes.push(("version".into(), "1.0.0".into()));
724        root.children = vec![info, general];
725        Document { version: version.into(), root }
726    }
727
728    #[test]
729    fn both_versions_round_trip() {
730        for version in ["0100", "0110"] {
731            let document = sample(version);
732            let bytes = document.to_bytes().unwrap();
733            assert_eq!(&bytes[..8], format!("cmt {version}").as_bytes());
734            let parsed = Document::parse(&bytes).unwrap();
735            assert_eq!(parsed, document);
736            assert_eq!(parsed.to_bytes().unwrap(), bytes);
737            let clip = parsed.action_clip().unwrap();
738            assert_eq!(clip.find(&["TimeClip", "End"]).unwrap().value.as_f64(), Some(145.0));
739        }
740    }
741
742    #[test]
743    fn version_0110_has_node_headers() {
744        let bytes = sample("0110").to_bytes().unwrap();
745        let count = u32::from_le_bytes(bytes[16..20].try_into().unwrap()) as usize;
746        let mut at = 20;
747        for _ in 0..count {
748            at += 1 + bytes[at] as usize;
749        }
750        // Root: header 16, attributes after 12 + 8 (null value), children after one attribute.
751        let words: Vec<u32> =
752            (0..3).map(|i| u32::from_le_bytes(bytes[at + 4 * i..at + 4 * i + 4].try_into().unwrap())).collect();
753        assert_eq!(words, [16, 20, 32]);
754    }
755
756    #[test]
757    fn string_table_follows_first_use() {
758        let bytes = sample("0100").to_bytes().unwrap();
759        let mut reader = Reader::new(&bytes[16..]);
760        let count = reader.le_u32().unwrap() as usize;
761        let strings: Vec<String> = (0..count)
762            .map(|_| {
763                let n = reader.u8().unwrap() as usize;
764                String::from_utf8(reader.bytes(n).unwrap().to_vec()).unwrap()
765            })
766            .collect();
767        assert_eq!(
768            &strings[22..28],
769            ["celsysdocument", "version", "1.0.0", "Version-Information", "Software", "CLIP STUDIO"]
770        );
771    }
772
773    #[test]
774    fn checksum_and_packing_are_checked() {
775        let mut bytes = sample("0110").to_bytes().unwrap();
776        let packed = pack(&bytes).unwrap();
777        assert_eq!(unpack(&packed).unwrap(), bytes);
778        *bytes.last_mut().unwrap() ^= 1;
779        assert!(Document::parse(&bytes).is_err());
780    }
781}