diff --git a/src/cuex.rs b/src/cuex.rs new file mode 100644 index 0000000..bbe359d --- /dev/null +++ b/src/cuex.rs @@ -0,0 +1,84 @@ +use std::fmt; + + +#[derive(Debug)] +pub struct NrgCuex { + pub size: u32, + pub tracks: Vec, +} + +impl NrgCuex { + pub fn new() -> NrgCuex { + NrgCuex { + size: 0, + tracks: Vec::new(), + } + } +} + +impl fmt::Display for NrgCuex { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + try!(write!(f, "Chunk ID: CUEX\n\ + Chunk description: Cue Sheet\n\ + Chunk size: {} Bytes", self.size)); + if self.tracks.is_empty() { + try!(write!(f, "\nNo CUEX tracks!")); + } else { + for track in &self.tracks { + try!(write!(f, "\n\ + Track:\n\ + {}", track)); + } + } + Ok(()) + } +} + + +#[derive(Debug)] +pub struct NrgCuexTrack { + pub mode: u8, + pub track_number: u8, + pub index_number: u8, + pub padding: u8, + pub position_sectors: i32, +} + +impl NrgCuexTrack { + pub fn new() -> NrgCuexTrack { + NrgCuexTrack { + mode: 0, + track_number: 0, + index_number: 0, + padding: 0, + position_sectors: 0, + } + } +} + +impl fmt::Display for NrgCuexTrack { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + try!(writeln!(f, "\tMode: 0x{:02X}", self.mode)); + + try!(write!(f, "\tTrack number: ")); + if self.track_number == 0 { + try!(writeln!(f, "0 (lead-in area)")); + } else if self.track_number == 0xAA { + try!(writeln!(f, "0xAA (lead-out area)")); + } else { + try!(writeln!(f, "{}", self.track_number)); + } + + try!(writeln!(f, "\tIndex number: {}", self.index_number)); + + if self.padding != 0 { + try!(writeln!(f, "\tPadding: {} (Warning: should be 0!)", + self.padding)); + } + + // Audio CDs are played at a 75 sectors per second rate: + let position_seconds: f64 = (self.position_sectors as f64) / 75.0; + write!(f, "\tPosition: {} sectors ({:.2} seconds)", + self.position_sectors, position_seconds) + } +} diff --git a/src/daox.rs b/src/daox.rs new file mode 100644 index 0000000..d0abf4f --- /dev/null +++ b/src/daox.rs @@ -0,0 +1,118 @@ +use std::fmt; + + +#[derive(Debug)] +pub struct NrgDaox { + pub size: u32, + pub size2: u32, + pub upc: String, + pub padding: u8, + pub toc_type: u16, + pub first_track: u8, + pub last_track: u8, + pub tracks: Vec, +} + +impl NrgDaox { + pub fn new() -> NrgDaox { + NrgDaox { + size: 0, + size2: 0, + upc: String::new(), + padding: 0, + toc_type: 0, + first_track: 0, + last_track: 0, + tracks: Vec::new(), + } + } +} + +impl fmt::Display for NrgDaox { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + try!(writeln!(f, "Chunk ID: DAOX\n\ + Chunk description: DAO (Disc At Once) Information\n\ + Chunk size: {} Bytes\n\ + Chunk size 2: {}\n\ + UPC: \"{}\"", + self.size, + self.size2, + self.upc)); + + if self.padding != 0 { + try!(writeln!(f, "Padding: {} (Warning: should be 0!)", + self.padding)); + } + + try!(write!(f, "TOC type: 0x{:04X}\n\ + First track in the session: {}\n\ + Last track in the session: {}", + self.toc_type, + self.first_track, + self.last_track)); + + if self.tracks.is_empty() { + try!(write!(f, "\nNo DAOX tracks!")); + } else { + let mut i = 1; + for track in &self.tracks { + try!(write!(f, "\n\ + Track {:02}:\n\ + {}", i, track)); + i += 1; + } + } + + Ok(()) + } +} + + +#[derive(Debug)] +pub struct NrgDaoxTrack { + pub isrc: String, + pub sector_size: u16, + pub data_mode: u16, + pub unknown: u16, + pub index0: u64, + pub index1: u64, + pub track_end: u64, +} + +impl NrgDaoxTrack { + pub fn new() -> NrgDaoxTrack { + NrgDaoxTrack { + isrc: String::new(), + sector_size: 0, + data_mode: 0, + unknown: 0, + index0: 0, + index1: 0, + track_end: 0, + } + } +} + +impl fmt::Display for NrgDaoxTrack { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + try!(writeln!(f, "\tISRC: \"{}\"\n\ + \tSector size in the image file: {} Bytes\n\ + \tMode of the data in the image file: 0x{:04X}", + self.isrc, + self.sector_size, + self.data_mode)); + + if self.unknown != 0x0001 { + try!(writeln!(f, "\tUnknown field: 0x{:04X} \ + (Warning: should be 0x0001!)", + self.unknown)); + } + + write!(f, "\tIndex0 (Pre-gap): {} Bytes\n\ + \tIndex1 (Start of track): {} Bytes\n\ + \tEnd of track + 1: {} Bytes", + self.index0, + self.index1, + self.track_end) + } +} diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..d03687f --- /dev/null +++ b/src/error.rs @@ -0,0 +1,49 @@ +use std::error::Error; +use std::fmt; +use std::io; + + +#[derive(Debug)] +pub enum NrgError { + Io(io::Error), + NrgFormat, + NrgFormatV1, + NrgChunkId, +} + +impl fmt::Display for NrgError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + NrgError::Io(ref err) => err.fmt(f), + NrgError::NrgFormat => write!(f, "NRG format unknown."), + NrgError::NrgFormatV1 => write!(f, "NRG v1 format is not handled."), + NrgError::NrgChunkId => write!(f, "NRG chunk ID unknown."), + } + } +} + +impl Error for NrgError { + fn description(&self) -> &str { + match *self { + NrgError::Io(ref err) => err.description(), + NrgError::NrgFormat => "NRG format", + NrgError::NrgFormatV1 => "NRG format v1", + NrgError::NrgChunkId => "NRG chunk ID", + } + } + + fn cause(&self) -> Option<&Error> { + match *self { + NrgError::Io(ref err) => Some(err), + NrgError::NrgFormat => None, + NrgError::NrgFormatV1 => None, + NrgError::NrgChunkId => None, + } + } +} + +impl From for NrgError { + fn from(err: io::Error) -> NrgError { + NrgError::Io(err) + } +} diff --git a/src/lib.rs b/src/lib.rs index 7a26a0f..e909d0e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,367 +1,26 @@ -use std::error::Error; -use std::fmt; use std::fs::File; -use std::io::{self, Read, Seek, SeekFrom}; -use std::mem; +use std::io::{Seek, SeekFrom}; +mod error; +use error::NrgError; -#[derive(Debug)] -pub enum NrgError { - Io(io::Error), - NrgFormat, - NrgFormatV1, - NrgChunkId, -} +mod metadata; +use metadata::NrgMetadata; -impl fmt::Display for NrgError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - NrgError::Io(ref err) => err.fmt(f), - NrgError::NrgFormat => write!(f, "NRG format unknown."), - NrgError::NrgFormatV1 => write!(f, "NRG v1 format is not handled."), - NrgError::NrgChunkId => write!(f, "NRG chunk ID unknown."), - } - } -} +mod cuex; +use cuex::{NrgCuex, NrgCuexTrack}; -impl Error for NrgError { - fn description(&self) -> &str { - match *self { - NrgError::Io(ref err) => err.description(), - NrgError::NrgFormat => "NRG format", - NrgError::NrgFormatV1 => "NRG format v1", - NrgError::NrgChunkId => "NRG chunk ID", - } - } +mod daox; +use daox::{NrgDaox, NrgDaoxTrack}; - fn cause(&self) -> Option<&Error> { - match *self { - NrgError::Io(ref err) => Some(err), - NrgError::NrgFormat => None, - NrgError::NrgFormatV1 => None, - NrgError::NrgChunkId => None, - } - } -} +mod sinf; +use sinf::{NrgSinf}; -impl From for NrgError { - fn from(err: io::Error) -> NrgError { - NrgError::Io(err) - } -} +mod mtyp; +use mtyp::{NrgMtyp}; - -#[derive(Debug)] -pub struct NrgMetadata { - pub file_size: u64, - pub nrg_version: u8, - pub chunk_offset: u64, - pub cuex_chunk: Option, - pub daox_chunk: Option, - pub sinf_chunk: Option, - pub mtyp_chunk: Option, -} - -impl NrgMetadata { - fn new() -> NrgMetadata { - NrgMetadata { - file_size: 0, - nrg_version: 0, - chunk_offset: 0, - cuex_chunk: None, - daox_chunk: None, - sinf_chunk: None, - mtyp_chunk: None, - } - } -} - -impl fmt::Display for NrgMetadata { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - try!(write!(f, "Image size: {} Bytes\n\ - NRG format version: {}\n\ - First chunk offset: {}", - self.file_size, - self.nrg_version, - self.chunk_offset, - )); - match self.cuex_chunk { - None => {}, - Some(ref chunk) => try!(write!(f, "\n\n\ - {}", chunk)), - } - match self.daox_chunk { - None => {}, - Some(ref chunk) => try!(write!(f, "\n\n\ - {}", chunk)), - } - match self.sinf_chunk { - None => {}, - Some(ref chunk) => try!(write!(f, "\n\n\ - {}", chunk)), - } - match self.mtyp_chunk { - None => {}, - Some(ref chunk) => try!(write!(f, "\n\n\ - {}", chunk)), - } - Ok(()) - } -} - - -#[derive(Debug)] -pub struct NrgCuex { - pub size: u32, - pub tracks: Vec, -} - -impl NrgCuex { - fn new() -> NrgCuex { - NrgCuex { - size: 0, - tracks: Vec::new(), - } - } -} - -impl fmt::Display for NrgCuex { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - try!(write!(f, "Chunk ID: CUEX\n\ - Chunk description: Cue Sheet\n\ - Chunk size: {} Bytes", self.size)); - if self.tracks.is_empty() { - try!(write!(f, "\nNo CUEX tracks!")); - } else { - for track in &self.tracks { - try!(write!(f, "\n\ - Track:\n\ - {}", track)); - } - } - Ok(()) - } -} - - -#[derive(Debug)] -pub struct NrgCuexTrack { - mode: u8, - track_number: u8, - index_number: u8, - padding: u8, - position_sectors: i32, -} - -impl NrgCuexTrack { - fn new() -> NrgCuexTrack { - NrgCuexTrack { - mode: 0, - track_number: 0, - index_number: 0, - padding: 0, - position_sectors: 0, - } - } -} - -impl fmt::Display for NrgCuexTrack { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - try!(writeln!(f, "\tMode: 0x{:02X}", self.mode)); - - try!(write!(f, "\tTrack number: ")); - if self.track_number == 0 { - try!(writeln!(f, "0 (lead-in area)")); - } else if self.track_number == 0xAA { - try!(writeln!(f, "0xAA (lead-out area)")); - } else { - try!(writeln!(f, "{}", self.track_number)); - } - - try!(writeln!(f, "\tIndex number: {}", self.index_number)); - - if self.padding != 0 { - try!(writeln!(f, "\tPadding: {} (Warning: should be 0!)", - self.padding)); - } - - // Audio CDs are played at a 75 sectors per second rate: - let position_seconds: f64 = (self.position_sectors as f64) / 75.0; - write!(f, "\tPosition: {} sectors ({:.2} seconds)", - self.position_sectors, position_seconds) - } -} - - -#[derive(Debug)] -pub struct NrgDaox { - pub size: u32, - pub size2: u32, - pub upc: String, - pub padding: u8, - pub toc_type: u16, - pub first_track: u8, - pub last_track: u8, - pub tracks: Vec, -} - -impl NrgDaox { - fn new() -> NrgDaox { - NrgDaox { - size: 0, - size2: 0, - upc: String::new(), - padding: 0, - toc_type: 0, - first_track: 0, - last_track: 0, - tracks: Vec::new(), - } - } -} - -impl fmt::Display for NrgDaox { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - try!(writeln!(f, "Chunk ID: DAOX\n\ - Chunk description: DAO (Disc At Once) Information\n\ - Chunk size: {} Bytes\n\ - Chunk size 2: {}\n\ - UPC: \"{}\"", - self.size, - self.size2, - self.upc)); - - if self.padding != 0 { - try!(writeln!(f, "Padding: {} (Warning: should be 0!)", - self.padding)); - } - - try!(write!(f, "TOC type: 0x{:04X}\n\ - First track in the session: {}\n\ - Last track in the session: {}", - self.toc_type, - self.first_track, - self.last_track)); - - if self.tracks.is_empty() { - try!(write!(f, "\nNo DAOX tracks!")); - } else { - let mut i = 1; - for track in &self.tracks { - try!(write!(f, "\n\ - Track {:02}:\n\ - {}", i, track)); - i += 1; - } - } - - Ok(()) - } -} - - -#[derive(Debug)] -pub struct NrgDaoxTrack { - isrc: String, - sector_size: u16, - data_mode: u16, - unknown: u16, - index0: u64, - index1: u64, - track_end: u64, -} - -impl NrgDaoxTrack { - fn new() -> NrgDaoxTrack { - NrgDaoxTrack { - isrc: String::new(), - sector_size: 0, - data_mode: 0, - unknown: 0, - index0: 0, - index1: 0, - track_end: 0, - } - } -} - -impl fmt::Display for NrgDaoxTrack { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - try!(writeln!(f, "\tISRC: \"{}\"\n\ - \tSector size in the image file: {} Bytes\n\ - \tMode of the data in the image file: 0x{:04X}", - self.isrc, - self.sector_size, - self.data_mode)); - - if self.unknown != 0x0001 { - try!(writeln!(f, "\tUnknown field: 0x{:04X} \ - (Warning: should be 0x0001!)", - self.unknown)); - } - - write!(f, "\tIndex0 (Pre-gap): {} Bytes\n\ - \tIndex1 (Start of track): {} Bytes\n\ - \tEnd of track + 1: {} Bytes", - self.index0, - self.index1, - self.track_end) - } -} - - -#[derive(Debug)] -pub struct NrgSinf { - pub size: u32, - pub nb_tracks: u32, -} - -impl NrgSinf { - fn new() -> NrgSinf { - NrgSinf { - size: 0, - nb_tracks: 0, - } - } -} - -impl fmt::Display for NrgSinf { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "Chunk ID: SINF\n\ - Chunk description: Session Information\n\ - Chunk size: {} Bytes\n\ - Number of tracks in the session: {}", - self.size, - self.nb_tracks) - } -} - - -#[derive(Debug)] -pub struct NrgMtyp { - pub size: u32, - pub unknown: u32, -} - -impl NrgMtyp { - fn new() -> NrgMtyp { - NrgMtyp { - size: 0, - unknown: 0, - } - } -} - -impl fmt::Display for NrgMtyp { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "Chunk ID: MTYP\n\ - Chunk description: Media Type (?)\n\ - Chunk size: {} Bytes\n\ - Unknown field: 0x{:04X}", - self.size, - self.unknown) - } -} +mod readers; +use readers::*; pub fn parse_nrg_metadata(img_name: String) -> Result { @@ -454,59 +113,6 @@ fn read_nrg_chunk_id(fd: &File) -> Result { } -/// Reads a String of size `size` from `fd`. -fn read_sized_string(fd: &File, size: u64) -> Result { - let mut handle = fd.take(size); - let mut string = String::new(); - try!(handle.read_to_string(&mut string)); - Ok(string) -} - - -/// Reads a 64-bit unsigned integer from `fd`. -fn read_u64(fd: &mut File) -> Result { - let mut buf = [0u8; 8]; - try!(fd.read_exact(&mut buf)); - let i: u64; - unsafe { - i = mem::transmute(buf); - } - Ok(u64::from_be(i)) -} - - -/// Reads a 32-bit unsigned integer from `fd`. -fn read_u32(fd: &mut File) -> Result { - let mut buf = [0u8; 4]; - try!(fd.read_exact(&mut buf)); - let i: u32; - unsafe { - i = mem::transmute(buf); - } - Ok(u32::from_be(i)) -} - - -/// Reads a 16-bit unsigned integer from `fd`. -fn read_u16(fd: &mut File) -> Result { - let mut buf = [0u8; 2]; - try!(fd.read_exact(&mut buf)); - let i: u16; - unsafe { - i = mem::transmute(buf); - } - Ok(u16::from_be(i)) -} - - -/// Reads an unsigned byte from `fd`. -fn read_u8(fd: &mut File) -> Result { - let mut buf = [0u8; 1]; - try!(fd.read_exact(&mut buf)); - Ok(buf[0]) -} - - fn skip_unhandled_chunk(fd: &mut File, chunk_id: &str) -> Result<(), NrgError> { let chunk_size = try!(read_u32(fd)); try!(fd.seek(SeekFrom::Current(chunk_size as i64))); diff --git a/src/metadata.rs b/src/metadata.rs new file mode 100644 index 0000000..c3922b0 --- /dev/null +++ b/src/metadata.rs @@ -0,0 +1,65 @@ +use std::fmt; + +use cuex::NrgCuex; +use daox::NrgDaox; +use sinf::NrgSinf; +use mtyp::NrgMtyp; + + +#[derive(Debug)] +pub struct NrgMetadata { + pub file_size: u64, + pub nrg_version: u8, + pub chunk_offset: u64, + pub cuex_chunk: Option, + pub daox_chunk: Option, + pub sinf_chunk: Option, + pub mtyp_chunk: Option, +} + +impl NrgMetadata { + pub fn new() -> NrgMetadata { + NrgMetadata { + file_size: 0, + nrg_version: 0, + chunk_offset: 0, + cuex_chunk: None, + daox_chunk: None, + sinf_chunk: None, + mtyp_chunk: None, + } + } +} + +impl fmt::Display for NrgMetadata { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + try!(write!(f, "Image size: {} Bytes\n\ + NRG format version: {}\n\ + First chunk offset: {}", + self.file_size, + self.nrg_version, + self.chunk_offset, + )); + match self.cuex_chunk { + None => {}, + Some(ref chunk) => try!(write!(f, "\n\n\ + {}", chunk)), + } + match self.daox_chunk { + None => {}, + Some(ref chunk) => try!(write!(f, "\n\n\ + {}", chunk)), + } + match self.sinf_chunk { + None => {}, + Some(ref chunk) => try!(write!(f, "\n\n\ + {}", chunk)), + } + match self.mtyp_chunk { + None => {}, + Some(ref chunk) => try!(write!(f, "\n\n\ + {}", chunk)), + } + Ok(()) + } +} diff --git a/src/mtyp.rs b/src/mtyp.rs new file mode 100644 index 0000000..02f55ef --- /dev/null +++ b/src/mtyp.rs @@ -0,0 +1,28 @@ +use std::fmt; + + +#[derive(Debug)] +pub struct NrgMtyp { + pub size: u32, + pub unknown: u32, +} + +impl NrgMtyp { + pub fn new() -> NrgMtyp { + NrgMtyp { + size: 0, + unknown: 0, + } + } +} + +impl fmt::Display for NrgMtyp { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Chunk ID: MTYP\n\ + Chunk description: Media Type (?)\n\ + Chunk size: {} Bytes\n\ + Unknown field: 0x{:04X}", + self.size, + self.unknown) + } +} diff --git a/src/readers.rs b/src/readers.rs new file mode 100644 index 0000000..9714534 --- /dev/null +++ b/src/readers.rs @@ -0,0 +1,58 @@ +use std::fs::File; +use std::io::Read; +use std::mem; + +use error::NrgError; + + +/// Reads a String of size `size` from `fd`. +pub fn read_sized_string(fd: &File, size: u64) -> Result { + let mut handle = fd.take(size); + let mut string = String::new(); + try!(handle.read_to_string(&mut string)); + Ok(string) +} + + +/// Reads a 64-bit unsigned integer from `fd`. +pub fn read_u64(fd: &mut File) -> Result { + let mut buf = [0u8; 8]; + try!(fd.read_exact(&mut buf)); + let i: u64; + unsafe { + i = mem::transmute(buf); + } + Ok(u64::from_be(i)) +} + + +/// Reads a 32-bit unsigned integer from `fd`. +pub fn read_u32(fd: &mut File) -> Result { + let mut buf = [0u8; 4]; + try!(fd.read_exact(&mut buf)); + let i: u32; + unsafe { + i = mem::transmute(buf); + } + Ok(u32::from_be(i)) +} + + +/// Reads a 16-bit unsigned integer from `fd`. +pub fn read_u16(fd: &mut File) -> Result { + let mut buf = [0u8; 2]; + try!(fd.read_exact(&mut buf)); + let i: u16; + unsafe { + i = mem::transmute(buf); + } + Ok(u16::from_be(i)) +} + + +/// Reads an unsigned byte from `fd`. +pub fn read_u8(fd: &mut File) -> Result { + let mut buf = [0u8; 1]; + try!(fd.read_exact(&mut buf)); + Ok(buf[0]) +} diff --git a/src/sinf.rs b/src/sinf.rs new file mode 100644 index 0000000..072473d --- /dev/null +++ b/src/sinf.rs @@ -0,0 +1,28 @@ +use std::fmt; + + +#[derive(Debug)] +pub struct NrgSinf { + pub size: u32, + pub nb_tracks: u32, +} + +impl NrgSinf { + pub fn new() -> NrgSinf { + NrgSinf { + size: 0, + nb_tracks: 0, + } + } +} + +impl fmt::Display for NrgSinf { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Chunk ID: SINF\n\ + Chunk description: Session Information\n\ + Chunk size: {} Bytes\n\ + Number of tracks in the session: {}", + self.size, + self.nb_tracks) + } +}