commit:b58c964a578a4e3eff12e74b0cd9ac3ddb8eefc6
author:Chip
committer:Chip
date:Fri Jun 7 23:18:28 2024 -0500
parents:
Maybe works?
diff --git a/.gitignore b/.gitignore
line changes: +2/-0
index 0000000..4fffb2f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+/target
+/Cargo.lock

diff --git a/Cargo.toml b/Cargo.toml
line changes: +12/-0
index 0000000..86d2ebe
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,12 @@
+[package]
+name = "omflib"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+clap = { version = "4.5.6", features = ["derive"] }
+num-derive = "0.4.2"
+num-traits = "0.2.19"
+pretty-hex = "0.4.1"

diff --git a/src/bin/omfdump/main.rs b/src/bin/omfdump/main.rs
line changes: +19/-0
index 0000000..cd8a18a
--- /dev/null
+++ b/src/bin/omfdump/main.rs
@@ -0,0 +1,19 @@
+use std::{fs, path::PathBuf, process::ExitCode};
+
+use clap::Parser;
+use omflib::OmfReader;
+
+#[derive(Parser, Debug)]
+struct Args {
+    file: PathBuf,
+}
+
+pub fn main() -> ExitCode {
+    let args = Args::parse();
+    let mut f = fs::File::open(args.file).expect("Could not open input file");
+    let reader = OmfReader::new(&mut f);
+    for section in reader {
+        println!("{}", section);
+    }
+    ExitCode::SUCCESS
+}

