ps2mc 0.1.0

A Rust library for reading, extracting, and rewriting PlayStation 2 memory card images.
Documentation
use super::error::Ps2mcError;

pub const SUPER_BLOCK_SIZE: usize = 340;
pub const SUPER_BLOCK_MAGIC: &[u8; 28] = b"Sony PS2 Memory Card Format ";

#[derive(Debug, Clone)]
pub struct SuperBlock {
    // pub magic: [u8; 28],
    // pub version: [u8; 12],
    pub page_size: usize,
    pub pages_per_cluster: usize,
    // pub pages_per_block: u16,
    pub clusters_per_card: usize,
    pub alloc_offset: usize,
    // pub alloc_end: u32,
    pub rootdir_cluster: usize,
    // 过滤掉 0 后的 IFC 列表
    pub ifc_list: Vec<u32>,
    // pub card_type: u8,
    // pub card_flags: u8,
    pub spare_size: usize,
    pub raw_page_size: usize,
    pub cluster_size: usize,
    pub fat_per_cluster: usize,
}

impl SuperBlock {
    /// 从传入的 340 字节切片中解析出 SuperBlock 结构。
    pub fn read_from(bytes: &[u8]) -> Result<Self, Ps2mcError> {
        // 1. 验证长度
        if bytes.len() < SUPER_BLOCK_SIZE {
            return Err(Ps2mcError::InvalidSuperBlockLength {
                expected: SUPER_BLOCK_SIZE,
                actual: bytes.len(),
            });
        }

        // 2. 验证 Magic 幻数
        if !bytes.starts_with(SUPER_BLOCK_MAGIC) {
            return Err(Ps2mcError::InvalidSuperBlockMagic);
        }

        // 3. 精准切片映射 (严格对照 Python 的 struct 偏移量)
        let mut magic = [0u8; 28];
        magic.copy_from_slice(&bytes[0..28]);

        // let mut version = [0u8; 12];
        // version.copy_from_slice(&bytes[28..40]);

        let page_size = u16::from_le_bytes([bytes[40], bytes[41]]) as usize;
        let pages_per_cluster = u16::from_le_bytes([bytes[42], bytes[43]]) as usize;
        // let pages_per_block = u16::from_le_bytes([bytes[44], bytes[45]]);

        // bytes[46..48] 是 2x 填充字节,直接跳过

        let clusters_per_card = u32::from_le_bytes([bytes[48], bytes[49], bytes[50], bytes[51]]) as usize;
        let alloc_offset = u32::from_le_bytes([bytes[52], bytes[53], bytes[54], bytes[55]]) as usize;
        // let alloc_end = u32::from_le_bytes([bytes[56], bytes[57], bytes[58], bytes[59]]);
        let rootdir_cluster = u32::from_le_bytes([bytes[60], bytes[61], bytes[62], bytes[63]]) as usize;

        // bytes[64..80] 是 backup_block 和 unknown 的 16 字节填充,直接跳过

        // 4. 解析并过滤 ifc_list (对应 Python 的 [x for x in ... if x > 0])
        // 区域处于 80..208 字节,共计 128 字节 (32 个 u32)
        let ifc_list = bytes[80..208]
            .chunks_exact(4)
            .map(|chunk| u32::from_le_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]))
            .filter(|&x| x > 0)
            .collect::<Vec<u32>>();

        // bytes[208..336] 是 bad_block_list 的 128 字节填充,直接跳过

        // let card_type = bytes[336];
        // let card_flags = bytes[337];

        // bytes[338..340] 是最后 2 字节填充

        Ok(Self {
            // magic,
            // version,
            page_size,
            pages_per_cluster,
            // pages_per_block,
            clusters_per_card,
            alloc_offset,
            // alloc_end,
            rootdir_cluster,
            ifc_list,
            // card_type,
            // card_flags,
            spare_size: (page_size / 128) * 4,
            raw_page_size: page_size + (page_size / 128) * 4,
            cluster_size: page_size * pages_per_cluster,
            fat_per_cluster: (page_size * pages_per_cluster) / 4,
        })
    }
}