redguard-preservation 0.6.1

A CLI tool to preserve Redguard files
Documentation
use super::{Error, MpobRecord, MpsRecord, Result, RgmSection, RgmSectionHeader};
use std::fmt::Write;

impl RgmSectionHeader {
    #[must_use]
    pub fn name(&self) -> String {
        String::from_utf8_lossy(&self.name).to_string()
    }
}

impl MpsRecord {
    #[must_use]
    pub fn model_name(&self) -> String {
        String::from_utf8_lossy(&self.model_name)
            .trim_matches('\0')
            .to_string()
    }
}

impl MpobRecord {
    #[must_use]
    pub fn model_name(&self) -> String {
        String::from_utf8_lossy(&self.model_name)
            .trim_matches('\0')
            .to_string()
    }

    #[must_use]
    pub fn script_name(&self) -> String {
        String::from_utf8_lossy(&self.script_name)
            .trim_matches('\0')
            .to_string()
    }
}

impl RgmSection {
    #[must_use]
    pub const fn header(&self) -> &RgmSectionHeader {
        match self {
            Self::Rahd(header, _)
            | Self::Rafs(header, _)
            | Self::Rast(header, _)
            | Self::Rasb(header, _)
            | Self::Rava(header, _)
            | Self::Rasc(header, _)
            | Self::Rahk(header, _)
            | Self::Ralc(header, _)
            | Self::Raex(header, _)
            | Self::RaexParsed(header, _)
            | Self::Raat(header, _)
            | Self::Raan(header, _)
            | Self::Ragr(header, _)
            | Self::Ranm(header, _)
            | Self::Ravc(header, _)
            | Self::RavcParsed(header, _)
            | Self::Mpob(header, _)
            | Self::MpobParsed(header, _)
            | Self::Mprp(header, _)
            | Self::MprpParsed(header, _)
            | Self::Mps(header, _)
            | Self::Mpl(header, _)
            | Self::MplParsed(header, _)
            | Self::Mpf(header, _)
            | Self::Mpm(header, _)
            | Self::Mpsz(header, _)
            | Self::Wdnm(header, _)
            | Self::Flat(header, _)
            | Self::End(header) => header,
        }
    }
}

pub(super) fn read_i32_le(bytes: &[u8], offset: usize) -> Option<i32> {
    let chunk: [u8; 4] = bytes.get(offset..offset + 4)?.try_into().ok()?;
    Some(i32::from_le_bytes(chunk))
}

pub(super) fn read_i16_le(bytes: &[u8], offset: usize) -> Option<i16> {
    let chunk: [u8; 2] = bytes.get(offset..offset + 2)?.try_into().ok()?;
    Some(i16::from_le_bytes(chunk))
}

pub(super) fn read_script_name_9(bytes: &[u8], offset: usize) -> Option<String> {
    let raw = bytes.get(offset..offset + 9)?;
    Some(String::from_utf8_lossy(raw).trim_matches('\0').to_string())
}

pub(super) fn dump_rgm_impl(input: &[u8]) -> Result<String> {
    let (_, rgm_file) = super::parser::parse_rgm_file(input)
        .map_err(|e| Error::Parse(format!("Failed to parse RGM file: {e}")))?;

    let mut output = String::new();
    output.push_str("RGM File Structure:\n");
    output.push_str("==================\n\n");
    let _ = writeln!(output, "Found {} sections", rgm_file.sections.len());
    for (section_index, section) in rgm_file.sections.iter().enumerate() {
        let _ = writeln!(output, "Section {}: {:?}", section_index + 1, section);
        match section {
            RgmSection::Mps(_, mps_records) => {
                let _ = writeln!(output, "  MPSO ({} records)", mps_records.len());
                for (record_index, record) in mps_records.iter().enumerate() {
                    if record_index < 5 {
                        let _ = writeln!(
                            output,
                            "    Record {}: Model='{}'",
                            record_index + 1,
                            record.model_name()
                        );
                    } else if record_index == 5 {
                        let _ =
                            writeln!(output, "    ... and {} more records", mps_records.len() - 5);
                        break;
                    }
                }
            }
            RgmSection::End(header) => {
                let _ = writeln!(output, "  END ({})", header.name());
            }
            _ => {}
        }
    }

    Ok(output)
}