1#![deny(rustdoc::broken_intra_doc_links)]
2#![deny(unsafe_code)]
3#![doc = include_str!("../README.md")]
4
5pub use error::{ASEError, ConformationError};
6use types::{BlockType, GroupHold};
7pub use types::{ColorBlock, ColorType, ColorValue, Group};
8
9mod buffer;
10mod error;
11mod types;
12
13pub fn create_ase(groups: Vec<Group>, colors: Vec<ColorBlock>) -> Vec<u8> {
23 let group_size: u32 = groups.iter().map(Group::calculate_length).sum();
24 let color_size: u32 = colors.iter().map(ColorBlock::calculate_length).sum();
25 let mut buf = buffer::Buffer::with_capacity((8 + group_size * 2 + color_size) as usize);
27
28 buf.write_slice(types::FILE_SIGNATURE);
30 buf.write_u32(types::VERSION);
31 buf.write_u32((groups.len() + colors.len()) as u32);
33
34 for group in groups {
36 group.write(&mut buf);
37 }
38
39 for block in colors {
41 block.write(&mut buf);
42 }
43
44 buf.into_vec()
45}
46
47pub fn read_ase<T: std::io::Read>(mut ase: T) -> Result<(Vec<Group>, Vec<ColorBlock>), ASEError> {
63 let mut buf_u32 = [0; 4];
64
65 ase.read_exact(&mut buf_u32)?;
67 if &buf_u32 != types::FILE_SIGNATURE {
68 return Err(ASEError::Invalid(error::ConformationError::FileSignature));
69 }
70
71 ase.read_exact(&mut buf_u32)?;
73 if buf_u32 != types::VERSION.to_be_bytes() {
74 return Err(ASEError::Invalid(error::ConformationError::FileVersion));
75 }
76
77 ase.read_exact(&mut buf_u32)?;
78 let number_of_blocks = u32::from_be_bytes(buf_u32);
79
80 let mut groups = Vec::new();
81 let mut color_blocks = Vec::new();
82 let mut buf_u16 = [0; 2];
83
84 let mut group_hold = GroupHold::Empty;
86 let mut group_hold_value = Group::default();
87
88 let mut blocks_to_read = number_of_blocks;
89
90 let mut skipped = 0;
92 let mut safe_to_skip = false;
93
94 while blocks_to_read > 0 {
95 ase.read_exact(&mut buf_u16)?;
96
97 if buf_u16 == [0, 0] && skipped < 2 && safe_to_skip {
99 skipped += 1;
100 continue;
101 }
102
103 let block_type = BlockType::try_from(u16::from_be_bytes(buf_u16))?;
104
105 if block_type != BlockType::GroupEnd && group_hold == GroupHold::HoldingBuilt {
106 return Err(ASEError::Invalid(error::ConformationError::GroupEnd));
107 }
108
109 let block_length = if block_type == BlockType::GroupEnd {
113 safe_to_skip = true;
114 skipped = 0;
115 0
116 } else {
117 ase.read_exact(&mut buf_u32)?;
118 let block_length = u32::from_be_bytes(buf_u32);
119 safe_to_skip = false;
120 block_length
121 };
122
123 let mut block = vec![0; block_length as usize];
124 ase.read_exact(&mut block)?;
125
126 match block_type {
128 BlockType::GroupStart => {
129 let block = Group::parse(&block)?;
130 if group_hold != GroupHold::Empty {
131 return Err(ASEError::Invalid(error::ConformationError::GroupEnd));
132 }
133 group_hold = if block.blocks.is_empty() {
137 GroupHold::HoldingBuilding
138 } else {
139 blocks_to_read += 1;
140 GroupHold::HoldingBuilt
141 };
142 group_hold_value = block;
143 }
144 BlockType::GroupEnd => match group_hold {
146 GroupHold::HoldingBuilding | GroupHold::HoldingBuilt => {
147 groups.push(group_hold_value.clone());
148 group_hold = GroupHold::Empty;
149 }
150 GroupHold::Empty => {
151 return Err(ASEError::Invalid(error::ConformationError::GroupEnd))
152 }
153 },
154 BlockType::ColorEntry => {
155 let block = ColorBlock::parse(&block)?;
156 match group_hold {
157 GroupHold::HoldingBuilding => group_hold_value.blocks.push(block),
158 GroupHold::Empty => color_blocks.push(block),
159 GroupHold::HoldingBuilt => {
160 return Err(ASEError::Invalid(error::ConformationError::GroupEnd))
161 }
162 }
163 }
164 }
165
166 blocks_to_read -= 1;
167 }
168
169 if group_hold == GroupHold::HoldingBuilding {
171 groups.push(group_hold_value);
172 }
173
174 if group_hold == GroupHold::HoldingBuilt {
176 return Err(ASEError::Invalid(error::ConformationError::GroupEnd));
177 }
178
179 Ok((groups, color_blocks))
180}
181
182#[cfg(test)]
183mod tests {
184 use crate::error::ConformationError;
185
186 use super::*;
187
188 #[test]
189 fn it_writes_empty_args() {
190 assert_eq!(
191 create_ase(vec![], vec![]),
192 vec![65, 83, 69, 70, 0, 1, 0, 0, 0, 0, 0, 0]
193 )
194 }
195
196 #[test]
197 fn it_writes_single_color() {
198 let block = ColorBlock::new("name".to_owned(), ColorValue::Gray(0.5), ColorType::Normal);
199 assert_eq!(
200 create_ase(vec![], vec![block]),
201 vec![
202 65, 83, 69, 70, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 22, 0, 5, 0, 110, 0, 97, 0,
203 109, 0, 101, 0, 0, 71, 114, 97, 121, 63, 0, 0, 0, 0, 2
204 ]
205 )
206 }
207
208 #[test]
209 fn it_writes_group_color() {
210 let group = Group::new(
211 "group name".to_owned(),
212 vec![
213 ColorBlock::new(
214 "light grey".to_owned(),
215 ColorValue::Gray(0.5),
216 ColorType::Normal,
217 ),
218 ColorBlock::new(
219 "dark red".to_owned(),
220 ColorValue::Rgb(0.5, 0.3, 0.1),
221 ColorType::Normal,
222 ),
223 ],
224 );
225 assert_eq!(
226 create_ase(vec![group], vec![]),
227 vec![
228 65, 83, 69, 70, 0, 1, 0, 0, 0, 0, 0, 1, 192, 1, 0, 0, 0, 108, 0, 11, 0, 103, 0,
229 114, 0, 111, 0, 117, 0, 112, 0, 32, 0, 110, 0, 97, 0, 109, 0, 101, 0, 0, 0, 1, 0,
230 0, 0, 34, 0, 11, 0, 108, 0, 105, 0, 103, 0, 104, 0, 116, 0, 32, 0, 103, 0, 114, 0,
231 101, 0, 121, 0, 0, 71, 114, 97, 121, 63, 0, 0, 0, 0, 2, 0, 1, 0, 0, 0, 38, 0, 9, 0,
232 100, 0, 97, 0, 114, 0, 107, 0, 32, 0, 114, 0, 101, 0, 100, 0, 0, 82, 71, 66, 32,
233 63, 0, 0, 0, 62, 153, 153, 154, 61, 204, 204, 205, 0, 2, 192, 2
234 ]
235 )
236 }
237
238 #[test]
239 fn it_writes_group_and_single_color() {
240 let group = Group::new(
241 "group name".to_owned(),
242 vec![
243 ColorBlock::new(
244 "light grey".to_owned(),
245 ColorValue::Gray(0.5),
246 ColorType::Normal,
247 ),
248 ColorBlock::new(
249 "dark red".to_owned(),
250 ColorValue::Rgb(0.5, 0.3, 0.1),
251 ColorType::Normal,
252 ),
253 ],
254 );
255 let block = ColorBlock::new("name".to_owned(), ColorValue::Gray(0.5), ColorType::Normal);
256 assert_eq!(
257 create_ase(vec![group], vec![block]),
258 vec![
259 65, 83, 69, 70, 0, 1, 0, 0, 0, 0, 0, 2, 192, 1, 0, 0, 0, 108, 0, 11, 0, 103, 0,
260 114, 0, 111, 0, 117, 0, 112, 0, 32, 0, 110, 0, 97, 0, 109, 0, 101, 0, 0, 0, 1, 0,
261 0, 0, 34, 0, 11, 0, 108, 0, 105, 0, 103, 0, 104, 0, 116, 0, 32, 0, 103, 0, 114, 0,
262 101, 0, 121, 0, 0, 71, 114, 97, 121, 63, 0, 0, 0, 0, 2, 0, 1, 0, 0, 0, 38, 0, 9, 0,
263 100, 0, 97, 0, 114, 0, 107, 0, 32, 0, 114, 0, 101, 0, 100, 0, 0, 82, 71, 66, 32,
264 63, 0, 0, 0, 62, 153, 153, 154, 61, 204, 204, 205, 0, 2, 192, 2, 0, 1, 0, 0, 0, 22,
265 0, 5, 0, 110, 0, 97, 0, 109, 0, 101, 0, 0, 71, 114, 97, 121, 63, 0, 0, 0, 0, 2
266 ]
267 )
268 }
269
270 #[test]
271 fn it_reads_empty() {
272 let res = read_ase(&*vec![65, 83, 69, 70, 0, 1, 0, 0, 0, 0, 0, 0]);
273 assert!(res.is_ok());
274 assert_eq!(res.unwrap(), (vec![], vec![]));
275 }
276
277 #[test]
278 fn it_reads_single_color() {
279 let block = ColorBlock::new("name".to_owned(), ColorValue::Gray(0.5), ColorType::Normal);
280 let res = read_ase(&*create_ase(vec![], vec![block.clone()]));
281 assert!(res.is_ok());
282 let res = res.unwrap();
283 assert_eq!(res, (vec![], vec![block]));
284 assert_eq!(res.1.first().unwrap().name, "name".to_owned());
285 }
286
287 #[test]
288 fn it_reads_group() {
289 let group = Group::new(
290 "group name".to_owned(),
291 vec![
292 ColorBlock::new(
293 "light grey".to_owned(),
294 ColorValue::Gray(0.5),
295 ColorType::Normal,
296 ),
297 ColorBlock::new(
298 "dark red".to_owned(),
299 ColorValue::Rgb(0.5, 0.3, 0.1),
300 ColorType::Normal,
301 ),
302 ],
303 );
304 let res = read_ase(&*create_ase(vec![group.clone()], vec![]));
305 assert!(res.is_ok());
306 let res = res.unwrap();
307 assert_eq!(res, (vec![group], vec![]));
308 assert_eq!(res.0.first().unwrap().name, "group name".to_owned());
309 }
310
311 #[test]
312 fn it_reads_group_and_single_color() {
313 let group = Group::new(
314 "group name".to_owned(),
315 vec![
316 ColorBlock::new(
317 "light grey".to_owned(),
318 ColorValue::Gray(0.5),
319 ColorType::Normal,
320 ),
321 ColorBlock::new(
322 "dark red".to_owned(),
323 ColorValue::Rgb(0.5, 0.3, 0.1),
324 ColorType::Normal,
325 ),
326 ],
327 );
328 let block = ColorBlock::new("name".to_owned(), ColorValue::Gray(0.5), ColorType::Normal);
329 let res = read_ase(&*create_ase(vec![group.clone()], vec![block.clone()]));
330 assert!(res.is_ok());
331 let res = res.unwrap();
332 assert_eq!(res, (vec![group], vec![block]));
333 assert_eq!(res.0.first().unwrap().name, "group name".to_owned());
334 assert_eq!(res.1.first().unwrap().name, "name".to_owned());
335 }
336
337 #[test]
338 fn it_reads_group_and_single_color_with_explicit_group_end_size() {
339 let group = Group::new(
340 "group name".to_owned(),
341 vec![
342 ColorBlock::new(
343 "light grey".to_owned(),
344 ColorValue::Gray(0.5),
345 ColorType::Normal,
346 ),
347 ColorBlock::new(
348 "dark red".to_owned(),
349 ColorValue::Rgb(0.5, 0.3, 0.1),
350 ColorType::Normal,
351 ),
352 ],
353 );
354 let block = ColorBlock::new("name".to_owned(), ColorValue::Gray(0.5), ColorType::Normal);
355 let input_ase_bytes = create_ase(vec![group.clone()], vec![block.clone()]);
356 let mut modified_ase_bytes = vec![0; 0];
357
358 modified_ase_bytes.extend_from_slice(&input_ase_bytes[..128]);
370 modified_ase_bytes.extend_from_slice(&[0; 4]);
371 modified_ase_bytes.extend_from_slice(&input_ase_bytes[128..]);
372 let res = read_ase(&*input_ase_bytes);
373 assert!(res.is_ok());
374 let res = res.unwrap();
375 assert_eq!(res, (vec![group], vec![block]));
376 }
377
378 #[test]
379 fn it_reads_group_and_single_color_with_group_block_name_only_size() {
380 let group = Group::new(
381 "group name".to_owned(),
382 vec![
383 ColorBlock::new(
384 "light grey".to_owned(),
385 ColorValue::Gray(0.5),
386 ColorType::Normal,
387 ),
388 ColorBlock::new(
389 "dark red".to_owned(),
390 ColorValue::Rgb(0.5, 0.3, 0.1),
391 ColorType::Normal,
392 ),
393 ],
394 );
395 let block = ColorBlock::new("name".to_owned(), ColorValue::Gray(0.5), ColorType::Normal);
396 let input_ase_bytes = create_ase(vec![group.clone()], vec![block.clone()]);
397 let mut modified_ase_bytes = vec![0; 0];
398
399 modified_ase_bytes.extend_from_slice(&input_ase_bytes[..8]);
418 modified_ase_bytes.extend_from_slice(&(5_u32.to_be_bytes()));
419 modified_ase_bytes.extend_from_slice(&input_ase_bytes[12..14]);
420 modified_ase_bytes.extend_from_slice(&(24_u32.to_be_bytes()));
421 modified_ase_bytes.extend_from_slice(&input_ase_bytes[18..]);
422 let res = read_ase(&*modified_ase_bytes);
423 assert!(res.is_ok());
424 let res = res.unwrap();
425 assert_eq!(res, (vec![group], vec![block]));
426 }
427
428 #[test]
429 fn it_reads_group_and_single_color_with_group_block_name_only_size_and_explicit_group_end_size()
430 {
431 let group = Group::new(
432 "group name".to_owned(),
433 vec![
434 ColorBlock::new(
435 "light grey".to_owned(),
436 ColorValue::Gray(0.5),
437 ColorType::Normal,
438 ),
439 ColorBlock::new(
440 "dark red".to_owned(),
441 ColorValue::Rgb(0.5, 0.3, 0.1),
442 ColorType::Normal,
443 ),
444 ],
445 );
446 let block = ColorBlock::new("name".to_owned(), ColorValue::Gray(0.5), ColorType::Normal);
447 let input_ase_bytes = create_ase(vec![group.clone()], vec![block.clone()]);
448 let mut modified_ase_bytes = vec![0; 0];
449
450 modified_ase_bytes.extend_from_slice(&input_ase_bytes[..8]);
471 modified_ase_bytes.extend_from_slice(&(5_u32.to_be_bytes()));
472 modified_ase_bytes.extend_from_slice(&input_ase_bytes[12..14]);
473 modified_ase_bytes.extend_from_slice(&(24_u32.to_be_bytes()));
474 modified_ase_bytes.extend_from_slice(&input_ase_bytes[18..128]);
475 modified_ase_bytes.extend_from_slice(&[0; 4]);
476 modified_ase_bytes.extend_from_slice(&input_ase_bytes[128..]);
477 let res = read_ase(&*modified_ase_bytes);
478 assert!(res.is_ok());
479 let res = res.unwrap();
480 assert_eq!(res, (vec![group], vec![block]));
481 }
482
483 #[test]
484 fn it_returns_incorrect_block_type_error() {
485 let input_bad_block_type = vec![
486 65, 83, 69, 70, 0, 1, 0, 0, 0, 0, 0, 1, 0, 2, 0, 0, 0, 22, 0, 5, 0, 110, 0, 97, 0, 109,
487 0, 101, 0, 0, 71, 114, 97, 121, 63, 0, 0, 0, 0, 2,
488 ];
489 let parser_result = read_ase(&*input_bad_block_type);
490 assert!(
491 parser_result.is_err(),
492 "Parser result must be an error with an invalid block type."
493 );
494 assert!(
495 matches!(parser_result.err(), Some(ASEError::BlockTypeError)),
496 "Expected bad block type error"
497 );
498 }
499
500 #[test]
501 fn it_returns_incorrect_color_type_error() {
502 let input_bad_color_type = vec![
503 65, 83, 69, 70, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 22, 0, 5, 0, 110, 0, 97, 0, 109,
504 0, 101, 0, 0, 71, 114, 97, 121, 63, 0, 0, 0, 0, 3,
505 ];
506 let parser_result = read_ase(&*input_bad_color_type);
507 assert!(
508 parser_result.is_err(),
509 "Parser result must be an error with an invalid color type."
510 );
511 assert!(
512 matches!(parser_result.err(), Some(ASEError::ColorTypeError)),
513 "Expected bad color type error"
514 );
515 }
516
517 #[test]
518 fn it_returns_incorrect_color_format_error() {
519 let input_bad_color_format = vec![
520 65, 83, 69, 70, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 22, 0, 5, 0, 110, 0, 97, 0, 109,
521 0, 101, 0, 0, 72, 114, 97, 121, 63, 0, 0, 0, 0, 3,
522 ];
523 let parser_result: Result<(Vec<Group>, Vec<ColorBlock>), ASEError> =
524 read_ase(&*input_bad_color_format);
525 assert!(
526 parser_result.is_err(),
527 "Parser result must be an error with an invalid color format."
528 );
529 assert!(
530 matches!(parser_result.err(), Some(ASEError::ColorFormat)),
531 "Expected bad color format error"
532 );
533 }
534
535 #[test]
536 fn it_returns_incorrect_signature_error() {
537 let input_bad_signature = vec![65, 80, 69, 70, 1, 1, 0, 0, 0, 0, 0, 0];
538 let parser_result = read_ase(&*input_bad_signature);
539 assert!(
540 parser_result.is_err(),
541 "Parser result must be an error with an invalid signature."
542 );
543 assert!(
544 matches!(
545 parser_result.err(),
546 Some(ASEError::Invalid(ConformationError::FileSignature))
547 ),
548 "Only ASEError::Invalid(error::ConformationError::FileSignature) should be returned"
549 );
550 }
551
552 #[test]
553 fn it_returns_incorrect_version_error() {
554 let input_bad_file_version = vec![65, 83, 69, 70, 1, 1, 0, 0, 0, 0, 0, 0];
555 let parser_result = read_ase(&*input_bad_file_version);
556 assert!(
557 parser_result.is_err(),
558 "Parser result must be an error with an invalid file version."
559 );
560 assert!(
561 matches!(
562 parser_result.err(),
563 Some(ASEError::Invalid(ConformationError::FileVersion))
564 ),
565 "Only ASEError::Invalid(error::ConformationError::FileVersion) should be returned"
566 );
567 }
568
569 #[test]
570 fn it_returns_incorrect_block_end_error() {
571 let input_bad_group_end = vec![
572 65, 83, 69, 70, 0, 1, 0, 0, 0, 0, 0, 2, 192, 1, 0, 0, 0, 108, 0, 11, 0, 103, 0, 114, 0,
573 111, 0, 117, 0, 112, 0, 32, 0, 110, 0, 97, 0, 109, 0, 101, 0, 0, 0, 1, 0, 0, 0, 34, 0,
574 11, 0, 108, 0, 105, 0, 103, 0, 104, 0, 116, 0, 32, 0, 103, 0, 114, 0, 101, 0, 121, 0,
575 0, 71, 114, 97, 121, 63, 0, 0, 0, 0, 2, 0, 1, 0, 0, 0, 38, 0, 9, 0, 100, 0, 97, 0, 114,
576 0, 107, 0, 32, 0, 114, 0, 101, 0, 100, 0, 0, 82, 71, 66, 32, 63, 0, 0, 0, 62, 153, 153,
577 154, 61, 204, 204, 205, 0, 2, 0, 1, 0, 1, 0, 0, 0, 22, 0, 5, 0, 110, 0, 97, 0, 109, 0,
578 101, 0, 0, 71, 114, 97, 121, 63, 0, 0, 0, 0, 2,
579 ];
580 let parser_result = read_ase(&*input_bad_group_end);
581 assert!(
582 parser_result.is_err(),
583 "Parser result must be an error with an invalid group end."
584 );
585 assert!(
586 matches!(
587 parser_result.err(),
588 Some(ASEError::Invalid(ConformationError::GroupEnd))
589 ),
590 "Only ASEError::Invalid(error::ConformationError::GroupEnd) should be returned"
591 );
592 }
593}