1use arrayvec::ArrayVec;
2use binrw::BinWrite;
3use libtw2_common::digest::Sha256;
4use libtw2_common::num::Cast;
5use libtw2_huffman::instances::TEEWORLDS as HUFFMAN;
6use libtw2_packer::with_packer;
7use std::io;
8use thiserror::Error;
9
10use crate::format::CappedString;
11use crate::format::ChunkHeader;
12use crate::format::DataKind;
13use crate::format::DemoKind;
14use crate::format::Header;
15use crate::format::MapSha256;
16use crate::format::RawChunk;
17use crate::format::TickMarker;
18use crate::format::TimelineMarkers;
19use crate::format::Version;
20use crate::format::MAX_SNAPSHOT_SIZE;
21
22#[derive(Error, Debug)]
23#[error(transparent)]
24pub struct WriteError(#[from] binrw::Error);
25
26impl WriteError {
27 pub fn io_error(self) -> Result<io::Error, WriteError> {
28 match self.0 {
29 binrw::Error::Io(io) => Ok(io),
30 err => Err(WriteError(err)),
31 }
32 }
33}
34
35pub struct Writer {
36 file: Box<dyn SeekableWrite>,
37 header: Header,
38 prev_tick: Option<i32>,
39 huffman: ArrayVec<[u8; MAX_SNAPSHOT_SIZE]>,
40 buffer2: ArrayVec<[u8; MAX_SNAPSHOT_SIZE]>,
41}
42
43const WRITER_VERSION: Version = Version::V5;
44const WRITER_VERSION_DDNET: Version = Version::V6Ddnet;
45
46pub(crate) trait SeekableWrite: io::Write + io::Seek {}
47impl<T: io::Write + io::Seek> SeekableWrite for T {}
48
49impl Writer {
50 pub fn new<W: io::Write + io::Seek + 'static>(
51 file: W,
52 net_version: &[u8],
53 map_name: &[u8],
54 map_sha256: Option<Sha256>,
55 map_crc: u32,
56 kind: DemoKind,
57 length: i32,
58 timestamp: &[u8],
59 map: &[u8],
60 ) -> Result<Writer, WriteError> {
61 let mut writer = Writer {
62 file: Box::new(file),
63 header: Header {
64 net_version: CappedString::from_raw(net_version),
65 map_name: CappedString::from_raw(map_name),
66 map_size: map.len().assert_i32(),
67 map_crc: map_crc,
68 kind: kind,
69 length,
70 timestamp: CappedString::from_raw(timestamp),
71 },
72 prev_tick: None,
73 huffman: ArrayVec::new(),
74 buffer2: ArrayVec::new(),
75 };
76 writer.write_header(map_sha256.is_some())?;
77 TimelineMarkers {
78 amount: 0,
79 markers: [0; 64],
80 }
81 .write(&mut writer.file)?;
82 if let Some(sha256) = map_sha256 {
83 MapSha256::new(sha256).write_le(&mut writer.file)?;
84 }
85 map.write(&mut writer.file)?;
86 Ok(writer)
87 }
88 fn write_header(&mut self, ddnet: bool) -> Result<(), WriteError> {
89 let version = if ddnet {
90 WRITER_VERSION_DDNET
91 } else {
92 WRITER_VERSION
93 };
94 version.write(&mut self.file)?;
95 self.header.write(&mut self.file)?;
96 Ok(())
97 }
98 pub fn write_chunk(&mut self, chunk: RawChunk) -> Result<(), WriteError> {
99 match chunk {
100 RawChunk::Tick { tick, keyframe } => self.write_tick(keyframe, tick),
101 RawChunk::Snapshot(snapshot) => self.write_snapshot(snapshot),
102 RawChunk::SnapshotDelta(delta) => self.write_snapshot_delta(delta),
103 RawChunk::Message(msg) => self.write_message(msg),
104 RawChunk::Unknown => panic!(),
105 }
106 }
107 pub fn write_tick(&mut self, keyframe: bool, tick: i32) -> Result<(), WriteError> {
108 let tm = TickMarker::new(tick, self.prev_tick, keyframe, WRITER_VERSION);
109 ChunkHeader::Tick {
110 marker: tm,
111 keyframe: keyframe,
112 }
113 .write(&mut self.file, WRITER_VERSION)?;
114 self.prev_tick = Some(tick);
115 Ok(())
116 }
117 fn write_chunk_impl(&mut self, kind: DataKind, data: Option<&[u8]>) -> Result<(), WriteError> {
118 let data = data.unwrap_or(&self.buffer2);
119 self.huffman.clear();
120 HUFFMAN
121 .compress(data, &mut self.huffman)
122 .expect("too long compression");
123 ChunkHeader::Data {
124 kind,
125 size: self.huffman.len().assert_u16(),
126 }
127 .write(&mut self.file, WRITER_VERSION)?;
128 self.file
129 .write_all(&self.huffman)
130 .map_err(binrw::Error::Io)?;
131 Ok(())
132 }
133 pub fn write_snapshot(&mut self, snapshot: &[u8]) -> Result<(), WriteError> {
134 self.write_chunk_impl(DataKind::Snapshot, Some(snapshot))
135 }
136 pub fn write_snapshot_delta(&mut self, delta: &[u8]) -> Result<(), WriteError> {
137 self.write_chunk_impl(DataKind::SnapshotDelta, Some(delta))
138 }
139 pub fn write_message(&mut self, msg: &[u8]) -> Result<(), WriteError> {
140 self.buffer2.clear();
141 with_packer(
142 &mut self.buffer2,
143 |mut p| -> Result<(), buffer::CapacityError> {
144 for b in msg.chunks(4) {
145 fn g(bytes: &[u8], idx: usize) -> u8 {
147 bytes.get(idx).cloned().unwrap_or(0)
148 }
149 p.write_int(i32::from_le_bytes([g(b, 0), g(b, 1), g(b, 2), g(b, 3)]))?;
150 }
151 Ok(())
152 },
153 )
154 .expect("overlong message");
155 self.write_chunk_impl(DataKind::Message, None)
156 }
157 }