pub(crate) mod predictor;
pub(crate) mod raw;
pub mod cn3;
pub mod midi;
pub mod nc2;
pub mod nc2d;
pub mod nd2;
pub mod nd3;
pub mod ne3;
pub mod ne4;
pub mod ne5;
pub mod ne6;
pub mod ne7;
pub mod ng2;
pub mod nl4;
pub mod nla1;
pub mod no3;
pub mod np;
pub mod np2;
pub mod np3;
pub mod np4;
pub mod np5;
pub mod npip;
pub mod npno;
pub mod ns2;
pub mod ns3;
pub mod ns4;
pub mod nsclassic;
pub mod nsmp;
pub mod nsmpproj;
pub mod nw;
pub mod nw2;
pub mod sysex;
use crate::error::{Error, ParseError};
pub(crate) fn known_version(
format: &'static str,
version: u32,
supported: &'static [u32],
) -> Result<(), Error> {
if supported.contains(&version) {
Ok(())
} else {
Err(ParseError::UnsupportedVersion {
format,
version,
supported,
}
.into())
}
}
#[cfg(feature = "bundle")]
pub(crate) fn zip_member_bytes(file: &mut zip::read::ZipFile<'_>) -> Result<Vec<u8>, Error> {
use std::cmp::Ordering;
use std::io::Read;
let refuse = |value: String| -> Error {
ParseError::OutOfBounds {
value,
bound: "the length its directory entry declares".into(),
}
.into()
};
let declared = file.size();
let one_past_declared = declared
.checked_add(1)
.ok_or_else(|| refuse(format!("a member declaring {declared} bytes")))?;
let mut bytes = Vec::new();
file.take(one_past_declared).read_to_end(&mut bytes)?;
let yielded = bytes.len() as u64;
match yielded.cmp(&declared) {
Ordering::Greater => Err(refuse(format!(
"a member yielding more than the {declared} bytes it declares"
))),
Ordering::Less => Err(refuse(format!(
"a member yielding {yielded} of the {declared} bytes it declares"
))),
Ordering::Equal => Ok(bytes),
}
}
#[cfg(feature = "bundle")]
pub(crate) fn zip_members(
reader: &mut (impl std::io::Read + std::io::Seek),
format: &'static str,
) -> Result<Vec<(String, crate::cbin::Cbin<crate::cbin::RawBody>)>, Error> {
let mut zip = zip::ZipArchive::new(reader)?;
let mut members = Vec::new();
for i in 0..zip.len() {
let mut file = zip.by_index(i)?;
if file.is_dir() {
continue;
}
let name = file.name().to_string();
let buffer = zip_member_bytes(&mut file)?;
let member = crate::cbin::read(&mut std::io::Cursor::new(buffer), format)?;
members.push((name, member));
}
Ok(members)
}
#[cfg(feature = "bundle")]
pub(crate) fn zip_raw_members(
reader: &mut (impl std::io::Read + std::io::Seek),
) -> Result<Vec<(String, crate::cbin::Cbin<crate::cbin::RawBody>)>, Error> {
let mut zip = zip::ZipArchive::new(reader)?;
let mut members = Vec::new();
for i in 0..zip.len() {
let mut file = zip.by_index(i)?;
if file.is_dir() || file.name().ends_with("meta.xml") {
continue;
}
let name = file.name().to_string();
let buffer = zip_member_bytes(&mut file)?;
let member = crate::cbin::read_raw(&mut std::io::Cursor::new(buffer))?;
members.push((name, member));
}
Ok(members)
}
#[cfg(all(test, feature = "bundle"))]
mod tests {
use super::*;
use std::io::{Cursor, Write};
const MEMBER: &[u8; 16] = b"0123456789abcdef";
fn archive_declaring(declared: u32) -> Vec<u8> {
let mut writer = zip::ZipWriter::new(Cursor::new(Vec::new()));
writer
.start_file(
"member.ne5p",
zip::write::SimpleFileOptions::default()
.compression_method(zip::CompressionMethod::Stored),
)
.unwrap();
writer.write_all(MEMBER).unwrap();
let mut bytes = writer.finish().unwrap().into_inner();
let at = |signature: &[u8; 4]| {
bytes
.windows(4)
.position(|window| window == signature)
.expect("a ZIP signature")
};
let (local, central) = (at(b"PK\x03\x04"), at(b"PK\x01\x02"));
bytes[local + 22..local + 26].copy_from_slice(&declared.to_le_bytes());
bytes[central + 24..central + 28].copy_from_slice(&declared.to_le_bytes());
bytes
}
fn member_bytes(archive: &[u8]) -> Result<Vec<u8>, Error> {
let mut zip = zip::ZipArchive::new(Cursor::new(archive)).unwrap();
let mut file = zip.by_index(0).unwrap();
zip_member_bytes(&mut file)
}
#[test]
fn a_member_is_read_to_its_declared_length() {
let bytes = member_bytes(&archive_declaring(MEMBER.len() as u32)).unwrap();
assert_eq!(bytes, MEMBER);
}
#[test]
fn a_member_yielding_more_than_it_declares_is_refused() {
let archive = archive_declaring(MEMBER.len() as u32 - 1);
let err = member_bytes(&archive).unwrap_err();
assert!(
err.to_string()
.contains("more than the 15 bytes it declares"),
"expected a refusal naming the declared length, got {err}"
);
}
#[test]
fn a_member_yielding_fewer_bytes_than_it_declares_is_refused() {
let archive = archive_declaring(MEMBER.len() as u32 + 1);
let err = member_bytes(&archive).unwrap_err();
assert!(
err.to_string().contains("16 of the 17 bytes it declares"),
"expected a refusal naming both lengths, got {err}"
);
}
#[test]
fn a_member_declaring_more_than_the_archive_holds_is_refused() {
let archive = archive_declaring(64 * 1024 * 1024);
assert!(
archive.len() < 1024,
"a declaration of 64 MiB should sit in a tiny archive, which is {} bytes",
archive.len()
);
let err = member_bytes(&archive).unwrap_err();
assert!(
err.to_string()
.contains("16 of the 67108864 bytes it declares"),
"expected a refusal naming the bytes the member yielded, got {err}"
);
}
}