flash_lso/amf0/
write.rs

1/// Support for encoding AMF0
2use crate::types::{Element, Value};
3use crate::PADDING;
4use cookie_factory::bytes::{be_f64, be_u16, be_u32, be_u8};
5use cookie_factory::{SerializeFn, WriteContext};
6use std::io::Write;
7
8use crate::amf0::type_marker::TypeMarker;
9use crate::amf3::write::AMF3Encoder;
10use crate::nom_utils::write_string;
11use cookie_factory::combinator::slice;
12use cookie_factory::combinator::string;
13use cookie_factory::multi::all;
14use cookie_factory::sequence::tuple;
15use std::ops::Deref;
16use std::rc::Rc;
17
18fn write_type_marker<'a, 'b: 'a, W: Write + 'a>(type_: TypeMarker) -> impl SerializeFn<W> + 'a {
19    be_u8(type_ as u8)
20}
21
22fn write_number_element<'a, 'b: 'a, W: Write + 'a>(s: f64) -> impl SerializeFn<W> + 'a {
23    tuple((write_type_marker(TypeMarker::Number), be_f64(s)))
24}
25
26fn write_bool_element<'a, 'b: 'a, W: Write + 'a>(s: bool) -> impl SerializeFn<W> + 'a {
27    tuple((
28        write_type_marker(TypeMarker::Boolean),
29        be_u8(if s { 1u8 } else { 0u8 }),
30    ))
31}
32
33fn write_long_string_content<'a, 'b: 'a, W: Write + 'a>(s: &'b str) -> impl SerializeFn<W> + 'a {
34    tuple((be_u32(s.len() as u32), string(s)))
35}
36
37fn write_long_string_element<'a, 'b: 'a, W: Write + 'a>(s: &'b str) -> impl SerializeFn<W> + 'a {
38    tuple((
39        write_type_marker(TypeMarker::LongString),
40        write_long_string_content(s),
41    ))
42}
43
44fn write_string_element<'a, 'b: 'a, W: Write + 'a>(s: &'b str) -> impl SerializeFn<W> + 'a {
45    tuple((write_type_marker(TypeMarker::String), write_string(s)))
46}
47
48fn write_object_element<'a, 'b: 'a, W: Write + 'a>(o: &'b [Element]) -> impl SerializeFn<W> + 'a {
49    tuple((
50        write_type_marker(TypeMarker::Object),
51        all(o.iter().map(write_element)),
52        be_u16(0),
53        write_type_marker(TypeMarker::ObjectEnd),
54    ))
55}
56
57fn write_null_element<'a, 'b: 'a, W: Write + 'a>() -> impl SerializeFn<W> + 'a {
58    write_type_marker(TypeMarker::Null)
59}
60
61fn write_undefined_element<'a, 'b: 'a, W: Write + 'a>() -> impl SerializeFn<W> + 'a {
62    write_type_marker(TypeMarker::Undefined)
63}
64
65fn write_strict_array_element<'a, 'b: 'a, W: Write + 'a>(
66    elements: &'b [Rc<Value>],
67) -> impl SerializeFn<W> + 'a {
68    tuple((
69        write_type_marker(TypeMarker::Array),
70        be_u32(elements.len() as u32),
71        all(elements.iter().map(write_value)),
72    ))
73}
74
75fn write_date_element<'a, 'b: 'a, W: Write + 'a>(
76    date: f64,
77    tz: Option<u16>,
78) -> impl SerializeFn<W> + 'a {
79    tuple((
80        write_type_marker(TypeMarker::Date),
81        be_f64(date),
82        be_u16(tz.unwrap_or(0)),
83    ))
84}
85
86fn write_unsupported_element<'a, 'b: 'a, W: Write + 'a>() -> impl SerializeFn<W> + 'a {
87    write_type_marker(TypeMarker::Unsupported)
88}
89
90fn write_xml_element<'a, 'b: 'a, W: Write + 'a>(content: &'b str) -> impl SerializeFn<W> + 'a {
91    tuple((
92        write_type_marker(TypeMarker::XML),
93        write_long_string_content(content),
94    ))
95}
96
97fn write_typed_object_element<'a, 'b: 'a, W: Write + 'a>(
98    name: &'b str,
99    elements: &'b [Element],
100) -> impl SerializeFn<W> + 'a {
101    tuple((
102        write_type_marker(TypeMarker::TypedObject),
103        write_string(name),
104        all(elements.iter().map(write_element)),
105        be_u16(0),
106        write_type_marker(TypeMarker::ObjectEnd),
107    ))
108}
109
110fn write_mixed_array<'a, 'b: 'a, W: Write + 'a>(
111    elements: &'b [Element],
112    length: u32,
113) -> impl SerializeFn<W> + 'a {
114    //TODO: what is the u16 padding
115    //TODO: sometimes array length is ignored (u32) sometimes its: elements.len() as u32
116
117    tuple((
118        write_type_marker(TypeMarker::MixedArrayStart),
119        be_u32(length),
120        all(elements.iter().map(write_element)),
121        be_u16(0),
122        write_type_marker(TypeMarker::ObjectEnd),
123    ))
124}
125
126fn write_value<'a, 'b: 'a, W: Write + 'a>(element: &'b Rc<Value>) -> impl SerializeFn<W> + 'a {
127    move |out: WriteContext<W>| match element.deref() {
128        Value::Number(n) => write_number_element(*n)(out),
129        Value::Bool(b) => write_bool_element(*b)(out),
130        Value::String(s) => {
131            if s.len() > 65535 {
132                write_long_string_element(s)(out)
133            } else {
134                write_string_element(s)(out)
135            }
136        }
137        Value::Object(elements, class_def) => {
138            if let Some(class_def) = class_def {
139                write_typed_object_element(&class_def.name, elements)(out)
140            } else {
141                write_object_element(elements)(out)
142            }
143        }
144        Value::Null => write_null_element()(out),
145        Value::Undefined => write_undefined_element()(out),
146        Value::StrictArray(a) => write_strict_array_element(a.as_slice())(out),
147        Value::Date(d, tz) => write_date_element(*d, *tz)(out),
148        Value::Unsupported => write_unsupported_element()(out),
149        Value::XML(x, _string) => write_xml_element(x)(out),
150        Value::ECMAArray(_dense, elems, elems_length) => {
151            write_mixed_array(elems, *elems_length)(out)
152        }
153        Value::AMF3(e) => AMF3Encoder::default().write_value_element(e)(out),
154        _ => {
155            write_unsupported_element()(out) /* Not in amf0, TODO: use the amf3 embedding for every thing else */
156        }
157    }
158}
159
160fn write_element<'a, 'b: 'a, W: Write + 'a>(element: &'b Element) -> impl SerializeFn<W> + 'a {
161    tuple((write_string(&element.name), write_value(&element.value)))
162}
163
164fn write_element_and_padding<'a, 'b: 'a, W: Write + 'a>(
165    element: &'b Element,
166) -> impl SerializeFn<W> + 'a {
167    tuple((write_element(element), slice(PADDING)))
168}
169
170pub(crate) fn write_body<'a, 'b: 'a, W: Write + 'a>(
171    elements: &'b [Element],
172) -> impl SerializeFn<W> + 'a {
173    all(elements.iter().map(write_element_and_padding))
174}