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.
This commit is contained in:
Matteo Cypriani 2016-12-07 16:06:10 -05:00
parent 5d0973f5ae
commit 4b92eb0239
4 changed files with 65 additions and 56 deletions

View File

@ -23,54 +23,54 @@
//! Module to extract the cue sheet from the NRG metadata. //! Module to extract the cue sheet from the NRG metadata.
use std::io::Write; use std::io::Write;
use std::ffi::OsStr;
use std::fs::File; use std::fs::File;
use std::path::PathBuf;
use ::error::NrgError; use ::error::NrgError;
use ::metadata::metadata::NrgMetadata; use ::metadata::metadata::NrgMetadata;
use ::metadata::cuex::NrgCuexTrack; 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. /// - `img_path` is the name of the input NRG file.
/// - `metadata` is the metadata extracted from `image_name` by /// - `metadata` is the metadata extracted from `img_path` by nrgrip::metadata.
/// nrgrip::metadata.
/// ///
/// The output file's name is derived from `image_name`. /// The output file's name will be `img_path`'s base name stripped for its
pub fn write_cue_sheet(image_name: &String, metadata: &NrgMetadata) /// extension (if any), with a ".cue" extension.
pub fn write_cue_sheet(img_path: &String, metadata: &NrgMetadata)
-> Result<(), NrgError> { -> Result<(), NrgError> {
if metadata.cuex_chunk.is_none() { // Make sure we have a cue sheet in the metadata
return Err(NrgError::NoNrgCue); let cuex_tracks = match metadata.cuex_chunk {
} None => return Err(NrgError::NoNrgCue),
let cuex_tracks = &metadata.cuex_chunk.as_ref().unwrap().tracks; Some(ref chunk) => &chunk.tracks,
};
let file_name = make_cue_sheet_name(image_name); // Get the image's base name
let mut fd = try!(File::create(file_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 // 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)); try!(write_cue_tracks(&mut fd, cuex_tracks));
Ok(()) 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`. /// Writes a list of cue tracks to `fd`.
fn write_cue_tracks(fd: &mut File, cuex_tracks: &Vec<NrgCuexTrack>) fn write_cue_tracks(fd: &mut File, cuex_tracks: &Vec<NrgCuexTrack>)
-> Result<(), NrgError> { -> Result<(), NrgError> {

View File

@ -34,8 +34,9 @@ pub enum NrgError {
NrgFormatV1, NrgFormatV1,
NrgChunkId, NrgChunkId,
NoNrgCue, NoNrgCue,
FileName,
AudioReadError, AudioReadError,
AudioWriteError AudioWriteError,
} }
impl fmt::Display for NrgError { 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::NrgFormatV1 => write!(f, "NRG v1 format is not handled."),
NrgError::NrgChunkId => write!(f, "NRG chunk ID unknown."), NrgError::NrgChunkId => write!(f, "NRG chunk ID unknown."),
NrgError::NoNrgCue => write!(f, "NRG cue sheet chunk absent."), 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::AudioReadError => write!(f, "Error reading raw audio."),
NrgError::AudioWriteError => write!(f, "Error writing raw audio."), NrgError::AudioWriteError => write!(f, "Error writing raw audio."),
} }
@ -60,6 +62,7 @@ impl Error for NrgError {
NrgError::NrgFormatV1 => "NRG format v1", NrgError::NrgFormatV1 => "NRG format v1",
NrgError::NrgChunkId => "NRG chunk ID", NrgError::NrgChunkId => "NRG chunk ID",
NrgError::NoNrgCue => "No NRG cue", NrgError::NoNrgCue => "No NRG cue",
NrgError::FileName => "File name",
NrgError::AudioReadError => "Audio read error", NrgError::AudioReadError => "Audio read error",
NrgError::AudioWriteError => "Audio write error", NrgError::AudioWriteError => "Audio write error",
} }
@ -72,6 +75,7 @@ impl Error for NrgError {
NrgError::NrgFormatV1 => None, NrgError::NrgFormatV1 => None,
NrgError::NrgChunkId => None, NrgError::NrgChunkId => None,
NrgError::NoNrgCue => None, NrgError::NoNrgCue => None,
NrgError::FileName => None,
NrgError::AudioReadError => None, NrgError::AudioReadError => None,
NrgError::AudioWriteError => None, NrgError::AudioWriteError => None,
} }

View File

@ -38,12 +38,12 @@ fn main() {
let prog_name = argv.next().unwrap_or("nrgrip".to_string()); let prog_name = argv.next().unwrap_or("nrgrip".to_string());
let img_name = argv.next().unwrap_or("".to_string()); let img_path = argv.next().unwrap_or("".to_string());
if img_name == "" { if img_path == "" {
exit_usage(&prog_name); 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 // We don't support more than one input file
if argv.next().is_some() { if argv.next().is_some() {
@ -51,10 +51,10 @@ fn main() {
} }
// Open the image file // Open the image file
let fd = File::open(&img_name); let fd = File::open(&img_path);
if fd.is_err() { if fd.is_err() {
println!("Can't open image file \"{}\": {}", println!("Can't open image file \"{}\": {}",
img_name, fd.unwrap_err().to_string()); img_path, fd.unwrap_err().to_string());
process::exit(1); process::exit(1);
} }
let mut fd = fd.unwrap(); let mut fd = fd.unwrap();
@ -63,7 +63,7 @@ fn main() {
let metadata = nrgrip::metadata::read_nrg_metadata(&mut fd); let metadata = nrgrip::metadata::read_nrg_metadata(&mut fd);
if metadata.is_err() { if metadata.is_err() {
println!("Error reading \"{}\": {}", println!("Error reading \"{}\": {}",
img_name, metadata.unwrap_err().to_string()); img_path, metadata.unwrap_err().to_string());
process::exit(1); process::exit(1);
} }
let metadata = metadata.unwrap(); let metadata = metadata.unwrap();
@ -71,7 +71,7 @@ fn main() {
// Read and write the cue sheet // Read and write the cue sheet
println!("\nNow extracting 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()); println!("Error writing cue sheet: {}", err.to_string());
process::exit(1); process::exit(1);
} }
@ -79,7 +79,7 @@ fn main() {
// Extract raw audio data // Extract raw audio data
println!("Now extracting raw audio data..."); println!("Now extracting raw audio data...");
if let Err(err) = nrgrip::raw_audio::extract_nrg_raw_audio( 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!("Error extracting raw audio data: {}", err.to_string());
} }
println!("OK!"); println!("OK!");

View File

@ -24,6 +24,7 @@
use std::fs::File; use std::fs::File;
use std::io::{Seek, SeekFrom, Read, Write}; use std::io::{Seek, SeekFrom, Read, Write};
use std::path::PathBuf;
use ::error::NrgError; use ::error::NrgError;
use ::metadata::metadata::NrgMetadata; use ::metadata::metadata::NrgMetadata;
@ -32,22 +33,23 @@ use ::metadata::metadata::NrgMetadata;
/// Extracts the raw audio data from an NRG image. /// Extracts the raw audio data from an NRG image.
/// ///
/// - `in_fd` is the handler to the NRG image file. /// - `in_fd` is the handler to the NRG image file.
/// - `image_name` is the name of the input NRG file. /// - `img_path` is the name of the input NRG file.
/// - `metadata` is the metadata extracted from `image_name` by /// - `metadata` is the metadata extracted from `img_path` by nrgrip::metadata.
/// 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, pub fn extract_nrg_raw_audio(in_fd: &mut File,
image_name: &String, img_path: &String,
metadata: &NrgMetadata) metadata: &NrgMetadata)
-> Result<(), NrgError> { -> Result<(), NrgError> {
// Seek to the first audio byte
let skip_bytes = get_daox_track1_index1(metadata); let skip_bytes = get_daox_track1_index1(metadata);
try!(in_fd.seek(SeekFrom::Start(skip_bytes))); try!(in_fd.seek(SeekFrom::Start(skip_bytes)));
let file_name = make_output_file_name(image_name); // Open output file
let mut out_fd = try!(File::create(file_name)); 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; let mut cur_offset = skip_bytes;
while cur_offset < metadata.chunk_offset { while cur_offset < metadata.chunk_offset {
let mut audio_buf = [0u8; 2352]; 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. /// 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 output file's name will be `img_path`'s base name stripped for its
/// the same as `image_name` with a `.raw` extension instead of `.nrg`. /// extension (if any), with a ".raw" extension.
/// Otherwise, it will be `image_name.raw`. fn make_output_file_name(img_path: &String) -> Result<String, NrgError> {
fn make_output_file_name(image_name: &String) -> String { let mut name = PathBuf::from(img_path);
let mut name = image_name.clone(); name.set_extension("raw");
if name.to_lowercase().ends_with(".nrg") { let name = PathBuf::from(name);
let newlen = name.len() - 4; let name = try!(name.file_name().ok_or(NrgError::FileName));
name.truncate(newlen);
// 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())
} }