1use std::{
13 convert::TryInto,
14 io::{
15 Error as IoError,
16 ErrorKind,
17 Read,
18 Seek,
19 SeekFrom,
20 Write,
21 },
22 str::FromStr,
23 u64,
24};
25
26use byteorder::{
27 ReadBytesExt,
28 WriteBytesExt,
29 LE,
30};
31use thiserror::Error;
32
33use crate::{
34 reader::Error as ReadError,
35 types::Version,
36 writer::Error as WriteError,
37};
38
39#[derive(Debug, Error)]
40#[error("Failed to parse chunk ID: {0}")]
41pub struct ChunkIdParseError(String);
42
43#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
49pub enum ChunkId {
50 Main,
52 Pack,
53 Size,
54 Xyzi,
55 Rgba,
56 Matt, Note,
60
61 Vox,
63 NTrn,
64 NGrp,
65 NShp,
66 Layr,
67 Matl,
68 RObj,
69 RCam,
70
71 Unsupported([u8; 4]),
73}
74
75impl ChunkId {
76 pub fn read<R: Read>(mut reader: R) -> Result<Self, ReadError> {
77 let mut id = [0u8; 4];
78 reader.read_exact(&mut id)?;
79 Ok(ChunkId::from(id))
80 }
81
82 pub fn write<W: Write>(&self, mut writer: W) -> Result<(), WriteError> {
83 let id: [u8; 4] = (*self).into();
84 Ok(writer.write_all(&id)?)
85 }
86
87 pub fn is_supported(&self) -> bool {
89 !matches!(self, ChunkId::Unsupported(_))
90 }
91}
92
93impl From<[u8; 4]> for ChunkId {
94 fn from(value: [u8; 4]) -> Self {
95 match &value {
96 b"MAIN" => Self::Main,
97 b"PACK" => Self::Pack,
98 b"SIZE" => Self::Size,
99 b"XYZI" => Self::Xyzi,
100 b"RGBA" => Self::Rgba,
101 b"MATT" => Self::Matt,
102 b"NOTE" => Self::Note,
103 b"VOX " => Self::Vox,
104 b"nTRN" => Self::NTrn,
105 b"nGRP" => Self::NGrp,
106 b"nSHP" => Self::NShp,
107 b"LAYR" => Self::Layr,
108 b"MATL" => Self::Matl,
109 b"rOBJ" => Self::RObj,
110 b"rCAM" => Self::RCam,
111 _ => Self::Unsupported(value),
112 }
113 }
114}
115
116impl From<ChunkId> for [u8; 4] {
117 fn from(chunk_id: ChunkId) -> Self {
118 match chunk_id {
119 ChunkId::Main => *b"MAIN",
120 ChunkId::Pack => *b"PACK",
121 ChunkId::Size => *b"SIZE",
122 ChunkId::Xyzi => *b"XYZI",
123 ChunkId::Rgba => *b"RGBA",
124 ChunkId::Matt => *b"MATT",
125 ChunkId::Note => *b"NOTE",
126 ChunkId::Vox => *b"VOX ",
127 ChunkId::NTrn => *b"nTRN",
128 ChunkId::NGrp => *b"nGRP",
129 ChunkId::NShp => *b"nSHP",
130 ChunkId::Layr => *b"LAYR",
131 ChunkId::Matl => *b"MATL",
132 ChunkId::RObj => *b"rOBJ",
133 ChunkId::RCam => *b"rCAM",
134 ChunkId::Unsupported(value) => value,
135 }
136 }
137}
138
139impl FromStr for ChunkId {
140 type Err = ChunkIdParseError;
141
142 fn from_str(s: &str) -> Result<Self, Self::Err> {
143 if s.as_bytes().len() != 4 {
144 return Err(ChunkIdParseError(s.to_owned()));
145 }
146
147 let mut id = [0u8; 4];
148 id.copy_from_slice(s.as_bytes());
149 Ok(ChunkId::from(id))
150 }
151}
152
153#[derive(Clone, Debug)]
157pub struct Chunk {
158 offset: u32,
159 id: ChunkId,
160 content_len: u32,
161 children_len: u32,
162}
163
164impl Chunk {
165 pub fn read<R: Read + Seek>(mut reader: R) -> Result<Self, ReadError> {
168 let offset = reader.stream_position()? as u32;
169
170 let id = ChunkId::read(&mut reader)?;
171 log::trace!("read chunk at {}: {:?}", offset, id);
172
173 let content_len = reader.read_u32::<LE>()?;
174 let children_len = reader.read_u32::<LE>()?;
175 log::trace!(
176 "content_len = {}, children_len = {}",
177 content_len,
178 children_len
179 );
180
181 Ok(Chunk {
182 offset,
183 id,
184 content_len,
185 children_len,
186 })
187 }
188
189 pub fn content<R: Read + Seek>(&self, mut reader: R) -> Result<ContentReader<R>, ReadError> {
191 let offset = self.content_offset();
192 log::trace!("content reader: id={:?}, offset={}", self.id, offset);
193 reader.seek(SeekFrom::Start(offset as u64))?;
194 Ok(ContentReader {
195 reader,
196 start: offset,
197 offset,
198 end: offset + self.content_len,
199 })
200 }
201
202 pub fn read_content_to_vec<R: Read + Seek>(&self, reader: R) -> Result<Vec<u8>, ReadError> {
203 let mut buf = vec![];
204 self.content(reader)?.read_to_end(&mut buf)?;
205 Ok(buf)
206 }
207
208 pub fn children<R: Read + Seek>(&self, reader: R) -> ChildrenReader<R> {
213 let offset = self.children_offset();
214
215 log::trace!(
216 "children reader: offset={}, end={}",
217 offset,
218 offset + self.children_len
219 );
220
221 ChildrenReader {
222 reader,
223 offset,
224 end: offset + self.children_len,
225 }
226 }
227
228 pub fn offset(&self) -> u32 {
233 self.offset
234 }
235
236 pub fn id(&self) -> ChunkId {
238 self.id
239 }
240
241 pub fn content_offset(&self) -> u32 {
244 self.offset + 12
245 }
246
247 pub fn content_len(&self) -> u32 {
249 self.content_len
250 }
251
252 pub fn children_offset(&self) -> u32 {
255 self.offset + 12 + self.content_len
256 }
257
258 pub fn children_len(&self) -> u32 {
260 self.children_len
261 }
262
263 pub fn len(&self) -> u32 {
266 self.content_len + self.children_len + 12
267 }
268
269 pub fn is_empty(&self) -> bool {
272 self.content_len == 0 && self.children_len == 0
273 }
274}
275
276pub struct ContentReader<R> {
278 reader: R,
279 offset: u32,
280 start: u32,
281 end: u32,
282}
283
284impl<R: Read> Read for ContentReader<R> {
285 fn read(&mut self, buf: &mut [u8]) -> Result<usize, IoError> {
286 log::trace!(
287 "read: offset={}, start={}, end={}",
288 self.offset,
289 self.start,
290 self.end
291 );
292 if self.offset < self.end {
293 let n_at_most = ((self.end - self.offset) as usize).min(buf.len());
294 log::trace!(
295 "read: offset={}, end={}, n_at_most={}",
296 self.offset,
297 self.end,
298 n_at_most
299 );
300 let n_read = self.reader.read(&mut buf[..n_at_most])?;
301 log::trace!("read: n_read={}", n_read);
302 self.offset += n_read as u32;
303 Ok(n_read)
304 }
305 else {
306 Ok(0)
307 }
308 }
309}
310
311impl<R: Seek> Seek for ContentReader<R> {
312 fn seek(&mut self, pos: SeekFrom) -> Result<u64, IoError> {
313 let new_offset = seek_to(self.offset, self.start, self.end, pos)?;
314
315 if new_offset != self.offset {
316 log::trace!(
317 "seek: offset={}, start={}, end={}, pos={:?}, new_offset={}",
318 self.offset,
319 self.start,
320 self.end,
321 pos,
322 new_offset
323 );
324
325 self.offset = new_offset;
326
327 self.reader.seek(SeekFrom::Start(self.offset.into()))?;
328 }
329
330 Ok((self.offset - self.start).into())
331 }
332}
333
334pub struct ChildrenReader<R> {
336 reader: R,
337 offset: u32,
338 end: u32,
339}
340
341impl<R: Read + Seek> Iterator for ChildrenReader<R> {
342 type Item = Result<Chunk, ReadError>;
343
344 fn next(&mut self) -> Option<Self::Item> {
345 log::trace!("read next child: offset={}, end={}", self.offset, self.end);
346 if self.offset < self.end {
347 Some(read_chunk_at(&mut self.reader, &mut self.offset))
348 }
349 else {
350 None
351 }
352 }
353}
354
355pub fn read_chunk_at<R: Read + Seek>(mut reader: R, offset: &mut u32) -> Result<Chunk, ReadError> {
357 log::trace!("reading chunk at {}", offset);
358 reader.seek(SeekFrom::Start((*offset).into()))?;
359 let chunk = Chunk::read(reader)?;
360 *offset += chunk.len();
361 Ok(chunk)
362}
363
364pub fn read_main_chunk<R: Read + Seek>(mut reader: R) -> Result<(Chunk, Version), ReadError> {
366 let mut buf = [0u8; 4];
367 reader.read_exact(&mut buf)?;
368 log::trace!("magic = {:?}", buf);
369 if &buf != b"VOX " {
370 return Err(ReadError::InvalidMagic { got: buf });
371 }
372
373 let version = Version::read(&mut reader)?;
374 log::trace!("version = {:?}", version);
375 if !version.is_supported() {
376 return Err(ReadError::UnsupportedFileVersion { version });
377 }
378
379 let main_chunk = Chunk::read(reader)?;
380
381 if main_chunk.id() != ChunkId::Main {
382 return Err(ReadError::ExpectedMainChunk { got: main_chunk });
383 }
384
385 Ok((main_chunk, version))
386}
387
388#[derive(Debug)]
390pub struct ChunkWriter<W> {
391 chunk_id: ChunkId,
392
393 writer: W,
394
395 offset: u64,
397
398 content_len: u32,
399
400 children_len: u32,
401}
402
403impl<W: Write + Seek> ChunkWriter<W> {
404 fn new(mut writer: W, chunk_id: ChunkId) -> Result<Self, WriteError> {
405 chunk_id.write(&mut writer)?;
406
407 let offset = writer.seek(SeekFrom::Current(8))? - 12;
410
411 Ok(Self {
412 chunk_id,
413 writer,
414 offset,
415 content_len: 0,
416 children_len: 0,
417 })
418 }
419
420 pub fn id(&self) -> ChunkId {
422 self.chunk_id
423 }
424
425 pub fn offset(&self) -> u64 {
430 self.offset
431 }
432
433 pub fn content_len(&self) -> u32 {
435 self.content_len
436 }
437
438 pub fn children_len(&self) -> u32 {
441 self.children_len
442 }
443
444 pub fn content_writer<'w, F: FnMut(&mut ContentWriter<&'w mut W>) -> Result<(), WriteError>>(
468 &'w mut self,
469 mut f: F,
470 ) -> Result<(), WriteError> {
471 if self.children_len != 0 {
475 panic!(
476 "Chunk children already written: children_len = {}",
477 self.children_len
478 );
479 }
480
481 let mut content_writer = ContentWriter::new(&mut self.writer)?;
482
483 f(&mut content_writer)?;
484
485 self.content_len += content_writer.len();
486
487 Ok(())
488 }
489
490 pub fn write_content(&mut self, data: &[u8]) -> Result<(), WriteError> {
492 self.content_writer(|content_writer| {
493 content_writer.write_all(data)?;
494 Ok(())
495 })
496 }
497
498 pub fn child_writer<'w, F: FnMut(&mut ChildWriter<'w, W>) -> Result<(), WriteError>>(
515 &'w mut self,
516 chunk_id: ChunkId,
517 mut f: F,
518 ) -> Result<(), WriteError> {
519 let mut child_writer = ChildWriter::new(ContentWriter::new(&mut self.writer)?, chunk_id)?;
520
521 f(&mut child_writer)?;
522
523 self.children_len += child_writer.writer.len();
524
525 child_writer.write_header()?;
526
527 Ok(())
528 }
529
530 pub fn child_content_writer<
533 'w,
534 F: FnMut(&mut ContentWriter<&mut ContentWriter<&'w mut W>>) -> Result<(), WriteError>,
535 >(
536 &'w mut self,
537 chunk_id: ChunkId,
538 mut f: F,
539 ) -> Result<(), WriteError> {
540 self.child_writer(chunk_id, |child_writer| {
541 child_writer.content_writer(|content_writer| f(content_writer))
542 })
543 }
544
545 fn write_header(&mut self) -> Result<(), WriteError> {
546 log::trace!(
547 "Write header for chunk {:?} to offset {}: content_len = {}, children_len = {}",
548 self.chunk_id,
549 self.offset,
550 self.content_len,
551 self.children_len
552 );
553
554 let old_pos = self.writer.seek(SeekFrom::Current(0))?;
555 self.writer.seek(SeekFrom::Start(self.offset + 4))?;
556
557 self.writer.write_u32::<LE>(self.content_len)?;
558 self.writer.write_u32::<LE>(self.children_len)?;
559
560 self.writer.seek(SeekFrom::Start(old_pos))?;
561
562 Ok(())
563 }
564}
565
566#[derive(Debug)]
569pub struct ContentWriter<W> {
570 writer: W,
571 offset: u32,
572 start: u32,
573 end: u32,
574}
575
576impl<W: Seek> ContentWriter<W> {
577 fn new(mut writer: W) -> Result<Self, WriteError> {
578 let offset = writer.stream_position()?.try_into()?;
579 Ok(Self {
580 writer,
581 offset,
582 start: offset,
583 end: offset,
584 })
585 }
586
587 fn len(&self) -> u32 {
588 self.end - self.start
589 }
590}
591
592impl<W: Write> Write for ContentWriter<W> {
593 fn write(&mut self, buf: &[u8]) -> Result<usize, IoError> {
594 let n_written = self.writer.write(buf)?;
595
596 self.offset = n_written
597 .try_into()
598 .ok()
599 .and_then(|n| self.offset.checked_add(n))
600 .ok_or_else(|| IoError::from(ErrorKind::Other))?;
601
602 if self.offset > self.end {
603 log::trace!(
604 "write {} bytes: offset={}, end={}",
605 n_written,
606 self.offset,
607 self.end
608 );
609 self.end = self.offset;
610 }
611
612 Ok(n_written)
613 }
614
615 fn flush(&mut self) -> std::io::Result<()> {
616 self.writer.flush()
617 }
618}
619
620impl<W: Seek> Seek for ContentWriter<W> {
621 fn seek(&mut self, pos: SeekFrom) -> Result<u64, IoError> {
622 let new_offset = seek_to(self.offset, self.start, self.end, pos)?;
623
624 if new_offset != self.offset {
625 log::trace!(
626 "seek: offset={}, start={}, end={}, pos={:?}, new_offset={}",
627 self.offset,
628 self.start,
629 self.end,
630 pos,
631 new_offset
632 );
633 self.writer.seek(SeekFrom::Start(new_offset.into()))?;
634 self.offset = new_offset;
635 }
636
637 Ok((self.offset - self.start).into())
638 }
639}
640
641pub type ChildWriter<'w, W> = ChunkWriter<ContentWriter<&'w mut W>>;
644
645pub fn chunk_writer<W: Write + Seek, F: FnMut(&mut ChunkWriter<W>) -> Result<(), WriteError>>(
650 writer: W,
651 chunk_id: ChunkId,
652 mut f: F,
653) -> Result<(), WriteError> {
654 let mut chunk_writer = ChunkWriter::new(writer, chunk_id)?;
655
656 f(&mut chunk_writer)?;
657
658 chunk_writer.write_header()?;
659
660 Ok(())
661}
662
663#[derive(Debug, Error)]
664#[error("The argument {pos:?} to seek is invalid.")]
665struct InvalidSeek {
666 current: u32,
667 start: u32,
668 end: u32,
669 pos: SeekFrom,
670}
671
672fn seek_to(current: u32, start: u32, end: u32, pos: SeekFrom) -> Result<u32, IoError> {
673 let (offset, delta) = match pos {
674 SeekFrom::Current(pos) => (current, Some(pos)),
675 SeekFrom::Start(pos) => (start, pos.try_into().ok()),
676 SeekFrom::End(pos) => (end, Some(pos)),
677 };
678
679 let offset = i64::from(offset);
680 let new_pos: Option<u32> = delta
681 .and_then(|d| offset.checked_add(d))
682 .and_then(|p| p.try_into().ok());
683
684 new_pos.ok_or_else(|| {
685 IoError::new(
686 ErrorKind::Other,
687 InvalidSeek {
688 current,
689 start,
690 end,
691 pos,
692 },
693 )
694 })
695}