+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")
+ }
+}
+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>,
+ },
+}