1use core::ffi::c_str::CStr;
17use std::path::Path;
18
19use encoding_rs::WINDOWS_1252;
20
21use super::{Bits, ByteInput as Input, prelude::*};
22
23mod huffman;
24pub use huffman::Tree;
25
26pub type Header = Vec<Entry>;
32
33pub fn header(input: &mut Input<'_>) -> ModalResult<Vec<Entry>> {
45 let (_, file_count, entry_len, _) = (magic_tag, le_u16, u8, take(3u8)).parse_next(input)?;
46 let header: Vec<Entry> =
47 repeat(..=file_count as usize, cut_err(take(entry_len).and_then(entry)))
48 .parse_next(input)?;
49 Ok(header)
50}
51
52#[derive(Debug)]
54pub struct Entry {
55 pub name: String,
60 pub packed: usize,
62 pub len: usize,
64}
65
66fn entry(input: &mut Input<'_>) -> ModalResult<Entry> {
67 let name = take(132usize).map(cstr).parse_next(input)?;
69
70 let (packed, len) = (le_i32.map(as_usize), le_i32.map(as_usize)).parse_next(input)?;
72
73 let _ = take(1usize).parse_next(input)?;
74
75 Ok(Entry { name, packed, len })
81}
82
83pub struct PackedFile<'a> {
85 pub name: String,
86 pub data: &'a [u8],
87 pub len: usize,
88}
89
90pub fn packed_file<'a>(entry: &Entry) -> impl FnMut(&mut Input<'a>) -> ModalResult<PackedFile<'a>> {
93 move |input: &mut Input<'_>| {
94 take(entry.packed)
95 .map(|data: &[u8]| PackedFile { name: entry.name.clone(), data, len: entry.len })
96 .parse_next(input)
97 }
98}
99
100pub fn unpack_cbv_to_disk(input: &mut Input<'_>) -> ModalResult<Header> {
105 unpack_cbv_to(Path::new(".")).parse_next(input)
106}
107
108pub fn unpack_cbv_to<'a>(directory: &'a Path) -> impl FnMut(&mut Input<'a>) -> ModalResult<Header> {
109 move |input: &mut Input<'_>| {
110 let header = header.parse_next(input)?;
111 for file in header.iter() {
112 let mut packed: PackedFile = packed_file(file).parse_next(input)?;
113 let unpacked = unpack_file.parse_next(&mut packed.data)?;
114 assert_eq!(file.len, unpacked.len());
115 let path = directory.join(&file.name);
116 if let Some(parent) = path.parent() {
117 std::fs::create_dir_all(parent).expect("can create output directory");
118 }
119 std::fs::write(path, &unpacked).expect("can write");
120 }
121 Ok(header)
122 }
123}
124
125pub fn unpack_file(input: &mut Input<'_>) -> ModalResult<Vec<u8>> {
137 let mut data = Vec::new();
138 while !input.is_empty() {
139 let (packing, mut block) = read_block.parse_next(input)?;
141
142 let block = if packing.huffman {
144 unpack_huffman(&mut block).expect("correct Huffman")
145 } else {
146 block.to_vec()
147 };
148
149 let mut block = if packing.rle {
151 unpack_rle_backref(&mut block.as_slice()).expect("correct RLE/backref")
152 } else {
153 block
154 };
155 data.append(&mut block);
156 }
157 Ok(data)
158}
159
160#[derive(Clone, Copy, Debug)]
162pub struct Packing {
163 pub huffman: bool,
165 pub rle: bool,
167}
168
169pub fn read_block<'a>(input: &mut Input<'a>) -> ModalResult<(Packing, &'a [u8])> {
170 let (block_size, _) = (le_u16, take(2usize)).parse_next(input)?;
173 let (packing, block) = take(block_size as usize).parse_next(input)?.split_first().unwrap();
174 assert!(*packing < 4);
175 let rle = packing & 1 != 0;
176 let huffman = packing & 2 != 0;
177 Ok((Packing { huffman, rle }, block))
178}
179
180pub fn unpack_huffman(input: &mut Input<'_>) -> winnow::Result<Vec<u8>> {
182 use core::array::from_fn;
183
184 let len = be_u16(input)? as usize;
186
187 let mut bits = Bits::from(*input);
189 let values: [(usize, u16); 256] = from_fn(|_| huffman_node(&mut bits).unwrap());
190
191 let tree = Tree::load(values);
193 let unpacked = tree.unpack(bits, len).unwrap();
194
195 assert_eq!(len, unpacked.len());
196 Ok(unpacked)
197}
198
199fn huffman_node(bits: &mut Bits) -> Option<(usize, u16)> {
200 let len = bits.unsigned(4)?;
201 let bits = bits.unsigned(len)? as u16;
202 Some((len, bits))
203}
204
205pub fn unpack_rle_backref(input: &mut Input<'_>) -> winnow::Result<Vec<u8>> {
207 use winnow::binary::{le_u16, u8};
208
209 let mut result = vec![];
210 while !input.is_empty() {
211 let indicator = le_u16(input)?.to_be_bytes();
212 let indicator = Bits::from(indicator.as_slice());
213
214 for encoded in indicator {
215 if input.is_empty() {
216 return Ok(result);
217 }
218
219 if !encoded {
220 result.push(u8(input)?);
221 continue;
222 }
223
224 let (high, low) = u8(input).map(nibbles)?;
225
226 match high {
227 0 => {
229 result.append(&mut vec![u8(input)?; 3 + low]);
230 }
231 1 => {
233 let size = 19 + low + ((u8(input)? as usize) << 4);
234 result.append(&mut vec![u8(input)?; size]);
235 }
236 _ => {
238 let start = result.len() - (3 + low + ((u8(input)? as usize) << 4));
240 let end = start
241 + if high > 2 {
242 high as usize
244 } else {
245 16 + u8(input)? as usize
247 };
248 result.extend_from_within(start..end);
249 }
250 }
251 }
252 }
253
254 Ok(result)
255}
256
257fn nibbles(x: u8) -> (u8, usize) {
258 (x >> 4, x as usize & 0xF)
259}
260
261fn magic_tag(input: &mut Input<'_>) -> ModalResult<[u8; 2]> {
266 const MAGIC_NUMBER: [u8; 2] = [0x08, 0x00];
267 MAGIC_NUMBER.as_slice().map(|_| MAGIC_NUMBER).parse_next(input)
268}
269
270fn as_usize(x: i32) -> usize {
271 x as usize
272}
273
274pub fn cstr(input: &[u8]) -> String {
275 let name = CStr::from_bytes_until_nul(input).expect("valid name");
276 let (cow, encoding_used, had_errors) = WINDOWS_1252.decode(name.to_bytes());
277 assert_eq!(encoding_used, WINDOWS_1252);
278 assert!(!had_errors);
279 cow.to_string()
280}
281
282#[test]
287fn example() {
288 const EXAMPLE: &[u8] = include_bytes!("../../examples/twic1616.cbv");
289
290 let header = unpack_cbv_to_disk.parse(EXAMPLE).unwrap();
292 println!("{header:?}");
293}