1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
#[macro_use]
extern crate nom;
#[macro_use]
extern crate itertools;
extern crate crc;

mod internal;
mod parser;
mod writer;

use serde::{Serialize, Deserialize};

#[derive(Debug, Serialize, Deserialize)]
pub struct Nus3audioFile {
    pub files: Vec<AudioFile>,
}

#[derive(Debug, Serialize, Deserialize)]
#[derive(binwrite::BinWrite)]
pub struct AudioFile {
    #[binwrite(ignore)]
    pub id: u32,
    #[binwrite(ignore)]
    pub name: String,
    #[binwrite(align_after(0x10))]
    pub data: Vec<u8>,
}

impl Nus3audioFile {
    pub fn new() -> Self {
        Nus3audioFile { files: vec![] }
    }

    pub fn from_bytes(data: &[u8]) -> Nus3audioFile {
        parser::take_file(&data[..])
            .expect("Failed to parse file")
            .1
    }

    pub fn try_from_bytes(data: &[u8]) -> Option<Nus3audioFile> {
        parser::take_file(&data[..])
            .map(|(_, file)| file)
            .ok()
    }

    pub fn open<P: AsRef<std::path::Path>>(path: P) -> Result<Nus3audioFile, std::io::Error> {
        Ok(Nus3audioFile::from_bytes(&std::fs::read(path)?[..]))
    }
}

impl AudioFile {
    pub fn from_id(id: u32) -> Self {
        AudioFile {
            data: vec![],
            name: String::new(),
            id,
        }
    }

    pub fn filename(&self) -> String {
        self.name.clone()
            + if self.data.len() >= 4 {
                match &self.data[..4] {
                    b"OPUS" => ".lopus",
                    b"IDSP" => ".idsp",
                    _ => ".bin",
                }
            } else {
                ".bin"
            }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    static TEST_FILE: &[u8] = b"NUS3x\x00\x00\x00AUDIINDX\x04\x00\x00\x00\x01\x00\x00\x00TNID\x04\x00\x00\x00E\x00\x00\x00NMOF\x04\x00\x00\x00H\x00\x00\x00ADOF\x08\x00\x00\x00p\x00\x00\x00\x05\x00\x00\x00TNNM\n\x00\x00\x00test_name\x00JUNK\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00PACK\x10\x00\x00\x00test\n\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
    static REPEAT_TEST_FILE: &[u8] = b"NUS3\x98\x00\x00\x00AUDIINDX\x04\x00\x00\x00\x02\x00\x00\x00TNID\x08\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00NMOF\x08\x00\x00\x00X\x00\x00\x00f\x00\x00\x00ADOF\x10\x00\x00\x00\x90\x00\x00\x00\x07\x00\x00\x00\x90\x00\x00\x00\x07\x00\x00\x00TNNM\x1c\x00\x00\x00repeat_test_1\x00repeat_test_2\x00JUNK\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00PACK\x10\x00\x00\x00repeat\n\x00\x00\x00\x00\x00\x00\x00\x00\x00";

    #[test]
    fn attempt_parse_test_file() {
        let nus_file = Nus3audioFile::from_bytes(&TEST_FILE[..]);
        assert_eq!(nus_file.files.len(), 1);
        assert_eq!(nus_file.files[0].name, "test_name");
        assert_eq!(nus_file.files[0].data, b"test\n");
        assert_eq!(nus_file.files[0].id, 69);
    }

    #[test]
    fn attempt_parse_repeat_test_file() {
        let nus_file = Nus3audioFile::from_bytes(&REPEAT_TEST_FILE[..]);
        assert_eq!(nus_file.files.len(), 2);
        assert_eq!(nus_file.files[0].name, "repeat_test_1");
        assert_eq!(nus_file.files[0].data, b"repeat\n");
        assert_eq!(nus_file.files[0].id, 0);
        assert_eq!(nus_file.files.len(), 2);
        assert_eq!(nus_file.files[1].name, "repeat_test_2");
        assert_eq!(nus_file.files[1].data, b"repeat\n");
        assert_eq!(nus_file.files[1].id, 1);
    }

    #[test]
    fn test_write_file() {
        let nus_file = Nus3audioFile::from_bytes(&REPEAT_TEST_FILE[..]);
        let mut nus_write_file: Vec<u8> = Vec::with_capacity(REPEAT_TEST_FILE.len());
        nus_file.write(&mut nus_write_file);
        assert_eq!(REPEAT_TEST_FILE, &nus_write_file[..]);
    }
}