diff --git a/src/error.rs b/src/error.rs
line changes: +20/-0
index 0000000..d965ce2
--- /dev/null
+++ b/src/error.rs
@@ -0,0 +1,20 @@
+use std::{io, string::FromUtf8Error};
+
+#[derive(Debug)]
+pub enum OmfError {
+    Io(io::Error),
+    Utf(FromUtf8Error),
+    Value(&'static str),
+}
+
+impl From<io::Error> for OmfError {
+    fn from(value: io::Error) -> Self {
+        OmfError::Io(value)
+    }
+}
+
+impl From<FromUtf8Error> for OmfError {
+    fn from(value: FromUtf8Error) -> Self {
+        OmfError::Utf(value)
+    }
+} 
\ No newline at end of file

diff --git a/src/lib.rs b/src/lib.rs
line changes: +273/-0
index 0000000..b7b751e
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,273 @@
+mod error;
+mod record;
+
+use std::cell::RefCell;
+use std::io::{self, Read};
+use std::rc::Rc;
+
+use error::OmfError;
+use num_traits::FromPrimitive;
+use record::{
+    AbsoluteSegmentAddress, CommentType, ExtName, GroupComponent, MAttrStart, OmfRecord,
+    OmfRecordData, PubName, SegmentAlignment, SegmentAttributes,
+};
+
+#[derive(Debug, Clone)]
+pub struct SegmentInfo {
+    pub segment_attributes: SegmentAttributes,
+    pub segment_length: u16,
+    pub segment_name_index: u8,
+    pub class_name_index: u8,
+    pub overlay_name_index: u8,
+}
+
+#[derive(Debug, Clone)]
+pub struct GroupInfo {
+    pub group_name_index: u8,
+    pub segment_definitions: Vec<GroupComponent>,
+}
+
+#[derive(Debug)]
+pub struct OmfInfo {
+    pub names: Vec<String>,
+    pub segments: Vec<SegmentInfo>,
+    pub groups: Vec<GroupInfo>,
+}
+
+impl OmfInfo {
+    pub fn new() -> OmfInfo {
+        OmfInfo {
+            names: vec![],
+            segments: vec![],
+            groups: vec![],
+        }
+    }
+}
+
+pub struct OmfReader<'a> {
+    r: &'a mut dyn Read,
+    info: Rc<RefCell<OmfInfo>>,
+}
+
+impl<'a> OmfReader<'a> {
+    pub fn new(r: &'a mut dyn Read) -> OmfReader<'a> {
+        OmfReader {
+            r,
+            info: Rc::new(RefCell::new(OmfInfo::new())),
+        }
+    }
+
+    fn read_u8(&mut self) -> Result<u8, io::Error> {
+        let mut buf = [0u8; 1];
+        self.r.read_exact(&mut buf)?;
+        Ok(buf[0])
+    }
+
+    fn read_u16(&mut self) -> Result<u16, io::Error> {
+        let mut buf = [0u8; 2];
+        self.r.read_exact(&mut buf)?;
+        Ok(u16::from_le_bytes(buf))
+    }
+
+    fn read_bytes(&mut self, len: usize) -> Result<Vec<u8>, io::Error> {
+        let mut buf = Vec::new();
+        buf.resize(len, 0u8);
+        self.r.read_exact(&mut buf)?;
+        Ok(buf)
+    }
+
+    fn read_string(&mut self) -> Result<String, OmfError> {
+        let len = self.read_u8()? as usize;
+        let str_vec = self.read_bytes(len)?;
+        Ok(String::from_utf8(str_vec)?)
+    }
+
+    fn get_next_record(&mut self) -> Result<Option<OmfRecord>, OmfError> {
+        let record_type = match self.read_u8() {
+            Ok(v) => v,
+            Err(_) => return Ok(None),
+        };
+        let record_length = self.read_u16()? as usize;
+
+        let data = match record_type {
+            0x80 => {
+                let name = self.read_string()?;
+                OmfRecordData::THeadr { name }
+            }
+            0x88 => {
+                let tmp = self.read_u8()?;
+                let no_purge = tmp & 0x80 != 0;
+                let no_list = tmp & 0x80 != 0;
+                let comment_type = CommentType { no_purge, no_list };
+                let comment_class = self.read_u8()?;
+                let comment_bytes = self.read_bytes(record_length - 3)?;
+                OmfRecordData::Coment {
+                    comment_type,
+                    comment_class,
+                    comment_bytes,
+                }
+            }
+            0x8A => {
+                let module_type = self.read_u8()?;
+                let main = module_type & 0x80 != 0;
+                let start = if module_type & 0x40 != 0 {
+                    MAttrStart::Start {
+                        end_data: self.read_u8()?,
+                        frame_datum: self.read_u8()?,
+                        target_datum: self.read_u8()?,
+                        target_displacement: self.read_u16()?,
+                    }
+                } else {
+                    MAttrStart::NoStart
+                };
+                OmfRecordData::ModEnd { main, start }
+            }
+            0x8C => {
+                let mut names = vec![];
+                let mut c = 0;
+                while c < record_length - 1 {
+                    let name = self.read_string()?;
+                    let type_index = self.read_u8()?;
+                    c += name.len() + 2;
+                    names.push(ExtName { name, type_index });
+                }
+                OmfRecordData::ExtDef { names }
+            }
+            0x90 => {
+                let base_group_index = self.read_u8()?;
+                let base_segment_index = self.read_u8()?;
+                let base_frame = if base_segment_index == 0 {
+                    self.read_u16()?
+                } else {
+                    0u16
+                };
+                let mut names = vec![];
+                let mut c = 0;
+                let rep_len = record_length - 3 - if base_segment_index == 0 { 2 } else { 0 };
+                while c < rep_len {
+                    let name = self.read_string()?;
+                    let public_offset = self.read_u16()?;
+                    let type_index = self.read_u8()?;
+                    c += name.len() + 4;
+                    names.push(PubName {
+                        name,
+                        public_offset,
+                        type_index,
+                    });
+                }
+                OmfRecordData::PubDef {
+                    base_group_index,
+                    base_segment_index,
+                    base_frame,
+                    names,
+                }
+            }
+            0x96 => {
+                let mut names = vec![];
+                let mut c = 0;
+                while c < record_length - 1 {
+                    let name = self.read_string()?;
+                    c += name.len() + 1;
+                    names.push(name);
+                }
+                self.info.borrow_mut().names.append(&mut (names.clone()));
+                OmfRecordData::LNames { names }
+            }
+            0x98 => {
+                let tmp = self.read_u8()?;
+                let alignment =
+                    FromPrimitive::from_u8(tmp >> 5).ok_or(OmfError::Value("alignment"))?;
+                let combination =
+                    FromPrimitive::from_u8((tmp >> 2) & 3).ok_or(OmfError::Value("combination"))?;
+                let absolute_segment_address = if alignment == SegmentAlignment::AbsoluteSegment {
+                    let frame_number = self.read_u16()?;
+                    let offset = self.read_u8()?;
+                    Some(AbsoluteSegmentAddress {
+                        frame_number,
+                        offset,
+                    })
+                } else {
+                    None
+                };
+                let segment_attributes = SegmentAttributes {
+                    alignment,
+                    combination,
+                    big: tmp & 2 != 0,
+                    bd32bit: tmp & 1 != 0,
+                    absolute_segment_address,
+                };
+                let segment_length = self.read_u16()?;
+                let segment_name_index = self.read_u8()?;
+                let class_name_index = self.read_u8()?;
+                let overlay_name_index = self.read_u8()?;
+                self.info.borrow_mut().segments.push(SegmentInfo {
+                    segment_attributes: segment_attributes,
+                    segment_length,
+                    segment_name_index,
+                    class_name_index,
+                    overlay_name_index,
+                });
+                OmfRecordData::SegDef {
+                    segment_attributes,
+                    segment_length,
+                    segment_name_index,
+                    class_name_index,
+                    overlay_name_index,
+                }
+            }
+            0x9A => {
+                let group_name_index = self.read_u8()?;
+                let mut segment_definitions = vec![];
+                let mut c = 0;
+                while c < record_length - 2 {
+                    let index = self.read_u8()?;
+                    let segment_definition = self.read_u8()?;
+                    c += 2;
+                    segment_definitions.push(GroupComponent {
+                        index,
+                        segment_definition,
+                    });
+                }
+                self.info.borrow_mut().groups.push(GroupInfo {
+                    group_name_index,
+                    segment_definitions: segment_definitions.clone(),
+                });
+                OmfRecordData::GrpDef {
+                    group_name_index,
+                    segment_definitions,
+                }
+            }
+            0xA0 => {
+                let segment_index = self.read_u8()?;
+                let enumerated_data_offset = self.read_u16()?;
+                let data = self.read_bytes(record_length - 4)?;
+                OmfRecordData::LEData {
+                    segment_index,
+                    enumerated_data_offset,
+                    data,
+                }
+            }
+            _ => {
+                let data = self.read_bytes(record_length - 1)?;
+                OmfRecordData::Unknown { data }
+            }
+        };
+        let checksum = self.read_u8()?;
+
+        Ok(Some(OmfRecord::new(
+            record_type,
+            record_length,
+            data,
+            checksum,
+            Rc::clone(&self.info),
+        )))
+    }
+}
+
+impl<'a> Iterator for OmfReader<'a> {
+    type Item = OmfRecord;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        self.get_next_record().expect("next")
+    }
+}

