feat: add cli support for ncm

This commit is contained in:
鲁树人
2024-09-14 16:16:20 +01:00
parent abd4549da3
commit 208461e089
9 changed files with 140 additions and 10 deletions

View File

@@ -1,5 +1,7 @@
use crate::{content_key, metadata, NetEaseCryptoError as Error};
use crate::{content_key, metadata, NetEaseCryptoError as Error, NetEaseCryptoError};
use byteorder::{ByteOrder, LE};
use std::cmp::max;
use std::io::Read;
const CRC32: crc::Crc<u32> = crc::Crc::<u32>::new(&crc::CRC_32_ISO_HDLC);
@@ -97,10 +99,10 @@ impl NCMFile {
let offset = offset + 1;
let cover_frame_len = LE::read_u32(&header[offset..offset + 4]) as usize;
let offset = offset + 4;
if header.len() < offset + cover_frame_len + 4 {
Err(Error::HeaderTooSmall(offset + cover_frame_len + 4))?;
}
let offset = offset + 4;
let image1_len = LE::read_u32(&header[offset..offset + 4]) as usize;
if image1_len > cover_frame_len {
@@ -110,6 +112,7 @@ impl NCMFile {
})?;
}
let offset = offset + 4;
let image2_len = cover_frame_len - image1_len;
let image1 = match image1_len {
0 => None,
@@ -135,6 +138,36 @@ impl NCMFile {
})
}
pub fn new_from_readable<T>(file: &mut T) -> Result<Self, Error>
where
T: Read + ?Sized,
{
let mut total_bytes_read = 0;
let mut next_read_len = 4096;
let mut header = vec![];
let ncm = loop {
header.resize(total_bytes_read + next_read_len, 0);
let this_read = file
.read(&mut header[total_bytes_read..])
.map_err(NetEaseCryptoError::FileIOError)?;
if this_read == 0 {
Err(NetEaseCryptoError::FileIOErrorReadZero)?;
}
total_bytes_read += this_read;
match NCMFile::new(&header) {
Ok(ncm) => break ncm,
Err(NetEaseCryptoError::HeaderTooSmall(expected_len)) => {
next_read_len = max(expected_len - total_bytes_read, 4096);
}
Err(err) => Err(err)?,
};
};
Ok(ncm)
}
pub fn get_metadata(&self) -> Result<Vec<u8>, Error> {
metadata::decrypt(&self.metadata)
}
@@ -145,18 +178,14 @@ impl NCMFile {
///
/// * `data`: The data to decrypt
/// * `offset`: Offset of the data in file, subtract `self.audio_data_offset`
///
/// returns: Result<(), NetEaseCryptoError>
pub fn decrypt<T>(&self, data: &mut T, offset: usize) -> Result<(), Error>
pub fn decrypt<T>(&self, data: &mut T, offset: usize)
where
T: AsMut<[u8]>,
T: AsMut<[u8]> + ?Sized,
{
let key_stream = self.audio_rc4_key_stream.iter().cycle().skip(offset & 0xff);
for (datum, &key) in data.as_mut().iter_mut().zip(key_stream) {
*datum ^= key;
}
Ok(())
}
}

View File

@@ -11,6 +11,11 @@ pub enum NetEaseCryptoError {
#[error("Header need at least {0} more bytes")]
HeaderTooSmall(usize),
#[error("File I/O Error: {0}")]
FileIOError(std::io::Error),
#[error("File I/O Error: Read 0 bytes")]
FileIOErrorReadZero,
#[error("Not a NCM file")]
NotNCMFile,
#[error("Invalid NCM checksum. Expected {expected:08x}, actual: {expected:08x}")]