1use std::fmt::Display;
2use std::fs;
3use std::io;
4use std::io::{BufReader, Read, Seek, SeekFrom, Write};
5use std::io::{Error, ErrorKind};
6use std::mem::swap;
7use std::ops::IndexMut;
8
9const WAV_RIFF_HEADER_LENGTH: u32 = 12;
10const WAV_FORMAT_HEADER_LENGTH: u32 = 24;
11const WAV_DATA_HEADER_LENGTH: u32 = 8;
12const WAV_HEADER_LENGTH: u32 =
13    WAV_RIFF_HEADER_LENGTH + WAV_FORMAT_HEADER_LENGTH + WAV_DATA_HEADER_LENGTH;
14
15const SECTOR_SIZE: u64 = 2352;
16
17#[derive(Default)]
18pub struct Args {
19    pub output_name: String,
20    pub bin_file: String,
21    pub cue_file: String,
22    pub verbose: bool,
23    pub psx_truncate: bool,
24    pub raw: bool,
25    pub swap_audo_bytes: bool,
26    pub to_wav: bool,
27}
28
29impl Args {
30    pub fn new(mut options: Args) -> Self {
31        if options.cue_file.is_empty() {
37            swap(&mut options.cue_file, &mut options.bin_file);
38        }
39
40        if options.output_name.is_empty() {
41            options.output_name = String::from(
42                options
43                    .cue_file
45                    .split('/')
46                    .next_back()
47                    .unwrap()
48                    .split('.')
49                    .next()
50                    .unwrap(),
51            );
52        }
53
54        options
55    }
56}
57
58#[derive(Default)]
59pub struct Track {
60    start_sector: u64,
61    stop_sector: Option<u64>,
62    start: u64,
63    stop: Option<u64>,
64    mode: Mode,
65    extension: Extension,
66    number: u32,
67    audio: bool,
68    data_block_offset: u32,
69    data_block_size: u32,
70}
71
72impl Track {
73    fn get_track_mode(&mut self, a: &Args) {
74        match self.mode {
75            Mode::Unknown => {
76                self.data_block_offset = 0;
77                self.data_block_size = 2352;
78                self.extension = Extension::Ugh;
79            }
80            Mode::Audio => {
81                self.data_block_offset = 0;
82                self.data_block_size = 2352;
83                self.audio = true;
84                if a.to_wav {
85                    self.extension = Extension::Wav;
86                } else {
87                    self.extension = Extension::Cdr;
88                }
89            }
90            Mode::Mode1_2352 => {
91                self.data_block_offset = 16;
92                self.data_block_size = 2048;
93                self.extension = Extension::Iso;
94            }
95            Mode::Mode2_2352 => {
96                self.extension = Extension::Iso;
97                if a.raw {
98                    self.data_block_offset = 0;
99                    self.data_block_size = 2352;
100                } else if a.psx_truncate {
101                    self.data_block_offset = 0;
102                    self.data_block_size = 2336;
103                } else {
104                    self.data_block_offset = 24;
105                    self.data_block_size = 2048;
106                }
107            }
108            Mode::Mode2_2336 => {
109                self.data_block_offset = 16;
110                self.data_block_size = 2336;
111                self.extension = Extension::Iso;
112            }
113        }
114    }
115
116    fn wav_header(&self) -> Vec<u8> {
117        let reallen =
119            (self.stop_sector.unwrap() - self.start_sector + 1) * self.data_block_size as u64;
120
121        let wav_header = [
122            "RIFF".as_bytes(),
124            (reallen as u32 + WAV_DATA_HEADER_LENGTH + WAV_FORMAT_HEADER_LENGTH + 4)
125                .to_le_bytes()
126                .as_slice(), "WAVE".as_bytes(),
128            "fmt ".as_bytes(),
130            0x10_u32.to_le_bytes().as_slice(), 0x1_u16.to_le_bytes().as_slice(),  0x2_u16.to_le_bytes().as_slice(),  44100_u32.to_le_bytes().as_slice(), (44100_u32 * 4).to_le_bytes().as_slice(), 0x4_u16.to_le_bytes().as_slice(),  0x10_u16.to_le_bytes().as_slice(), "data".as_bytes(),
139            (reallen as u32).to_le_bytes().as_slice(),
140        ]
141        .concat();
142        wav_header
143    }
144
145    fn write_to_file(&self, reader: &mut BufReader<&std::fs::File>, a: &Args) -> io::Result<()> {
146        let filename = format!(
147            "{}{:0>2}.{}",
148            a.output_name,
149            self.number,
150            self.extension.as_ref()
151        );
152        let sectors = self.stop_sector.unwrap() - self.start_sector + 1;
153        let mut file_length = sectors * self.data_block_size as u64;
154        let mut sector = [0u8; SECTOR_SIZE as usize];
155
156        let out_file = match fs::File::create(&filename) {
157            Ok(t_file) => t_file,
158            Err(e) => {
159                return Err(Error::new(
160                    ErrorKind::Other,
161                    format!("Could not write to track: {}", e),
162                ))
163            }
164        };
165
166        let mut writer: std::io::BufWriter<&std::fs::File> =
167            std::io::BufWriter::with_capacity(SECTOR_SIZE as usize * 16, &out_file);
168
169        if let Err(e) = reader.seek(SeekFrom::Start(self.start)) {
170            return Err(Error::new(
171                ErrorKind::Other,
172                format!("Could not seek to track location {}", e),
173            ));
174        }
175
176        if a.to_wav && self.audio {
177            file_length += WAV_HEADER_LENGTH as u64;
178            if let Err(e) = writer.write(&self.wav_header()) {
179                return Err(Error::new(
180                    ErrorKind::Other,
181                    format!("Could not write to track {}", e),
182                ));
183            };
184        }
185
186        for _ in 0..sectors {
187            if let Err(e) = reader.read(&mut sector) {
188                return Err(Error::new(
189                    ErrorKind::Other,
190                    format!("Could not read from {} {}", &a.bin_file, e),
191                ));
192            }
193            if self.audio && a.swap_audo_bytes {
194                for i in (0..SECTOR_SIZE as usize).step_by(2) {
195                    sector.swap(i, i + 1);
196                }
197            }
198            if let Err(e) = writer.write(
199                §or[self.data_block_offset as usize
200                    ..(self.data_block_offset + self.data_block_size) as usize],
201            ) {
202                return Err(Error::new(
203                    ErrorKind::Other,
204                    format!("Could not write to track {}", e),
205                ));
206            };
207        }
208
209        if a.verbose {
210            println!(
211                "{}: {} {}MiB",
212                self.number,
213                filename,
214                file_length / 1024 / 1024
215            );
216        }
217
218        Ok(())
219    }
220}
221
222#[derive(Default)]
223pub enum Mode {
224    #[default]
225    Unknown,
226    Audio,
227    Mode1_2352,
228    Mode2_2352,
229    Mode2_2336,
230}
231
232impl Mode {
233    const UNKNOWN: &'static str = "UNKNOWN";
234    const AUDIO: &'static str = "AUDIO";
235    const MODE1_2352: &'static str = "MODE1/2352";
236    const MODE2_2352: &'static str = "MODE2/2352";
237    const MODE2_2336: &'static str = "MODE2/2336";
238}
239
240impl AsRef<str> for Mode {
241    fn as_ref(&self) -> &'static str {
242        match self {
243            Mode::Unknown => Mode::UNKNOWN,
244            Mode::Audio => Mode::AUDIO,
245            Mode::Mode1_2352 => Mode::MODE1_2352,
246            Mode::Mode2_2352 => Mode::MODE2_2352,
247            Mode::Mode2_2336 => Mode::MODE2_2352,
248        }
249    }
250}
251
252impl Display for Mode {
253    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
254        f.pad(self.as_ref())
255    }
256}
257
258impl From<&str> for Mode {
259    fn from(s: &str) -> Self {
260        match s {
261            Mode::AUDIO => Mode::Audio,
262            Mode::MODE1_2352 => Mode::Mode1_2352,
263            Mode::MODE2_2336 => Mode::Mode2_2336,
264            Mode::MODE2_2352 => Mode::Mode2_2352,
265            _ => Mode::Unknown,
266        }
267    }
268}
269
270#[derive(Default)]
271enum Extension {
272    #[default]
273    Ugh,
274    Iso,
275    Cdr,
276    Wav,
277}
278
279impl Extension {
280    const UGH: &'static str = "ugh";
281    const ISO: &'static str = "iso";
282    const CDR: &'static str = "cdr";
283    const WAV: &'static str = "wav";
284}
285
286impl AsRef<str> for Extension {
287    fn as_ref(&self) -> &str {
288        match self {
289            Extension::Ugh => Extension::UGH,
290            Extension::Iso => Extension::ISO,
291            Extension::Cdr => Extension::CDR,
292            Extension::Wav => Extension::WAV,
293        }
294    }
295}
296
297fn read_cue(args: &mut Args) -> io::Result<Vec<Track>> {
298    let mut tracks: Vec<Track> = Vec::with_capacity(32);
299
300    let cue = match std::fs::read_to_string(&args.cue_file) {
301        Ok(f) => f,
302        Err(e) => {
303            return Err(Error::new(
304                ErrorKind::Other,
305                format!("Could not open CUE file: {}", e),
306            ))
307        }
308    };
309
310    for s in cue.lines() {
311        for e in s.split_whitespace() {
312            match e {
313                "TRACK" => {
314                    tracks.push(Default::default());
315                    if args.verbose {
316                        println!();
317                    }
318                    let mut t = s.split_whitespace().skip(1);
319                    match t.next() {
320                        Some(num_s) => match num_s.parse() {
321                            Ok(num) => {
322                                tracks.last_mut().unwrap().number = num;
323                                if args.verbose {
324                                    print!("Track {:>2}: ", num);
325                                }
326                            }
327                            Err(e) => {
328                                return Err(Error::new(
329                                    ErrorKind::Other,
330                                    format!("Error parsing track number! {}", e),
331                                ))
332                            }
333                        },
334                        None => return Err(Error::new(ErrorKind::Other, "Unknown error")),
335                    }
336                    match t.next() {
337                        Some(mode) => {
338                            tracks.last_mut().unwrap().mode = mode.into();
339                            tracks.last_mut().unwrap().get_track_mode(args);
340                            if args.verbose {
341                                print!("{:12}", tracks.last().unwrap().mode);
342                            }
343                        }
344                        None => return Err(Error::new(ErrorKind::Other, "Unknown error")),
345                    }
346                    break;
347                }
348                "INDEX" => {
349                    let mut i = s.split_whitespace().skip(1);
350                    match i.next() {
351                        Some(index_s) => {
352                            if args.verbose {
353                                print!("{} ", index_s);
354                            }
355                        }
356                        None => return Err(Error::new(ErrorKind::Other, "Missing index number")),
357                    }
358                    match i.next() {
359                        Some(time) => {
360                            if args.verbose {
361                                print!("{} ", time);
362                            }
363                            tracks.last_mut().unwrap().start_sector = time_to_frames(time).unwrap();
364                            tracks.last_mut().unwrap().start =
365                                tracks.last_mut().unwrap().start_sector * SECTOR_SIZE;
366                            if tracks.len() > 1 && tracks[tracks.len() - 2].stop_sector.is_none() {
367                                tracks.index_mut(tracks.len() - 2).stop_sector =
368                                    Some(tracks.last().unwrap().start_sector - 1);
369                                tracks.index_mut(tracks.len() - 2).stop =
370                                    Some(tracks.last().unwrap().start - 1);
371                            }
372                        }
373                        None => return Err(Error::new(ErrorKind::Other, "Missing INDEX time")),
374                    }
375                    break;
376                }
377                "FILE" => {
378                    let mut f = s.split_whitespace().skip(1);
379                    match f.next() {
380                        Some(fname) => {
381                            let mut filename = fname.chars();
382                            filename.next();
383                            filename.next_back();
384                            if args.bin_file.is_empty() {
385                                args.bin_file = String::from(filename.as_str());
386                                if args.verbose {
387                                    eprintln!(
388                                        "BIN file not supplied. Reading BIN file from CUE file"
389                                    );
390                                }
391                            } else if filename.as_str() != args.bin_file.split('/').last().unwrap()
392                                && args.verbose
393                            {
394                                eprintln!("Filename in CUE file doesn't match filename provided")
395                            }
396                        }
397                        None => return Err(Error::new(ErrorKind::Other, "Error reading FILE row")),
398                    }
399                    break;
400                }
401                _ => continue,
402            }
403        }
404    }
405    if tracks.is_empty() {
406        return Err(Error::new(ErrorKind::Other, "No valid CUE data found"));
407    }
408    let bin_file_size = match fs::metadata(&args.bin_file) {
410        Ok(metadata) => metadata.len(),
411        Err(e) => {
412            return Err(Error::new(
413                ErrorKind::Other,
414                format!("Could not open BIN file\n{}", e),
415            ))
416        }
417    };
418    tracks.last_mut().unwrap().stop = Some(bin_file_size - 1);
419    tracks.last_mut().unwrap().stop_sector =
420        Some(tracks.last().unwrap().stop.unwrap() / SECTOR_SIZE);
421
422    Ok(tracks)
423}
424
425fn time_to_frames(s: &str) -> io::Result<u64> {
426    let mut duration = [0u64; 3]; for (c, t) in s.split(':').zip(duration.iter_mut()) {
429        *t = match c.parse() {
430            Ok(t) => t,
431            Err(e) => {
432                return Err(Error::new(
433                    ErrorKind::Other,
434                    format!("parse int error on time_to_frames {}", e),
435                ))
436            }
437        };
438    }
439    Ok(75 * (duration[0] * 60 + duration[1]) + duration[2])
440}
441
442pub fn convert(options: Args) -> io::Result<()> {
443    let mut args = Args::new(options);
444
445    let tracks = match read_cue(&mut args) {
446        Ok(i_tracks) => i_tracks,
447        Err(e) => return Err(e),
448    };
449
450    let in_file = match fs::File::open(&args.bin_file) {
453        Ok(i_file) => i_file,
454        Err(e) => return Err(e),
455    };
456    let mut reader: std::io::BufReader<&std::fs::File> =
457        std::io::BufReader::with_capacity(SECTOR_SIZE as usize * 16, &in_file);
458
459    for t in &tracks {
460        match t.write_to_file(&mut reader, &args) {
461            Ok(()) => {}
462            Err(err) => return Err(err),
463        }
464    }
465
466    Ok(())
467}