diff --git a/src/record.rs b/src/record.rs
line changes: +422/-0
index 0000000..4403a50
--- /dev/null
+++ b/src/record.rs
@@ -0,0 +1,422 @@
+use std::{cell::RefCell, fmt::Display, rc::Rc};
+
+use num_derive::FromPrimitive;
+use pretty_hex::{HexConfig, PrettyHex};
+
+use crate::{error::OmfError, GroupInfo, OmfInfo, SegmentInfo};
+
+#[derive(Debug)]
+pub struct CommentType {
+    pub no_purge: bool,
+    pub no_list: bool,
+}
+
+#[derive(Debug)]
+pub enum MAttrStart {
+    NoStart,
+    Start {
+        end_data: u8,
+        frame_datum: u8,
+        target_datum: u8,
+        target_displacement: u16,
+    },
+}
+
+#[derive(Debug)]
+pub struct PubName {
+    pub name: String,
+    pub public_offset: u16,
+    pub type_index: u8,
+}
+
+#[derive(Debug)]
+pub struct ExtName {
+    pub name: String,
+    pub type_index: u8,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, FromPrimitive)]
+pub enum SegmentAlignment {
+    AbsoluteSegment = 0,
+    RelocatableByteAligned = 1,
+    RelocatableWordAligned = 2,
+    RelocatableParagraphAligned = 3,
+    RelocatablePageAligned = 4,
+    RelocatableDWordAligned = 5,
+}
+
+impl Display for SegmentAlignment {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            SegmentAlignment::AbsoluteSegment => write!(f, "absolute segment"),
+            SegmentAlignment::RelocatableByteAligned => write!(f, "relocatable, byte aligned"),
+            SegmentAlignment::RelocatableWordAligned => write!(f, "relocatable, word aligned"),
+            SegmentAlignment::RelocatableParagraphAligned => {
+                write!(f, "relocatable, paragraph aligned")
+            }
+            SegmentAlignment::RelocatablePageAligned => write!(f, "relocatable, page aligned"),
+            SegmentAlignment::RelocatableDWordAligned => {
+                write!(f, "relocatable, double word aligned")
+            }
+        }
+    }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, FromPrimitive)]
+pub enum SegmentCombination {
+    Private = 0,
+    Public = 2,
+    Public2 = 4,
+    Stack = 5,
+    Common = 6,
+    Public3 = 7,
+}
+
+impl Display for SegmentCombination {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            SegmentCombination::Private => write!(f, "private"),
+            SegmentCombination::Public => write!(f, "public"),
+            SegmentCombination::Public2 => write!(f, "public"),
+            SegmentCombination::Stack => write!(f, "stack"),
+            SegmentCombination::Common => write!(f, "common"),
+            SegmentCombination::Public3 => write!(f, "public"),
+        }
+    }
+}
+
+#[derive(Debug, Clone, Copy)]
+pub struct AbsoluteSegmentAddress {
+    pub frame_number: u16,
+    pub offset: u8,
+}
+
+#[derive(Debug, Clone, Copy)]
+pub struct SegmentAttributes {
+    pub alignment: SegmentAlignment,
+    pub combination: SegmentCombination,
+    pub big: bool,
+    pub bd32bit: bool,
+    pub absolute_segment_address: Option<AbsoluteSegmentAddress>,
+}
+
+#[derive(Debug, Clone, Copy)]
+pub struct GroupComponent {
+    pub index: u8,
+    pub segment_definition: u8,
+}
+
+#[derive(Debug)]
+pub struct OmfRecord {
+    pub record_type: u8,
+    pub record_length: usize,
+    pub data: OmfRecordData,
+    pub checksum: u8,
+    info: Rc<RefCell<OmfInfo>>,
+}
+
+impl OmfRecord {
+    pub fn new(
+        record_type: u8,
+        record_length: usize,
+        data: OmfRecordData,
+        checksum: u8,
+        info: Rc<RefCell<OmfInfo>>,
+    ) -> OmfRecord {
+        OmfRecord {
+            record_type,
+            record_length,
+            data,
+            checksum,
+            info,
+        }
+    }
+
+    pub fn name_from_index(&self, index: u8) -> Result<String, OmfError> {
+        let i = (index as usize) - 1;
+        let info = self.info.borrow();
+        let s = info
+            .names
+            .get(i)
+            .ok_or_else(|| OmfError::Value("name index not found"))?;
+        Ok(s.clone())
+    }
+
+    pub fn get_segment(&self, index: u8) -> Result<SegmentInfo, OmfError> {
+        let i = (index as usize) - 1;
+        let info = self.info.borrow();
+        let s = info
+            .segments
+            .get(i)
+            .ok_or_else(|| OmfError::Value("segment index not found"))?;
+        Ok(s.clone())
+    }
+
+    pub fn get_group(&self, index: u8) -> Result<GroupInfo, OmfError> {
+        let i = (index as usize) - 1;
+        let info = self.info.borrow();
+        let s = info
+            .groups
+            .get(i)
+            .ok_or_else(|| OmfError::Value("group index not found"))?;
+        Ok(s.clone())
+    }
+}
+
+impl Display for OmfRecord {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        let cfg = HexConfig {
+            group: 8,
+            ..HexConfig::default()
+        };
+
+        writeln!(
+            f,
+            "Record type {:02X}h length {}",
+            self.record_type, self.record_length
+        )?;
+
+        match &self.data {
+            OmfRecordData::THeadr { name } => {
+                writeln!(f, "Translator Header:")?;
+                writeln!(f, "    Name: {name}")
+            }
+            OmfRecordData::Coment {
+                comment_type,
+                comment_class,
+                comment_bytes,
+            } => {
+                writeln!(
+                    f,
+                    "Comment - {}{}class {:02X}",
+                    if comment_type.no_purge {
+                        "no purge "
+                    } else {
+                        ""
+                    },
+                    if comment_type.no_list { "no list " } else { "" },
+                    comment_class
+                )?;
+                writeln!(f, "{:?}", comment_bytes.hex_conf(cfg))
+            }
+            OmfRecordData::ModEnd { main, start } => {
+                writeln!(f, "Module End{}", if *main { " (MAIN)" } else { "" })?;
+                match start {
+                    MAttrStart::NoStart => (),
+                    MAttrStart::Start {
+                        end_data,
+                        frame_datum,
+                        target_datum,
+                        target_displacement,
+                    } => {
+                        writeln!(f, "    End data: {end_data:02X}, frame: {frame_datum:02X}, target: {target_datum:02X}, displacement: {target_displacement:04X}")?;
+                    }
+                }
+                Ok(())
+            }
+            OmfRecordData::ExtDef { names } => {
+                writeln!(f, "External Names Definition")?;
+                for (i, n) in names.iter().enumerate() {
+                    writeln!(f, "    {i:<4} {} type {}", n.name, n.type_index)?;
+                }
+                Ok(())
+            }
+            OmfRecordData::PubDef {
+                base_group_index,
+                base_segment_index,
+                base_frame,
+                names,
+            } => {
+                writeln!(f, "Public Names Definition")?;
+                if *base_group_index == 0 && *base_segment_index == 0 {
+                    writeln!(f, "    Base Frame: {base_frame:04X}")?;
+                } else {
+                    if *base_group_index == 0 {
+                        writeln!(f, "    Base Group: None")?;
+                    } else {
+                        let base_group = self.get_group(*base_group_index).expect("group index");
+                        writeln!(
+                            f,
+                            "    Base Group: {} ({})",
+                            self.name_from_index(base_group.group_name_index)
+                                .expect("name index"),
+                            base_group_index
+                        )?;
+                    }
+                    let base_segment = self
+                        .get_segment(*base_segment_index)
+                        .expect("segment index");
+                    writeln!(
+                        f,
+                        "    Base Segment: {} ({})",
+                        self.name_from_index(base_segment.segment_name_index)
+                            .expect("name lookup"),
+                        base_segment_index
+                    )?;
+                }
+                writeln!(f, "    Names:")?;
+                for n in names {
+                    writeln!(
+                        f,
+                        "        {} offset {:04X} type {}",
+                        n.name, n.public_offset, n.type_index
+                    )?;
+                }
+                Ok(())
+            }
+            OmfRecordData::LNames { names } => {
+                writeln!(f, "List of Names")?;
+                for (i, n) in names.iter().enumerate() {
+                    writeln!(f, "    {:<4} {}", i + 1, n)?;
+                }
+                Ok(())
+            }
+            OmfRecordData::SegDef {
+                segment_attributes,
+                segment_length,
+                segment_name_index,
+                class_name_index,
+                overlay_name_index,
+            } => {
+                writeln!(
+                    f,
+                    "Segment Definition - {} ({})",
+                    self.name_from_index(*segment_name_index)
+                        .expect("name lookup"),
+                    *segment_name_index
+                )?;
+                writeln!(
+                    f,
+                    "    Attributes: {}; {} combination{}{}",
+                    segment_attributes.alignment,
+                    segment_attributes.combination,
+                    if segment_attributes.big { "; BIG" } else { "" },
+                    if segment_attributes.bd32bit {
+                        "; USE32"
+                    } else {
+                        ""
+                    }
+                )?;
+                writeln!(f, "    Segment length: {segment_length:04X}")?;
+                writeln!(
+                    f,
+                    "    Class name: {} ({})",
+                    self.name_from_index(*class_name_index)
+                        .expect("name lookup"),
+                    class_name_index
+                )?;
+                writeln!(
+                    f,
+                    "    Overlay name: {} ({})",
+                    self.name_from_index(*overlay_name_index)
+                        .expect("name lookup"),
+                    overlay_name_index
+                )?;
+                Ok(())
+            }
+            OmfRecordData::GrpDef {
+                group_name_index,
+                segment_definitions,
+            } => {
+                writeln!(
+                    f,
+                    "Group Definition - {} ({})",
+                    self.name_from_index(*group_name_index)
+                        .expect("name lookup"),
+                    group_name_index
+                )?;
+                writeln!(f, "    Segments:")?;
+                for (i, s) in segment_definitions.iter().enumerate() {
+                    let segment = self
+                        .get_segment(s.segment_definition)
+                        .expect("segment index");
+                    writeln!(
+                        f,
+                        "        {:<4} {} ({})",
+                        i,
+                        self.name_from_index(segment.segment_name_index)
+                            .expect("name lookup"),
+                        s.segment_definition
+                    )?;
+                }
+                Ok(())
+            }
+            OmfRecordData::LEData {
+                segment_index,
+                enumerated_data_offset,
+                data,
+            } => {
+                let segment = self.get_segment(*segment_index).expect("segment index");
+                writeln!(
+                    f,
+                    "Logical Enumerated Data - {} ({}) offset {:04X}h",
+                    self.name_from_index(segment.segment_name_index)
+                        .expect("name lookup"),
+                    segment_index,
+                    enumerated_data_offset
+                )?;
+                writeln!(f, "{:?}", data.hex_conf(cfg))
+            }
+            OmfRecordData::Unknown { data } => {
+                writeln!(f, "Unknown Data")?;
+                writeln!(f, "{:?}", data.hex_conf(cfg))
+            }
+        }
+    }
+}
+
+#[derive(Debug)]
+pub enum OmfRecordData {
+    THeadr {
+        // 80
+        name: String,
+    },
+    Coment {
+        // 88
+        comment_type: CommentType,
+        comment_class: u8,
+        comment_bytes: Vec<u8>,
+    },
+    ModEnd {
+        // 8A
+        main: bool,
+        start: MAttrStart,
+    },
+    ExtDef {
+        // 8C
+        names: Vec<ExtName>,
+    },
+    PubDef {
+        // 90
+        base_group_index: u8,
+        base_segment_index: u8,
+        base_frame: u16,
+        names: Vec<PubName>,
+    },
+    LNames {
+        // 96
+        names: Vec<String>,
+    },
+    SegDef {
+        // 98
+        segment_attributes: SegmentAttributes,
+        segment_length: u16,
+        segment_name_index: u8,
+        class_name_index: u8,
+        overlay_name_index: u8,
+    },
+    GrpDef {
+        // 9A
+        group_name_index: u8,
+        segment_definitions: Vec<GroupComponent>,
+    },
+    LEData {
+        // A0
+        segment_index: u8,
+        enumerated_data_offset: u16,
+        data: Vec<u8>,
+    },
+    Unknown {
+        data: Vec<u8>,
+    },
+}