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
#[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))] #[serde(skip)] 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 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[..]); } }