casc_storage/archive/
mod.rs

1//! Archive file handling for CASC storage
2
3mod archive_reader;
4mod archive_writer;
5
6pub use archive_reader::ArchiveReader;
7pub use archive_writer::ArchiveWriter;
8
9use crate::error::Result;
10use crate::types::ArchiveLocation;
11use std::borrow::Cow;
12use std::path::{Path, PathBuf};
13
14/// Represents a CASC archive file (data.XXX)
15pub struct Archive {
16    /// Archive ID (the XXX in data.XXX)
17    pub id: u16,
18    /// Path to the archive file
19    pub path: PathBuf,
20    /// Current size of the archive
21    pub size: u64,
22    /// Reader for this archive
23    reader: Option<ArchiveReader>,
24}
25
26impl Archive {
27    /// Create a new archive reference
28    pub fn new(id: u16, path: PathBuf) -> Result<Self> {
29        let size = if path.exists() {
30            std::fs::metadata(&path)?.len()
31        } else {
32            0
33        };
34
35        Ok(Self {
36            id,
37            path,
38            size,
39            reader: None,
40        })
41    }
42
43    /// Open the archive for reading
44    pub fn open(&mut self) -> Result<&mut ArchiveReader> {
45        if self.reader.is_none() {
46            self.reader = Some(ArchiveReader::open(&self.path)?);
47        }
48        Ok(self.reader.as_mut().unwrap())
49    }
50
51    /// Read data from a specific location in the archive (zero-copy when possible)
52    pub fn read_at_cow(&self, location: &ArchiveLocation) -> Result<Cow<'_, [u8]>> {
53        if let Some(ref reader) = self.reader {
54            reader.read_at_cow(location.offset, location.size as usize)
55        } else {
56            // Need to open the reader first - this will require ownership
57            let mut reader = ArchiveReader::open(&self.path)?;
58            let data = reader.read_at(location.offset, location.size as usize)?;
59            Ok(Cow::Owned(data))
60        }
61    }
62
63    /// Read data from a specific location in the archive (allocates)
64    pub fn read_at(&mut self, location: &ArchiveLocation) -> Result<Vec<u8>> {
65        let reader = self.open()?;
66        reader.read_at(location.offset, location.size as usize)
67    }
68
69    /// Check if a file exists at the given location
70    pub fn contains(&self, location: &ArchiveLocation) -> bool {
71        location.archive_id == self.id && location.offset + location.size as u64 <= self.size
72    }
73
74    /// Get the archive filename (e.g., "data.001")
75    pub fn filename(&self) -> String {
76        format!("data.{:03}", self.id)
77    }
78
79    /// Get the path to this archive
80    pub fn path(&self) -> &Path {
81        &self.path
82    }
83}