更新进度条。更新覆盖保存开关。
Some checks failed
Release / Publish to Github Releases (, macos-latest, aarch64-apple-darwin, true) (push) Has been cancelled
Release / Publish to Github Releases (, macos-latest, x86_64-apple-darwin) (push) Has been cancelled
Release / Publish to Github Releases (, ubuntu-latest, aarch64-unknown-linux-musl, true) (push) Has been cancelled
Release / Publish to Github Releases (, ubuntu-latest, arm-unknown-linux-musleabihf, true) (push) Has been cancelled
Release / Publish to Github Releases (, ubuntu-latest, armv7-unknown-linux-musleabihf, true) (push) Has been cancelled
Release / Publish to Github Releases (, ubuntu-latest, i686-unknown-linux-musl, true) (push) Has been cancelled
Release / Publish to Github Releases (, ubuntu-latest, x86_64-unknown-linux-musl, true) (push) Has been cancelled
Release / Publish to Github Releases (, windows-latest, aarch64-pc-windows-msvc, true) (push) Has been cancelled
Release / Publish to Github Releases (, windows-latest, i686-pc-windows-msvc, true) (push) Has been cancelled
Release / Publish to Github Releases (, windows-latest, x86_64-pc-windows-msvc) (push) Has been cancelled
Release / Publish to crates.io (push) Has been cancelled

This commit is contained in:
Lkhsss
2025-01-07 00:34:16 +08:00
parent 2f54a6727b
commit ad3436d405
8 changed files with 264 additions and 214 deletions

View File

@ -9,9 +9,14 @@ pub struct Cli {
#[arg(short, long)]
pub workers: Option<usize>,
/// 需要解密的文件夹或文件
#[arg(short, long, name = "输入文件/文件夹")]
#[arg(short, long, name = "输入文件/目录")]
pub input: Vec<String>,
#[arg(short, long, name = "输出文件夹", default_value = "NcmmiaoOutput")]
/// 输出目录
#[arg(short, long, name = "输出目录", default_value = "NcmmiaoOutput")]
pub output: Option<String>,
/// 强制覆盖保存开关
#[arg(short,long,name="强制覆盖开关")]
pub forcesave:bool
}

View File

