Skip to main content

egdata_manifests_parser/types/
chunk.rs

1use byteorder::{LittleEndian, ReadBytesExt};
2use hex;
3use log::debug;
4use serde::{Deserialize, Serialize};
5use std::io::{Read, Seek};
6use uuid::Uuid;
7
8use crate::error::Error;
9
10#[derive(Debug, Clone, Serialize, Deserialize, Default)]
11pub struct Chunk {
12    pub guid: String,
13    pub hash: String, // Store as hex string for NAPI compatibility
14    pub sha_hash: String,
15    pub group: u8,
16    pub window_size: u32,
17    pub file_size: String, // Store as string for NAPI compatibility
18}
19
20impl Chunk {
21    pub fn guid(&self) -> String {
22        self.guid.to_string()
23    }
24
25    pub fn hash(&self) -> String {
26        self.hash.to_string()
27    }
28
29    pub fn sha_hash(&self) -> String {
30        self.sha_hash.to_string()
31    }
32}
33
34#[derive(Debug, Clone, Serialize, Deserialize, Default)]
35pub struct ChunkDataList {
36    pub data_size: u32,
37    pub data_version: u8,
38    pub count: u32,
39    pub elements: Vec<Chunk>,
40    #[serde(skip)]
41    pub chunk_lookup: std::collections::HashMap<String, u32>,
42}
43
44impl ChunkDataList {
45    pub fn read<R: Read + Seek>(mut rdr: R) -> Result<Self, Error> {
46        debug!(
47            "Reading chunk list at position: {} (0x{:x})",
48            rdr.stream_position()?,
49            rdr.stream_position()?
50        );
51
52        let data_size = rdr.read_u32::<LittleEndian>()?;
53        debug!("  Data size: {} (0x{:x})", data_size, data_size);
54
55        if data_size == 0 || data_size > 1024 * 1024 * 1024 {
56            // 1GB max
57            return Err(Error::Invalid(format!(
58                "Invalid data size: {} (0x{:x}). Must be between 1 and 1GB",
59                data_size, data_size
60            )));
61        }
62
63        let data_version = rdr.read_u8()?;
64        debug!("  Data version: {} (0x{:x})", data_version, data_version);
65
66        let count = rdr.read_u32::<LittleEndian>()?;
67        debug!("  Count: {} (0x{:x})", count, count);
68
69        if count > 1_000_000 {
70            // Reasonable max chunk count
71            return Err(Error::Invalid(format!(
72                "Invalid count: {} (0x{:x}). Must be less than 1,000,000",
73                count, count
74            )));
75        }
76
77        let mut elements = Vec::with_capacity(count as usize);
78        let mut chunk_lookup = std::collections::HashMap::with_capacity(count as usize);
79
80        debug!("\nReading GUIDs...");
81        for i in 0..count {
82            let mut guid_bytes = [0u8; 16];
83            rdr.read_exact(&mut guid_bytes)?;
84            let guid = Uuid::from_bytes(guid_bytes);
85            let guid_str = guid.to_string();
86            chunk_lookup.insert(guid_str.clone(), i);
87            elements.push(Chunk {
88                guid: guid_str,
89                hash: String::new(),
90                sha_hash: String::new(),
91                group: 0,
92                window_size: 0,
93                file_size: String::new(),
94            });
95        }
96
97        debug!("\nReading hashes...");
98        for chunk in &mut elements {
99            let hash = rdr.read_u64::<LittleEndian>()?;
100            chunk.hash = format!("{:016x}", hash);
101        }
102
103        debug!("\nReading SHA hashes...");
104        for chunk in &mut elements {
105            let mut sha_hash = [0u8; 20];
106            rdr.read_exact(&mut sha_hash)?;
107            chunk.sha_hash = hex::encode(sha_hash);
108        }
109
110        debug!("\nReading groups...");
111        for chunk in &mut elements {
112            chunk.group = rdr.read_u8()?;
113        }
114
115        debug!("\nReading window sizes...");
116        for chunk in &mut elements {
117            chunk.window_size = rdr.read_u32::<LittleEndian>()?;
118        }
119
120        debug!("\nReading file sizes...");
121        for chunk in &mut elements {
122            let file_size = rdr.read_u64::<LittleEndian>()?;
123            chunk.file_size = file_size.to_string();
124        }
125
126        Ok(Self {
127            data_size,
128            data_version,
129            count,
130            elements,
131            chunk_lookup,
132        })
133    }
134}
135
136#[derive(Debug, Clone, Serialize, Deserialize, Default)]
137pub struct ChunkPart {
138    pub data_size: u32,
139    pub parent_guid: String,
140    pub offset: u32,
141    pub size: u32,
142    #[serde(skip)]
143    pub chunk: Option<Chunk>, // Reference to parent chunk
144}
145
146impl ChunkPart {
147    pub fn read<R: Read + Seek>(
148        rdr: &mut R,
149        chunk_lookup: &std::collections::HashMap<String, u32>,
150        chunks: &[Chunk],
151    ) -> Result<Self, Error> {
152        let data_size = rdr.read_u32::<LittleEndian>()?;
153
154        // Read GUID
155        let mut guid_bytes = [0u8; 16];
156        rdr.read_exact(&mut guid_bytes)?;
157        let parent_guid = Uuid::from_bytes(guid_bytes).to_string();
158
159        // Validate parent GUID exists in chunk lookup
160        if !chunk_lookup.contains_key(&parent_guid) {
161            return Err(Error::Invalid(format!(
162                "Parent GUID {} not found in chunk lookup",
163                parent_guid
164            )));
165        }
166
167        let offset = rdr.read_u32::<LittleEndian>()?;
168        let size = rdr.read_u32::<LittleEndian>()?;
169
170        // Get reference to parent chunk
171        let chunk_idx = chunk_lookup[&parent_guid];
172        let chunk = chunks.get(chunk_idx as usize).cloned();
173
174        Ok(Self {
175            data_size,
176            parent_guid,
177            offset,
178            size,
179            chunk,
180        })
181    }
182}