xo65 0.1.0

Library for parsing cc65 object files (xo65 format)
Documentation
use crate::expr::Expr;
use crate::parse::{
    parse_bail, parse_bytes_at, parse_uleb128_at, ParseAt, ParseErrorContext as _, ParseResult,
};
use crate::xo65::Xo65Bytes;

#[derive(Debug)]
pub struct SectionTable<'data> {
    bytes: Xo65Bytes<'data>,

    sections: Box<[Section<'data>]>,
}

impl<'data> SectionTable<'data> {
    pub fn as_bytes(&self) -> &'data [u8] {
        self.bytes.as_inner()
    }

    pub fn count(&self) -> usize {
        self.sections.len()
    }

    pub fn get(&self, idx: usize) -> Option<&Section<'data>> {
        self.sections.get(idx)
    }

    pub fn iter(
        &self,
    ) -> impl DoubleEndedIterator<Item = &Section<'data>>
           + ExactSizeIterator
           + std::iter::FusedIterator
           + Clone {
        self.sections.iter()
    }

    pub(crate) fn parse(bytes: Xo65Bytes<'data>) -> ParseResult<Self> {
        let mut off = 0;

        let count = parse_uleb128_at(&bytes, &mut off)? as usize;
        let mut sections = Vec::<Section>::with_capacity(count);

        for i in 0..count {
            let section = Section::parse_at(&bytes, &mut off)
                .with_context(|| format!("section {i} parse error"))?;
            sections.push(section);
        }

        Ok(Self {
            bytes,
            sections: sections.into(),
        })
    }
}

#[derive(Debug)]
pub struct Section<'data> {
    bytes: Xo65Bytes<'data>,

    seg_name: u32,
    flags: u32,
    len: u32,
    align: u32,
    addr_size: u8,
    fragment_count: u32,
    fragments: Box<[SectionFragment<'data>]>,
}

impl<'data> Section<'data> {
    pub fn as_bytes(&self) -> &'data [u8] {
        self.bytes.as_inner()
    }

    pub fn segment_name(&self) -> u32 {
        self.seg_name
    }

    pub fn flags(&self) -> u32 {
        self.flags
    }

    /// アドレス空間内に占めるサイズを返す。
    ///
    /// NOTE: 実際にファイルへ出力されるサイズとは必ずしも一致しない (BSS セグメントの場合があるので)。
    pub fn len(&self) -> u32 {
        self.len
    }

    /// アドレス空間内に占めるサイズが 0 かどうかを返す。
    pub fn is_empty(&self) -> bool {
        self.len == 0
    }

    pub fn align(&self) -> u32 {
        self.align
    }

    pub fn addr_size(&self) -> u8 {
        self.addr_size
    }

    pub fn fragment_count(&self) -> u32 {
        self.fragment_count
    }

    pub fn fragments(&self) -> &[SectionFragment<'data>] {
        &self.fragments
    }
}

impl<'data> ParseAt<'data> for Section<'data> {
    fn parse_at(bytes: &Xo65Bytes<'data>, off: &mut usize) -> ParseResult<Self> {
        let total_len = u32::parse_at(bytes, off)?;
        let bytes = bytes.get(..*off + total_len as usize)?;

        let name = parse_uleb128_at(&bytes, off)?;
        let flags = parse_uleb128_at(&bytes, off)?;
        let len = parse_uleb128_at(&bytes, off)?;
        let align = parse_uleb128_at(&bytes, off)?;
        let addr_size = u8::parse_at(&bytes, off)?;
        let fragment_count = parse_uleb128_at(&bytes, off)?;

        let mut fragments = Vec::<SectionFragment>::with_capacity(fragment_count as usize);

        for i in 0..fragment_count {
            let fragment = SectionFragment::parse_at(&bytes, off)
                .with_context(|| format!("fragment {i} parse error"))?;
            fragments.push(fragment);
        }

        Ok(Section {
            bytes,
            seg_name: name,
            flags,
            len,
            align,
            addr_size,
            fragment_count,
            fragments: fragments.into(),
        })
    }
}

#[derive(Debug)]
pub struct SectionFragment<'data> {
    body: SectionFragmentBody<'data>,
    lines: Box<[u32]>,
}

