From 4b92eb0239571ed1e9ec0cc58538bd8044ee4c35 Mon Sep 17 00:00:00 2001 From: Matteo Cypriani Date: Wed, 7 Dec 2016 16:06:10 -0500 Subject: [PATCH] Improve handling of output file names * Use PathBuf operations to generate output file names. * Output files are now written to the working directory. * The cue sheet contains only the image's base name, not the full path. --- src/cue_sheet.rs | 58 ++++++++++++++++++++++++------------------------ src/error.rs | 6 ++++- src/main.rs | 16 ++++++------- src/raw_audio.rs | 41 +++++++++++++++++++--------------- 4 files changed, 65 insertions(+), 56 deletions(-) diff --git a/src/cue_sheet.rs b/src/cue_sheet.rs index 3095726..5c0ec7f 100644 --- a/src/cue_sheet.rs +++ b/src/cue_sheet.rs @@ -23,54 +23,54 @@ //! Module to extract the cue sheet from the NRG metadata. use std::io::Write; +use std::ffi::OsStr; use std::fs::File; +use std::path::PathBuf; use ::error::NrgError; use ::metadata::metadata::NrgMetadata; use ::metadata::cuex::NrgCuexTrack; -/// Writes the cue sheet for `image_name` into a file. +/// Writes the cue sheet for `img_path` into a file. /// -/// - `image_name` is the name of the input NRG file. -/// - `metadata` is the metadata extracted from `image_name` by -/// nrgrip::metadata. +/// - `img_path` is the name of the input NRG file. +/// - `metadata` is the metadata extracted from `img_path` by nrgrip::metadata. /// -/// The output file's name is derived from `image_name`. -pub fn write_cue_sheet(image_name: &String, metadata: &NrgMetadata) +/// The output file's name will be `img_path`'s base name stripped for its +/// extension (if any), with a ".cue" extension. +pub fn write_cue_sheet(img_path: &String, metadata: &NrgMetadata) -> Result<(), NrgError> { - if metadata.cuex_chunk.is_none() { - return Err(NrgError::NoNrgCue); - } - let cuex_tracks = &metadata.cuex_chunk.as_ref().unwrap().tracks; + // Make sure we have a cue sheet in the metadata + let cuex_tracks = match metadata.cuex_chunk { + None => return Err(NrgError::NoNrgCue), + Some(ref chunk) => &chunk.tracks, + }; - let file_name = make_cue_sheet_name(image_name); - let mut fd = try!(File::create(file_name)); + // Get the image's base name + let img_name = PathBuf::from(img_path); + let img_name = match img_name.file_name() { + Some(name) => name, + None => return Err(NrgError::FileName), + }; + + // Set the cue sheet file's name + let mut cue_name = PathBuf::from(img_name); + if cue_name.extension().unwrap_or(OsStr::new("")) == "cue" { + // img_path's extension was already .cue: problem! + return Err(NrgError::FileName); + } + cue_name.set_extension("cue"); // Write cue sheet - try!(writeln!(fd, "FILE \"{}\" RAW", image_name)); + let mut fd = try!(File::create(cue_name)); + try!(writeln!(fd, "FILE \"{}\" RAW", img_name.to_string_lossy())); try!(write_cue_tracks(&mut fd, cuex_tracks)); Ok(()) } -/// Generates the cue sheet's name from the NRG image's name. -/// -/// If `image_name`'s extension is `.nrg` (case-insensitive), the cue sheet's -/// name will be the same as `image_name` with a `.cue` extension instead of -/// `.nrg`. Otherwise, the cue sheet's name will be `image_name.cue`. -fn make_cue_sheet_name(image_name: &String) -> String { - let mut name = image_name.clone(); - if name.to_lowercase().ends_with(".nrg") { - let newlen = name.len() - 4; - name.truncate(newlen); - } - name.push_str(".cue"); - name -} - - /// Writes a list of cue tracks to `fd`. fn write_cue_tracks(fd: &mut File, cuex_tracks: &Vec) -> Result<(), NrgError> { diff --git a/src/error.rs b/src/error.rs index dc13a53..b248934 100644 --- a/src/error.rs +++ b/src/error.rs @@ -34,8 +34,9 @@ pub enum NrgError { NrgFormatV1, NrgChunkId, NoNrgCue, + FileName, AudioReadError, - AudioWriteError + AudioWriteError, } impl fmt::Display for NrgError { @@ -46,6 +47,7 @@ impl fmt::Display for NrgError { NrgError::NrgFormatV1 => write!(f, "NRG v1 format is not handled."), NrgError::NrgChunkId => write!(f, "NRG chunk ID unknown."), NrgError::NoNrgCue => write!(f, "NRG cue sheet chunk absent."), + NrgError::FileName => write!(f, "Invalid file name."), NrgError::AudioReadError => write!(f, "Error reading raw audio."), NrgError::AudioWriteError => write!(f, "Error writing raw audio."), } @@ -60,6 +62,7 @@ impl Error for NrgError { NrgError::NrgFormatV1 => "NRG format v1", NrgError::NrgChunkId => "NRG chunk ID", NrgError::NoNrgCue => "No NRG cue", + NrgError::FileName => "File name", NrgError::AudioReadError => "Audio read error", NrgError::AudioWriteError => "Audio write error", } @@ -72,6 +75,7 @@ impl Error for NrgError { NrgError::NrgFormatV1 => None, NrgError::NrgChunkId => None, NrgError::NoNrgCue => None, + NrgError::FileName => None, NrgError::AudioReadError => None, NrgError::AudioWriteError => None, } diff --git a/src/main.rs b/src/main.rs index 99e2bbc..1fe7a94 100644 --- a/src/main.rs +++ b/src/main.rs @@ -38,12 +38,12 @@ fn main() { let prog_name = argv.next().unwrap_or("nrgrip".to_string()); - let img_name = argv.next().unwrap_or("".to_string()); - if img_name == "" { + let img_path = argv.next().unwrap_or("".to_string()); + if img_path == "" { exit_usage(&prog_name); } - println!("NRG image name: \"{}\"", img_name); + println!("NRG image path: \"{}\"", img_path); // We don't support more than one input file if argv.next().is_some() { @@ -51,10 +51,10 @@ fn main() { } // Open the image file - let fd = File::open(&img_name); + let fd = File::open(&img_path); if fd.is_err() { println!("Can't open image file \"{}\": {}", - img_name, fd.unwrap_err().to_string()); + img_path, fd.unwrap_err().to_string()); process::exit(1); } let mut fd = fd.unwrap(); @@ -63,7 +63,7 @@ fn main() { let metadata = nrgrip::metadata::read_nrg_metadata(&mut fd); if metadata.is_err() { println!("Error reading \"{}\": {}", - img_name, metadata.unwrap_err().to_string()); + img_path, metadata.unwrap_err().to_string()); process::exit(1); } let metadata = metadata.unwrap(); @@ -71,7 +71,7 @@ fn main() { // Read and write the cue sheet println!("\nNow extracting cue sheet..."); - if let Err(err) = nrgrip::cue_sheet::write_cue_sheet(&img_name, &metadata) { + if let Err(err) = nrgrip::cue_sheet::write_cue_sheet(&img_path, &metadata) { println!("Error writing cue sheet: {}", err.to_string()); process::exit(1); } @@ -79,7 +79,7 @@ fn main() { // Extract raw audio data println!("Now extracting raw audio data..."); if let Err(err) = nrgrip::raw_audio::extract_nrg_raw_audio( - &mut fd, &img_name, &metadata) { + &mut fd, &img_path, &metadata) { println!("Error extracting raw audio data: {}", err.to_string()); } println!("OK!"); diff --git a/src/raw_audio.rs b/src/raw_audio.rs index 9785f88..c02b7ef 100644 --- a/src/raw_audio.rs +++ b/src/raw_audio.rs @@ -24,6 +24,7 @@ use std::fs::File; use std::io::{Seek, SeekFrom, Read, Write}; +use std::path::PathBuf; use ::error::NrgError; use ::metadata::metadata::NrgMetadata; @@ -32,22 +33,23 @@ use ::metadata::metadata::NrgMetadata; /// Extracts the raw audio data from an NRG image. /// /// - `in_fd` is the handler to the NRG image file. -/// - `image_name` is the name of the input NRG file. -/// - `metadata` is the metadata extracted from `image_name` by -/// nrgrip::metadata. +/// - `img_path` is the name of the input NRG file. +/// - `metadata` is the metadata extracted from `img_path` by nrgrip::metadata. /// -/// The output file's name is derived from `image_name`. +/// The output file's name is derived from `img_path`. pub fn extract_nrg_raw_audio(in_fd: &mut File, - image_name: &String, + img_path: &String, metadata: &NrgMetadata) -> Result<(), NrgError> { + // Seek to the first audio byte let skip_bytes = get_daox_track1_index1(metadata); - try!(in_fd.seek(SeekFrom::Start(skip_bytes))); - let file_name = make_output_file_name(image_name); - let mut out_fd = try!(File::create(file_name)); + // Open output file + let audio_name = try!(make_output_file_name(img_path)); + let mut out_fd = try!(File::create(audio_name)); + // Read/write audio data let mut cur_offset = skip_bytes; while cur_offset < metadata.chunk_offset { let mut audio_buf = [0u8; 2352]; @@ -85,15 +87,18 @@ fn get_daox_track1_index1(metadata: &NrgMetadata) -> u64 { /// Generates the output file's name from the NRG image's name. /// -/// If `image_name`'s extension is `.nrg` (case-insensitive), the name will be -/// the same as `image_name` with a `.raw` extension instead of `.nrg`. -/// Otherwise, it will be `image_name.raw`. -fn make_output_file_name(image_name: &String) -> String { - let mut name = image_name.clone(); - if name.to_lowercase().ends_with(".nrg") { - let newlen = name.len() - 4; - name.truncate(newlen); +/// The output file's name will be `img_path`'s base name stripped for its +/// extension (if any), with a ".raw" extension. +fn make_output_file_name(img_path: &String) -> Result { + let mut name = PathBuf::from(img_path); + name.set_extension("raw"); + let name = PathBuf::from(name); + let name = try!(name.file_name().ok_or(NrgError::FileName)); + + // Make sure the new name and the original name are different + if name == img_path.as_str() { + return Err(NrgError::FileName); } - name.push_str(".raw"); - name + + Ok(name.to_string_lossy().into_owned()) }