use std::io::{Read, Seek, SeekFrom};
use crate::VssError;
const CATALOG_BLOCK_SIZE: usize = 0x4000; const ENTRY_SIZE: usize = 128;
const HEADER_SIZE: usize = 128;
#[derive(Debug, Clone)]
pub enum CatalogEntry {
Meta(StoreMeta),
Location(StoreLocation),
Empty,
}
#[derive(Debug, Clone)]
pub struct StoreMeta {
pub volume_size: u64,
pub store_id: [u8; 16],
pub sequence: u64,
pub flags: u64,
pub creation_time: u64,
}
#[derive(Debug, Clone)]
pub struct StoreLocation {
pub block_list_offset: u64,
pub store_id: [u8; 16],
pub store_header_offset: u64,
pub block_range_list_offset: u64,
pub bitmap_offset: u64,
}
pub fn parse_catalog<R: Read + Seek>(
reader: &mut R,
first_block_offset: u64,
) -> Result<Vec<CatalogEntry>, VssError> {
let mut entries = Vec::new();
let mut current_offset = first_block_offset;
loop {
if current_offset == 0 {
break;
}
reader.seek(SeekFrom::Start(current_offset))
.map_err(VssError::Io)?;
let mut block = vec![0u8; CATALOG_BLOCK_SIZE];
reader.read_exact(&mut block).map_err(VssError::Io)?;
let record_type = u32::from_le_bytes(block[20..24].try_into().unwrap());
if record_type != 0x02 {
break;
}
let next_offset = u64::from_le_bytes(block[40..48].try_into().unwrap());
let mut pos = HEADER_SIZE;
while pos + ENTRY_SIZE <= CATALOG_BLOCK_SIZE {
let entry_data = &block[pos..pos + ENTRY_SIZE];
let entry_type = u64::from_le_bytes(entry_data[0..8].try_into().unwrap());
match entry_type {
0x02 => {
entries.push(CatalogEntry::Meta(StoreMeta {
volume_size: u64::from_le_bytes(entry_data[8..16].try_into().unwrap()),
store_id: entry_data[16..32].try_into().unwrap(),
sequence: u64::from_le_bytes(entry_data[32..40].try_into().unwrap()),
flags: u64::from_le_bytes(entry_data[40..48].try_into().unwrap()),
creation_time: u64::from_le_bytes(entry_data[48..56].try_into().unwrap()),
}));
}
0x03 => {
entries.push(CatalogEntry::Location(StoreLocation {
block_list_offset: u64::from_le_bytes(entry_data[8..16].try_into().unwrap()),
store_id: entry_data[16..32].try_into().unwrap(),
store_header_offset: u64::from_le_bytes(entry_data[32..40].try_into().unwrap()),
block_range_list_offset: u64::from_le_bytes(entry_data[40..48].try_into().unwrap()),
bitmap_offset: u64::from_le_bytes(entry_data[48..56].try_into().unwrap()),
}));
}
0x00 => {
break;
}
_ => {
entries.push(CatalogEntry::Empty);
}
}
pos += ENTRY_SIZE;
}
current_offset = next_offset;
}
Ok(entries)
}