impl<'data> SectionFragment<'data> {
    pub fn body(&self) -> &SectionFragmentBody<'data> {
        &self.body
    }

    pub fn lines(&self) -> &[u32] {
        &self.lines
    }
}

impl<'data> ParseAt<'data> for SectionFragment<'data> {
    fn parse_at(bytes: &Xo65Bytes<'data>, off: &mut usize) -> ParseResult<Self> {
        let body = SectionFragmentBody::parse_at(bytes, off)?;
        let lines = parse_lines_at(bytes, off)?;

        Ok(Self { body, lines })
    }
}

#[derive(Debug)]
pub enum SectionFragmentBody<'data> {
    Literal(&'data [u8]),
    ExprU8(Expr),
    ExprU16(Expr),
    ExprU24(Expr),
    ExprU32(Expr),
    ExprI8(Expr),
    ExprI16(Expr),
    ExprI24(Expr),
    ExprI32(Expr),
    Fill(u32),
}

impl<'data> ParseAt<'data> for SectionFragmentBody<'data> {
    fn parse_at(bytes: &Xo65Bytes<'data>, off: &mut usize) -> ParseResult<Self> {
        let ty = u8::parse_at(bytes, off)?;

        let fragment = match ty & 0x38 {
            0x00 => parse_frag_body_lit_at(bytes, off)?,
            0x08 => parse_frag_body_expru_at(bytes, off, ty & 7)?,
            0x10 => parse_frag_body_expri_at(bytes, off, ty & 7)?,
            0x20 => parse_frag_body_fill_at(bytes, off)?,
            _ => parse_bail!("invalid fragment type: {ty:#X}"),
        };

        Ok(fragment)
    }
}

fn parse_frag_body_lit_at<'data>(
    bytes: &Xo65Bytes<'data>,
    off: &mut usize,
) -> ParseResult<SectionFragmentBody<'data>> {
    let len = parse_uleb128_at(bytes, off)? as usize;
    let lit = parse_bytes_at(bytes, off, len)?;

    Ok(SectionFragmentBody::Literal(lit))
}

fn parse_frag_body_expru_at<'data>(
    bytes: &Xo65Bytes<'data>,
    off: &mut usize,
    size: u8,
) -> ParseResult<SectionFragmentBody<'data>> {
    let expr = Expr::parse_at(bytes, off)?;

    let fragment = match size {
        1 => SectionFragmentBody::ExprU8(expr),
        2 => SectionFragmentBody::ExprU16(expr),
        3 => SectionFragmentBody::ExprU24(expr),
        4 => SectionFragmentBody::ExprU32(expr),
        _ => parse_bail!("invalid unsigned expr size: {size}"),
    };

    Ok(fragment)
}

fn parse_frag_body_expri_at<'data>(
    bytes: &Xo65Bytes<'data>,
    off: &mut usize,
    size: u8,
) -> ParseResult<SectionFragmentBody<'data>> {
    let expr = Expr::parse_at(bytes, off)?;

    let fragment = match size {
        1 => SectionFragmentBody::ExprI8(expr),
        2 => SectionFragmentBody::ExprI16(expr),
        3 => SectionFragmentBody::ExprI24(expr),
        4 => SectionFragmentBody::ExprI32(expr),
        _ => parse_bail!("invalid signed expr size: {size}"),
    };

    Ok(fragment)
}

fn parse_frag_body_fill_at<'data>(
    bytes: &Xo65Bytes<'data>,
    off: &mut usize,
) -> ParseResult<SectionFragmentBody<'data>> {
    let len = parse_uleb128_at(bytes, off)?;

    Ok(SectionFragmentBody::Fill(len))
}

fn parse_lines_at(bytes: &Xo65Bytes<'_>, off: &mut usize) -> ParseResult<Box<[u32]>> {
    let count = parse_uleb128_at(bytes, off)? as usize;
    let mut lines = Vec::<u32>::with_capacity(count);

    for _ in 0..count {
        let line = parse_uleb128_at(bytes, off)?;
        lines.push(line);
    }

    Ok(lines.into())
}