@ -1,7 +1,15 @@
use std::{path::Path, sync::{Arc, Mutex}};
use ::clap::Parser;
#[allow(unused_imports)]
use log::{error, info, warn};
use messager::Signals;
use std::time::Duration;
use std::{
path::Path,
sync::{
mpsc::{self, Sender},
Arc, Mutex,
},
};
use colored::Colorize;
@ -9,6 +17,7 @@ use walkdir::WalkDir; //遍历目录
mod clap;
mod logger;
mod messager;
mod ncmdump;
mod threadpool;
use ncmdump::Ncmfile;
@ -36,6 +45,10 @@ fn main() {
let input = cli.input;
let outputdir = cli.output.unwrap();
let forcesave = cli.forcesave;
if forcesave{
warn!("文件强制覆盖已开启!")
}
let mut undumpfile = Vec::new(); // 该列表将存入文件的路径
@ -79,22 +92,56 @@ fn main() {
// 初始化线程池
let pool = threadpool::Pool::new(max_workers);
info!("启用{}线程", max_workers);
// 初始化通讯
let (tx, rx) = mpsc::channel();
// 循环开始
for filepath in undumpfile {
let output = outputdir.clone();
let successful = Arc::clone(&successful);
pool.execute(move || {
match Ncmfile::new(filepath.as_str()) {
Ok(mut n) => match n.dump(Path::new(&output)) {
Ok(_) => {
let mut num = successful.lock().unwrap();
*num += 1;},
Err(e) => error!("[{}]解密失败: {}", filepath.yellow(), e),
},
Err(e) => error!("[{}]解密失败: {}", filepath.yellow(), e),
}
let sender: Sender<messager::Message> = tx.clone();
pool.execute(move || match Ncmfile::new(filepath.as_str()) {
Ok(mut n) => match n.dump(Path::new(&output), sender,forcesave) {
Ok(_) => {
let mut num = successful.lock().unwrap();
*num += 1;
}
Err(e) => error!("[{}] 解密失败: {}", filepath.yellow(), e),
},
Err(e) => error!("[{}] 解密失败: {}", filepath.yellow(), e),
});
}
//循环到此结束
//进度条
use indicatif::ProgressBar;
let progressbar = ProgressBar::new((taskcount) as u64)
.with_elapsed(Duration::from_millis(50))
.with_message("破解中");
//接受消息
for messages in rx {
match messages.signal {
Signals::Start => {
// progressbar.inc(1);
info!("[{}] 开始读取文件", messages.name)
}
Signals::Decrypt => {
// progressbar.inc(1);
info!("[{}] 开始解密", messages.name)
}
Signals::Save => {
// progressbar.inc(1);
info!("[{}] 保存文件", messages.name)
}
Signals::End => {
progressbar.inc(1);
info!("[{}] 成功!", messages.name)
}
}
}
progressbar.finish_and_clear();
}
let timecount = timer.compare();
let showtime = || {

42
src/messager.rs Normal file
View File

@ -0,0 +1,42 @@
use crate::messager;
use std::fmt::Debug;
use std::sync::mpsc;
pub struct Messager {
name: String,
sender: mpsc::Sender<messager::Message>,
}
pub struct Message {
pub name: String,
pub signal: Signals,
}
pub enum Signals {
Start,
Decrypt,
Save,
End,
}
impl Messager {
pub fn new(name: String, sender: mpsc::Sender<messager::Message>) -> Self {
Self { name, sender }
}
pub fn send(&self, s: Signals) -> Result<(), std::sync::mpsc::SendError<messager::Message>> {
self.sender.send(Message {
name: self.name.clone(),
signal: s,
})
}
}
impl Debug for Message {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let message = match &self.signal {
Signals::Start => "开始破解".to_string(),
Signals::Decrypt => "开始解密".to_string(),
Signals::Save => "保存文件".to_string(),
Signals::End => "破解完成".to_string(),
};
write!(f, "[{}] {}", self.name, message)
}
}

View File

@ -1,3 +1,4 @@
use crate::messager;
use aes::cipher::generic_array::typenum::U16;
use aes::cipher::{generic_array::GenericArray, BlockDecrypt, KeyInit};
use aes::Aes128;
@ -8,12 +9,15 @@ use hex::decode;
use lazy_static::lazy_static;
#[allow(unused_imports)]
use log::{debug, error, info, trace, warn};
use messager::Signals;
use serde_derive::{Deserialize, Serialize};
use serde_json::{self, Value};
use std::fmt::Debug;
use std::fs::{self, File};
use std::io::{BufReader, BufWriter, Read, Seek, SeekFrom, Write};
use std::path::{Path, PathBuf};
use std::str::from_utf8;
use std::sync::mpsc;
use std::vec;
use std::time::{SystemTime, UNIX_EPOCH};
@ -144,13 +148,24 @@ impl Ncmfile {
/// 解密函数
#[allow(unused_assignments)]
pub fn dump(&mut self, outputdir: &Path) -> Result<(), NcmError> {
info!("开始解密[{}]文件", self.fullfilename.yellow());
pub fn dump(
&mut self,
outputdir: &Path,
tx: mpsc::Sender<messager::Message>,
force_save:bool
) -> Result<(), NcmError> {
let messager = messager::Messager::new(self.fullfilename.clone(), tx);
let _ = messager.send(Signals::Start);
//TODO 通讯合法化
// info!("开始解密[{}]文件", self.fullfilename.yellow());
// 获取magic header 。应为CTENFDAM
let magic_header = match self.seekread(8) {
Ok(header) => header,
Err(_e) => {
return Err(NcmError::FileReadError);
return Err(NcmError::FileReadError); //TODO去除向上传播
}
};
@ -211,12 +226,39 @@ impl Ncmfile {
Err(_) => return Err(NcmError::CannotReadMetaInfo),
};
debug!("json_data: {}", json_data);
let data: Value = match serde_json::from_str(&json_data[..]){Ok(o) => o,
let data: Value = match serde_json::from_str(&json_data[..]) {
Ok(o) => o,
Err(_) => return Err(NcmError::CannotReadMetaInfo),
}; //解析json数据
data
};
//处理文件路径
trace!("拼接文件路径");
let path = {
let filename = format!(
"{}.{}",
self.filename,
meta_data.get("format").unwrap().as_str().unwrap()
);
// let filename = standardize_filename(filename);
debug!("文件名:{}", filename.yellow());
//链级创建输出目录
match fs::create_dir_all(outputdir) {
Err(_) => return Err(NcmError::FileWriteError),
_ => (),
};
outputdir.join(filename)
};
debug!("文件路径: {:?}", path);
// 先检查是否存在
if !force_save && Path::new(&path).exists(){
return Err(NcmError::ProtectFile);
}
// 跳过4个字节的校验码
trace!("读取校验码");
// let _crc32 = u32::from_le_bytes(self.seekread(4).unwrap().try_into().unwrap()) as u64;
@ -228,8 +270,7 @@ impl Ncmfile {
// 获取图片数据的大小
trace!("获取图片数据的大小");
let image_data_length =
u32::from_le_bytes(self.seekread(4)?.try_into().unwrap()) as u64;
let image_data_length = u32::from_le_bytes(self.seekread(4)?.try_into().unwrap()) as u64;
// 读取图片,并写入文件当中
let image_data = self.seekread(image_data_length)?; //读取图片数据
@ -280,6 +321,7 @@ impl Ncmfile {
//解密音乐数据
trace!("解密音乐数据");
let _ = messager.send(Signals::Decrypt);
let mut music_data: Vec<u8> = Vec::new();
loop {
let mut chunk = self.seekread_no_error(0x8000);
@ -325,30 +367,15 @@ impl Ncmfile {
//退出循环,写入文件
//处理文件路径
trace!("拼接文件路径");
let path = {
let filename = format!(
"{}.{}",
self.filename,
meta_data.get("format").unwrap().as_str().unwrap()
);
// let filename = standardize_filename(filename);
debug!("文件名:{}", filename.yellow());
//链级创建输出目录
match fs::create_dir_all(outputdir){Err(_)=>return Err(NcmError::FileWriteError),_=>()};
outputdir.join(filename)
};
debug!("文件路径: {:?}", path);
let _ = messager.send(Signals::Save);
self.save(&path, music_data)?;
{
// 保存封面
let mut tag = match Tag::new().read_from_path(&path){
Ok(o)=>o,
Err(_)=>return Err(NcmError::CoverCannotSave)
let mut tag = match Tag::new().read_from_path(&path) {
Ok(o) => o,
Err(_) => return Err(NcmError::CoverCannotSave),
};
let cover = Picture {
mime_type: MimeType::Jpeg,
@ -368,19 +395,21 @@ impl Ncmfile {
self.fullfilename.yellow(),
"解密成功".bright_green()
);
let _ = messager.send(Signals::End);
Ok(())
}
fn save(&mut self, path: &PathBuf, data: Vec<u8>)->Result<(),NcmError> {
let music_file = match File::create(path){
Ok(o)=>o,
Err(_)=>return Err(NcmError::FileWriteError)
fn save(&mut self, path: &PathBuf, data: Vec<u8>) -> Result<(), NcmError> {
let music_file = match File::create(path) {
Ok(o) => o,
Err(_) => return Err(NcmError::FileWriteError),
};
let mut writer = BufWriter::new(music_file);
let _ = writer.write_all(&data);
// 关闭文件
match writer.flush(){
Ok(o)=>o,
Err(_)=>return Err(NcmError::FileWriteError)
match writer.flush() {
Ok(o) => o,
Err(_) => return Err(NcmError::FileWriteError),
};
Ok(())
}
@ -544,7 +573,8 @@ pub enum NcmError {
FileSkipError,
FileWriteError,
FullFilenameError,
FileNotFoundError,
FileNotFound,
ProtectFile,
}
impl std::error::Error for NcmError {}
@ -560,6 +590,7 @@ impl std::fmt::Display for NcmError {
Self::FileReadError => write!(f, "读取文件时发生错误"),
Self::FileWriteError => write!(f, "写入文件时错误"),
Self::FullFilenameError => write!(f, "文件名不符合规范"),
Self::ProtectFile=>write!(f, "已关闭文件强制覆盖且文件已存在。使用-f或-forcesave开启强制覆盖。"),
_ => write!(f, "未知错误"),
}
}