Skip to main content

egdata_manifests_parser/types/
header.rs

1use hex;
2use log::debug;
3use serde::{Deserialize, Serialize};
4use std::io::{Read, Seek};
5
6use crate::parser::reader::ReadExt;
7use crate::{error::Error, types::flags::*};
8
9const MANIFEST_MAGIC: u32 = 0x44BEC00C;
10
11#[derive(Debug, Clone, Serialize, Deserialize, Default)]
12pub struct ManifestHeader {
13    pub header_size: i32,
14    pub data_size_uncompressed: i32,
15    pub data_size_compressed: i32,
16    pub sha1_hash: String,
17    pub stored_as: u8,
18    pub version: i32,
19    pub guid: String,
20    pub rolling_hash: i64,
21    pub hash_type: u32,
22}
23
24impl ManifestHeader {
25    pub fn read<R: Read + Seek>(mut rdr: R) -> Result<Self, Error> {
26        // Read and verify magic number
27        let magic = rdr.u32()?;
28        if magic != MANIFEST_MAGIC {
29            return Err(Error::Invalid("invalid manifest magic number".to_string()));
30        }
31
32        // Read header size
33        let header_size = rdr.i32()?;
34        debug!("  Header size from file: {}", header_size);
35
36        // Read data sizes
37        let data_size_uncompressed = rdr.i32()?;
38        let data_size_compressed = rdr.i32()?;
39
40        // Read SHA1 hash
41        let mut hash = [0u8; 20];
42        rdr.read_exact(&mut hash)?;
43        debug!("Raw SHA-1 bytes from file: {:02x?}", hash);
44
45        // Read stored_as flag
46        let stored_as = rdr.u8()?;
47
48        // Read version if header size > 37 bytes
49        let version = if header_size > 37 {
50            rdr.i32()?
51        } else {
52            0 // Default to 0 for older versions
53        };
54
55        // Skip to the end of the header
56        let current_pos = rdr.stream_position()?;
57        if current_pos < header_size as u64 {
58            rdr.seek(std::io::SeekFrom::Start(header_size as u64))?;
59        }
60
61        Ok(Self {
62            header_size,
63            data_size_uncompressed,
64            data_size_compressed,
65            sha1_hash: hex::encode(hash),
66            stored_as,
67            version,
68            guid: String::new(), // Not used in newer versions
69            rolling_hash: 0,     // Not used in newer versions
70            hash_type: 0,        // Not used in newer versions
71        })
72    }
73
74    /// helpers
75    pub fn is_compressed(&self) -> bool {
76        self.stored_as & STORED_COMPRESSED != 0
77    }
78    pub fn is_encrypted(&self) -> bool {
79        self.stored_as & STORED_ENCRYPTED != 0
80    }
81}