Skip to main content

ltk_modpkg/
read.rs

1use binrw::BinRead;
2use byteorder::{ReadBytesExt, LE};
3use std::{
4    collections::HashMap,
5    io::{BufReader, Read, Seek, SeekFrom},
6};
7
8use ltk_io_ext::ReaderExt;
9
10use crate::{
11    chunk::{ModpkgChunk, NO_LAYER_HASH, NO_LAYER_INDEX},
12    error::ModpkgError,
13    hash_chunk_name, hash_layer_name, hash_wad_name, Modpkg, ModpkgLayer,
14};
15
16impl<TSource: Read + Seek> Modpkg<TSource> {
17    const MAGIC: [u8; 8] = *b"_modpkg_";
18
19    pub fn mount_from_reader(mut source: TSource) -> Result<Self, ModpkgError> {
20        let mut reader = BufReader::with_capacity(64 * 1024, &mut source);
21
22        let magic = reader.read_u64::<LE>()?;
23        if magic != u64::from_le_bytes(Self::MAGIC) {
24            return Err(ModpkgError::InvalidMagic(magic));
25        }
26
27        let version = reader.read_u32::<LE>()?;
28        if version != 1 {
29            return Err(ModpkgError::InvalidVersion(version));
30        }
31
32        let signature_size = reader.read_u32::<LE>()?;
33        let chunk_count = reader.read_u32::<LE>()?;
34
35        let mut signature = vec![0; signature_size as usize];
36        reader.read_exact(&mut signature)?;
37
38        let (layer_indices, layers) = read_layers(&mut reader)?;
39        let (chunk_path_indices, chunk_paths) = read_chunk_paths(&mut reader)?;
40        let (wads_indices, wads) = read_wads(&mut reader)?;
41
42        // Skip alignment
43        let position = reader.stream_position()?;
44        reader.seek(SeekFrom::Current(((8 - (position % 8)) % 8) as i64))?;
45
46        let mut chunks = HashMap::new();
47        let mut chunks_by_wad_layer: HashMap<(u32, u32), Vec<(u64, u64)>> = HashMap::new();
48        for _ in 0..chunk_count {
49            let chunk = ModpkgChunk::read(&mut reader)?;
50            let layer_hash = if chunk.layer_index == NO_LAYER_INDEX {
51                NO_LAYER_HASH
52            } else {
53                layer_indices[chunk.layer_index as usize]
54            };
55
56            let key = (chunk.path_hash, layer_hash);
57            chunks_by_wad_layer
58                .entry((chunk.wad_index, chunk.layer_index))
59                .or_default()
60                .push(key);
61            chunks.insert(key, chunk);
62        }
63
64        drop(reader);
65
66        Ok(Self {
67            signature,
68            layer_indices,
69            layers,
70            chunk_path_indices,
71            chunk_paths,
72            wads_indices,
73            wads,
74            chunks,
75            chunks_by_wad_layer,
76            source,
77        })
78    }
79}
80
81fn read_layers<R: Read + Seek>(
82    reader: &mut R,
83) -> Result<(Vec<u64>, HashMap<u64, ModpkgLayer>), ModpkgError> {
84    let layer_count = reader.read_u32::<LE>()?;
85    let mut layer_indices = Vec::with_capacity(layer_count as usize);
86    let mut layers = HashMap::with_capacity(layer_count as usize);
87    for _ in 0..layer_count {
88        let layer = ModpkgLayer::read(reader)?;
89        let layer_hash = hash_layer_name(&layer.name);
90        layers.insert(layer_hash, layer);
91        layer_indices.push(layer_hash);
92    }
93    Ok((layer_indices, layers))
94}
95
96fn read_chunk_paths<R: Read + Seek>(
97    reader: &mut R,
98) -> Result<(Vec<u64>, HashMap<u64, String>), ModpkgError> {
99    let chunk_paths_count = reader.read_u32::<LE>()?;
100    let mut chunk_path_indices = Vec::with_capacity(chunk_paths_count as usize);
101    let mut chunk_paths = HashMap::with_capacity(chunk_paths_count as usize);
102    for _ in 0..chunk_paths_count {
103        let chunk_path = crate::utils::normalize_chunk_path(&reader.read_str_until_nul()?);
104        let chunk_path_hash = hash_chunk_name(&chunk_path);
105        chunk_path_indices.push(chunk_path_hash);
106        chunk_paths.insert(chunk_path_hash, chunk_path);
107    }
108    Ok((chunk_path_indices, chunk_paths))
109}
110
111fn read_wads<R: Read + Seek>(
112    reader: &mut R,
113) -> Result<(Vec<u64>, HashMap<u64, String>), ModpkgError> {
114    let wads_count = reader.read_u32::<LE>()?;
115    let mut wads_indices = Vec::with_capacity(wads_count as usize);
116    let mut wads = HashMap::with_capacity(wads_count as usize);
117    for _ in 0..wads_count {
118        let wad = reader.read_str_until_nul()?;
119        let wad_hash = hash_wad_name(&wad);
120        wads.insert(wad_hash, wad);
121        wads_indices.push(wad_hash);
122    }
123    Ok((wads_indices, wads))
124}