- 修改默认线程数为cpu核心数

- 修改多线程通信为crossbeam-channel库,增加通讯性能
 - ⬆️ 升级依赖
 - 🔨 优化解密算法,提高解密效率
This commit is contained in:
lkhsss
2025-08-12 22:26:46 +08:00
parent 3ad5be3a01
commit e7ecb37878
12 changed files with 178 additions and 145 deletions

View File

@ -68,3 +68,15 @@
- 修正了解密完成后不会自动退出的bug - 修正了解密完成后不会自动退出的bug
### Refactoring ### Refactoring
- :hammer: 重构部分代码! - :hammer: 重构部分代码!
## [2.7.20] - 2025.8.12
### Features :sparkles:
- 修改默认线程数为cpu核心数
- 修改多线程通信为crossbeam-channel库增加通讯性能
### Fixed :bug:
- :arrow_up: 升级依赖
- 修正了trace级别日志输出时显示debug级别的问题
### Refactoring
- :hammer: 优雅处理所有的错误
- :hammer: 将代码分离为单个文件
- :hammer: 优化解密算法,提高解密效率

30
Cargo.lock generated
View File

@ -375,6 +375,15 @@ dependencies = [
"cfg-if", "cfg-if",
] ]
[[package]]
name = "crossbeam-channel"
version = "0.5.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2"
dependencies = [
"crossbeam-utils",
]
[[package]] [[package]]
name = "crossbeam-deque" name = "crossbeam-deque"
version = "0.8.6" version = "0.8.6"
@ -540,6 +549,12 @@ version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "hermit-abi"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c"
[[package]] [[package]]
name = "hex" name = "hex"
version = "0.4.3" version = "0.4.3"
@ -811,7 +826,7 @@ checksum = "07dcca13d1740c0a665f77104803360da0bdb3323ecce2e93fa2c959a6d52806"
[[package]] [[package]]
name = "ncmmiao" name = "ncmmiao"
version = "2.7.11" version = "2.7.20"
dependencies = [ dependencies = [
"aes", "aes",
"audiotags", "audiotags",
@ -819,12 +834,13 @@ dependencies = [
"chrono", "chrono",
"clap", "clap",
"colored", "colored",
"crossbeam-channel",
"env_logger", "env_logger",
"image", "image",
"indicatif", "indicatif",
"lazy_static", "lazy_static",
"log", "log",
"rayon", "num_cpus",
"serde", "serde",
"serde_derive", "serde_derive",
"serde_json", "serde_json",
@ -903,6 +919,16 @@ dependencies = [
"autocfg", "autocfg",
] ]
[[package]]
name = "num_cpus"
version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b"
dependencies = [
"hermit-abi",
"libc",
]
[[package]] [[package]]
name = "number_prefix" name = "number_prefix"
version = "0.4.0" version = "0.4.0"

View File

@ -1,6 +1,6 @@
[package] [package]
name = "ncmmiao" name = "ncmmiao"
version = "2.7.11" version = "2.7.20"
edition = "2021" edition = "2021"
authors = ["Lkhsss <lkhsss1019@gmail.com>"] authors = ["Lkhsss <lkhsss1019@gmail.com>"]
description = "A magic tool convert ncm to flac" description = "A magic tool convert ncm to flac"
@ -18,12 +18,13 @@ base64 = {version = "0.22.*"}
chrono = {version = "0.4.*",features = ["clock"],default-features = false} chrono = {version = "0.4.*",features = ["clock"],default-features = false}
clap = { version = "4.5.*", features = ["derive","std"]} clap = { version = "4.5.*", features = ["derive","std"]}
colored = {version = "3.0.0",default-features = false} colored = {version = "3.0.0",default-features = false}
crossbeam-channel = "0.5.15"
env_logger = {version = "0.11.7",default-features = false} env_logger = {version = "0.11.7",default-features = false}
image = "0.25.*" image = "0.25.*"
indicatif = "0.17.9" indicatif = "0.17.9"
lazy_static = "1.5.0" lazy_static = "1.5.0"
log = "0.4.20" log = "0.4.20"
rayon = "1.10.0" num_cpus = "1.17.0"
serde = { version = "1.0.195", features = ["derive"] } serde = { version = "1.0.195", features = ["derive"] }
serde_derive = "1.0.195" serde_derive = "1.0.195"
serde_json = "1.0.111" serde_json = "1.0.111"

View File

@ -44,13 +44,7 @@ Options:
- [ ] 优化并发设置 - [ ] 优化并发设置
- [ ] 优化信息传递 - [ ] 优化信息传递
# 性能对比
|线程池框架|数量|时间ms|
|-|-|-|
|自制|16|1470|
|自制|8|1514|
|自制|8|1573|
|自制|8|1448|
--- ---
# [Changelog](CHANGELOG.md) # [Changelog](CHANGELOG.md)
--- ---

View File

@ -5,7 +5,7 @@ use clap::Parser;
#[command(author = "lkhsss")] #[command(author = "lkhsss")]
#[command(version,about = "一个解密ncm文件的神秘程序 By Lkhsss", long_about = None)] #[command(version,about = "一个解密ncm文件的神秘程序 By Lkhsss", long_about = None)]
pub struct Cli { pub struct Cli {
/// 并发的最大线程数,默认为8线程 /// 并发的最大线程数,默认为cpu核心数
#[arg(short, long)] #[arg(short, long)]
pub workers: Option<usize>, pub workers: Option<usize>,
/// 需要解密的文件夹或文件 /// 需要解密的文件夹或文件
@ -23,4 +23,7 @@ pub struct Cli {
/// 自动打开输出目录 /// 自动打开输出目录
#[arg(short, long, name = "自动打开输出目录")] #[arg(short, long, name = "自动打开输出目录")]
pub autoopen: bool, pub autoopen: bool,
#[arg(short, long, action = clap::ArgAction::Count)]
pub debug: u8,
} }

View File

@ -1,7 +1,7 @@
use colored::Color::{Cyan, Green, Red, Yellow,Magenta}; use colored::Color::{Cyan, Green, Red, Yellow,Magenta};
use colored::Colorize; use colored::Colorize;
use indicatif::MultiProgress; use indicatif::MultiProgress;
use log::{LevelFilter, Log, Metadata, Record, SetLoggerError}; use log::{Log, Metadata, Record, SetLoggerError};
use std::sync::Arc; use std::sync::Arc;
// 自定义Logger将日志发送到MultiProgress // 自定义Logger将日志发送到MultiProgress
@ -42,10 +42,6 @@ pub fn init_logger() -> Result<(), SetLoggerError> {
mp: crate::MP.clone(), mp: crate::MP.clone(),
}; };
log::set_boxed_logger(Box::new(logger))?; log::set_boxed_logger(Box::new(logger))?;
if cfg!(debug_assertions) {
log::set_max_level(LevelFilter::Trace);//FIXME
} else {
log::set_max_level(LevelFilter::Info);
}
Ok(()) Ok(())
} }

View File

@ -1,20 +1,13 @@
#![deny(clippy::all)]
use ::clap::Parser; use ::clap::Parser;
use colored::{Color, Colorize}; use colored::{Color, Colorize};
use crossbeam_channel::{bounded, Sender};
use indicatif::{MultiProgress, ProgressBar, ProgressStyle}; use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
use lazy_static::lazy_static; use lazy_static::lazy_static;
use log::{error, info, trace, warn}; use log::{error, info, warn, LevelFilter};
use messager::Message; use messager::{Message, Messager, Signals};
use rayon::ThreadPoolBuilder;
use std::process::exit; use std::process::exit;
use std::time::Duration; use std::time::Duration;
use std::{ use std::{path::Path, sync::Arc};
path::Path,
sync::{
mpsc::{self, Sender},
Arc, Mutex,
},
};
mod apperror; mod apperror;
mod clap; mod clap;
@ -29,7 +22,7 @@ mod time;
use apperror::AppError; use apperror::AppError;
use ncmdump::Ncmfile; use ncmdump::Ncmfile;
use time::TimeCompare; use time::TimeCompare;
const DEFAULT_MAXWORKER: usize = 8;
fn main() -> Result<(), AppError> { fn main() -> Result<(), AppError> {
let timer = match TimeCompare::new() { let timer = match TimeCompare::new() {
@ -49,6 +42,8 @@ fn main() -> Result<(), AppError> {
let cli = clap::Cli::parse(); let cli = clap::Cli::parse();
//获取cpu核心数
let cpus = num_cpus::get();
// 最大线程数 // 最大线程数
let max_workers = match cli.workers { let max_workers = match cli.workers {
Some(n) => { Some(n) => {
@ -58,7 +53,7 @@ fn main() -> Result<(), AppError> {
1 1
} }
} }
None => DEFAULT_MAXWORKER, None => cpus,//默认使用cpu核心数作为线程数
}; };
//输入目录 //输入目录
let input = cli.input; let input = cli.input;
@ -69,11 +64,23 @@ fn main() -> Result<(), AppError> {
if forcesave { if forcesave {
warn!("文件{}已开启!", "强制覆盖".bright_red()) warn!("文件{}已开启!", "强制覆盖".bright_red())
} }
let level = match cli.debug {
0 | 3 => LevelFilter::Info,
1 => LevelFilter::Error,
2 => LevelFilter::Warn,
4 => LevelFilter::Debug,
5 => LevelFilter::Trace,
_ => LevelFilter::Off,
};
info!("日志等级:{}", level.to_string());
log::set_max_level(level);
let undumpfile = pathparse::pathparse(input); // 该列表将存入文件的路径 let undumpfile = pathparse::pathparse(input); // 该列表将存入文件的路径
let taskcount = undumpfile.len(); let taskcount = undumpfile.len();
let mut success_count = 0; //成功任务数 let mut success_count = 0; //成功任务数
let mut ignore_count = 0; //忽略的任务数
let mut failure_count = 0; //发生错误的
if taskcount == 0 { if taskcount == 0 {
error!("没有找到有效文件。使用-i参数输入需要解密的文件或文件夹。"); error!("没有找到有效文件。使用-i参数输入需要解密的文件或文件夹。");
@ -81,40 +88,34 @@ fn main() -> Result<(), AppError> {
}; };
// 初始化线程池 // 初始化线程池
let pool = threadpool::Pool::new(max_workers); let pool = threadpool::Pool::new(max_workers);
// let threadpoolbuilder = ThreadPoolBuilder::new()
// .num_threads(max_workers)
// .build()
// .unwrap(); //初始化线程池
// //FIXME暂时unwrap
info!( info!(
"将启用{}线程", "将启用{}线程",
max_workers.to_string().color(Color::BrightGreen) max_workers.to_string().color(Color::BrightGreen)
); );
// 初始化通讯 // 初始化通讯
let (tx, rx) = mpsc::channel(); // let (tx, rx) = mpsc::channel();
let (tx, rx) = bounded(taskcount * 6);
// 循环开始 // 循环开始
for filepath in undumpfile { for filepath in undumpfile {
let output = outputdir.clone(); let output = outputdir.clone();
let senderin: Sender<messager::Message> = tx.clone(); let senderin: Sender<Message> = tx.clone();
let senderon: Sender<messager::Message> = tx.clone(); let senderon: Sender<Message> = tx.clone();
// 多线程 // 多线程
pool.execute(move || match Ncmfile::new(filepath.as_str()) { pool.execute(move || match Ncmfile::new(filepath.as_str()) {
Ok(mut n) => match n.dump(Path::new(&output), senderin, forcesave) { Ok(mut n) => match n.dump(Path::new(&output), senderin, forcesave) {
Ok(_) => {} Ok(_) => {}
Err(e) => { Err(e) => {
let messager = messager::Messager::new(n.fullfilename, senderon); let messager = Messager::new(n.fullfilename, senderon);
let _ = messager.send(messager::Signals::Err(e)); let _ = messager.send(Signals::Err(e));
} }
}, },
Err(e) => { Err(e) => {
let messager = messager::Messager::new(filepath, senderon); let messager = Messager::new(filepath, senderon);
let _ = messager.send(messager::Signals::Err(e)); let _ = messager.send(Signals::Err(e));
} }
}); });
// threadpoolbuilder.install(|| {
// //TODO
// })
} }
//循环到此结束 //循环到此结束
//进度条 //进度条
@ -134,12 +135,14 @@ fn main() -> Result<(), AppError> {
// 接受消息!!!!!!!!!! // 接受消息!!!!!!!!!!
for messages in rx { for messages in rx {
match messages.signal { match messages.signal {
messager::Signals::End | messager::Signals::Err(_) => success_count += 1, Signals::End => success_count += 1,
Signals::Err(AppError::ProtectFile) => ignore_count += 1,
Signals::Err(_) => failure_count += 1,
_ => (), _ => (),
} }
if success_count < taskcount { if (success_count+ignore_count+failure_count) < taskcount {
progressbar.inc(1); progressbar.inc(1);
messages.log(); //发送log // messages.log(); //发送log
} else { } else {
break; break;
} }
@ -155,9 +158,10 @@ fn main() -> Result<(), AppError> {
} }
}; };
info!( info!(
"成功解密{}个文件,{}个文件解密失败,{}", "成功解密{}个文件,跳过{}个文件,{}个文件解密失败,{}",
success_count.to_string().bright_green(), success_count.to_string().bright_green(),
(taskcount - success_count).to_string().bright_red(), ignore_count.to_string().purple(),
failure_count.to_string().bright_red(),
showtime() showtime()
); );

View File

@ -1,39 +1,35 @@
use colored::Colorize;
use log::{error, info, warn};
use crate::{messager, ncmdump,AppError};
use crate::{messager, AppError};
use std::fmt::Debug; use std::fmt::Debug;
use std::sync::mpsc; // use
pub struct Messager { pub struct Messager {
name: String, name: String,
sender: mpsc::Sender<messager::Message>, sender: crossbeam_channel::Sender<messager::Message>,
} }
pub struct Message { pub struct Message {
pub name: String, pub name: String,
pub signal: Signals, pub signal: Signals,
} }
impl Message { // impl Message {
// 定义一个公共方法 log用于记录不同信号状态下的日志信息 // // 定义一个公共方法 log用于记录不同信号状态下的日志信息
pub fn log(&self) { // pub fn log(&self) {
let loginfo = match &self.signal {
Signals::Start => "读取文件", // match &self.signal {
Signals::GetMetaInfo => "解密歌曲元信息", // Signals::Err(e) => match e {
Signals::GetCover => "解密封面图片数据", // AppError::ProtectFile => warn!("[{}] {}", self.name.cyan(), "强制覆盖已关闭。不保存文件"),
Signals::Decrypt => "解密歌曲信息", // _ => error!("[{}] {}", self.name.cyan(), e),
Signals::Save => "保存文件", // },
Signals::End => "成功!", // Signals::Start => trace!("开始读取文件"),
Signals::Err(e) => &e.to_string(), // Signals::GetMetaInfo => trace!("解密歌曲元信息"),
}; // Signals::GetCover => trace!("解密封面图片数据"),
match &self.signal { // Signals::Decrypt => trace!("解密歌曲信息"),
Signals::Err(e) => match e { // Signals::Save => info!("保存文件"),
AppError::ProtectFile => warn!("[{}] {}", self.name.cyan(), loginfo), // Signals::End => trace!("解密流程结束")
_ => error!("[{}] {}", self.name.cyan(), loginfo), // }
}, // }
_ => info!("[{}] {}", self.name.cyan(), loginfo), // }
}
}
}
#[derive(PartialEq)] #[derive(PartialEq)]
pub enum Signals { pub enum Signals {
Start, Start,
@ -46,10 +42,10 @@ pub enum Signals {
} }
impl Messager { impl Messager {
pub fn new(name: String, sender: mpsc::Sender<messager::Message>) -> Self { pub fn new(name: String, sender: crossbeam_channel::Sender<messager::Message>) -> Self {
Self { name, sender } Self { name, sender }
} }
pub fn send(&self, s: Signals) -> Result<(), std::sync::mpsc::SendError<messager::Message>> { pub fn send(&self, s: Signals) -> Result<(), crossbeam_channel::SendError<messager::Message>> {
self.sender.send(Message { self.sender.send(Message {
name: self.name.clone(), name: self.name.clone(),
signal: s, signal: s,

View File

@ -6,8 +6,7 @@ use aes::Aes128;
use audiotags::{MimeType, Picture, Tag}; use audiotags::{MimeType, Picture, Tag};
use base64::{self, Engine}; use base64::{self, Engine};
use colored::*; use colored::*;
#[allow(unused_imports)] use log::{debug, info, trace};
use log::{debug, error, info, trace, warn};
use messager::Signals; use messager::Signals;
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use serde_json::{self, Value}; use serde_json::{self, Value};
@ -16,7 +15,6 @@ use std::fs::{self, File};
use std::io::{BufReader, BufWriter, Read, Seek, SeekFrom, Write}; use std::io::{BufReader, BufWriter, Read, Seek, SeekFrom, Write};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::str::from_utf8; use std::str::from_utf8;
use std::sync::mpsc;
use std::vec; use std::vec;
// lazy_static! { // lazy_static! {
@ -26,9 +24,13 @@ use std::vec;
// } // }
// 原KEY_CORE数据687A4852416D736F356B496E62617857 // 原KEY_CORE数据687A4852416D736F356B496E62617857
const NEW_KEY_CORE:[u8;16] = [0x68, 0x7A, 0x48, 0x52, 0x41, 0x6D, 0x73, 0x6F, 0x35, 0x6B, 0x49, 0x6E, 0x62, 0x61, 0x78, 0x57]; const NEW_KEY_CORE: [u8; 16] = [
0x68, 0x7A, 0x48, 0x52, 0x41, 0x6D, 0x73, 0x6F, 0x35, 0x6B, 0x49, 0x6E, 0x62, 0x61, 0x78, 0x57,
];
// 原KEY_META数据2331346C6A6B5F215C5D2630553C2728 // 原KEY_META数据2331346C6A6B5F215C5D2630553C2728
const NEW_KEY_META:[u8;16]=[0x23, 0x31, 0x34, 0x6C, 0x6A, 0x6B, 0x5F, 0x21, 0x5C, 0x5D, 0x26, 0x30, 0x55, 0x3C, 0x27, 0x28]; const NEW_KEY_META: [u8; 16] = [
0x23, 0x31, 0x34, 0x6C, 0x6A, 0x6B, 0x5F, 0x21, 0x5C, 0x5D, 0x26, 0x30, 0x55, 0x3C, 0x27, 0x28,
];
#[derive(Debug)] #[derive(Debug)]
#[allow(unused_variables)] #[allow(unused_variables)]
pub struct Ncmfile { pub struct Ncmfile {
@ -80,7 +82,7 @@ impl Ncmfile {
/// - length 想要读取的长度 /// - length 想要读取的长度
pub fn seekread(&mut self, length: u64) -> Result<Vec<u8>, AppError> { pub fn seekread(&mut self, length: u64) -> Result<Vec<u8>, AppError> {
if self.position + length > self.size { if self.position + length > self.size {
return Err(AppError::FileReadError); Err(AppError::FileReadError)
} else { } else {
let mut reader = BufReader::new(&self.file); let mut reader = BufReader::new(&self.file);
let _ = reader.seek(SeekFrom::Start(self.position)); let _ = reader.seek(SeekFrom::Start(self.position));
@ -100,7 +102,7 @@ impl Ncmfile {
#[allow(dead_code)] #[allow(dead_code)]
pub fn seekread_from(&mut self, offset: u64, length: u64) -> Result<Vec<u8>, AppError> { pub fn seekread_from(&mut self, offset: u64, length: u64) -> Result<Vec<u8>, AppError> {
if self.position + length > self.size { if self.position + length > self.size {
return Err(AppError::FileReadError); Err(AppError::FileReadError)
} else { } else {
let mut reader = BufReader::new(&self.file); let mut reader = BufReader::new(&self.file);
let _ = reader.seek(SeekFrom::Start(offset)); let _ = reader.seek(SeekFrom::Start(offset));
@ -122,7 +124,7 @@ impl Ncmfile {
pub fn seekread_no_error(&mut self, length: u64) -> Vec<u8> { pub fn seekread_no_error(&mut self, length: u64) -> Vec<u8> {
if self.position + length > self.size { if self.position + length > self.size {
if self.position >= self.size { if self.position >= self.size {
return vec![]; vec![]
} else { } else {
let mut reader = BufReader::new(&self.file); let mut reader = BufReader::new(&self.file);
let _ = reader.seek(SeekFrom::Start(self.position)); let _ = reader.seek(SeekFrom::Start(self.position));
@ -130,7 +132,7 @@ impl Ncmfile {
let mut buf: Vec<u8> = vec![0; (self.size - self.position) as usize]; let mut buf: Vec<u8> = vec![0; (self.size - self.position) as usize];
let _ = reader.read_exact(&mut buf); let _ = reader.read_exact(&mut buf);
self.position += length; self.position += length;
return buf[..].to_vec(); buf[..].to_vec()
} }
} else { } else {
let mut reader = BufReader::new(&self.file); let mut reader = BufReader::new(&self.file);
@ -144,7 +146,7 @@ impl Ncmfile {
/// 跳过某些数据 /// 跳过某些数据
pub fn skip(&mut self, length: u64) -> Result<(), AppError> { pub fn skip(&mut self, length: u64) -> Result<(), AppError> {
if self.position + length > self.size { if self.position + length > self.size {
return Err(AppError::FileReadError); Err(AppError::FileReadError)
} else { } else {
self.position += length; self.position += length;
Ok(()) Ok(())
@ -152,8 +154,8 @@ impl Ncmfile {
} }
///按字节进行0x64异或。 ///按字节进行0x64异或。
fn parse_key(key: &mut [u8]) -> &[u8] { fn parse_key(key: &mut [u8]) -> &[u8] {
for i in 0..key.len() { for item in &mut *key {
key[i] ^= 0x64; *item ^= 0x64;
} }
key key
} }
@ -175,7 +177,7 @@ impl Ncmfile {
fn is_ncm(data: Vec<u8>) -> Result<(), AppError> { fn is_ncm(data: Vec<u8>) -> Result<(), AppError> {
let header = from_utf8(&data).map_err(|_| AppError::NotNcmFile)?; let header = from_utf8(&data).map_err(|_| AppError::NotNcmFile)?;
if header != "CTENFDAM" { if header != "CTENFDAM" {
return Err(AppError::NotNcmFile); Err(AppError::NotNcmFile)
} else { } else {
Ok(()) Ok(())
} }
@ -192,13 +194,12 @@ impl Ncmfile {
pub fn dump( pub fn dump(
&mut self, &mut self,
outputdir: &Path, outputdir: &Path,
tx: mpsc::Sender<messager::Message>, tx: crossbeam_channel::Sender<messager::Message>,
force_save: bool, force_save: bool,
) -> Result<(), AppError> { ) -> Result<(), AppError> {
let messager = messager::Messager::new(self.fullfilename.clone(), tx); let messager = messager::Messager::new(self.fullfilename.clone(), tx);
let _ = messager.send(Signals::Start); let _ = messager.send(Signals::Start);
//TODO 通讯合法化
// info!("开始解密[{}]文件", self.fullfilename.yellow());
// 获取magic header 应为CTENFDAM // 获取magic header 应为CTENFDAM
trace!("读取magic header"); trace!("读取magic header");
let magic_header = self.seekread(8)?; let magic_header = self.seekread(8)?;
@ -224,7 +225,8 @@ impl Ncmfile {
trace!("读取RC4密钥"); trace!("读取RC4密钥");
let mut key_data = self.seekread(key_length)?; let mut key_data = self.seekread(key_length)?;
//aes128解密 //aes128解密
let key_data = &aes128_to_slice(&NEW_KEY_CORE, Self::parse_key(&mut key_data[..]).to_vec())?; //先把密钥按照字节进行0x64异或 let key_data =
&aes128_to_slice(&NEW_KEY_CORE, Self::parse_key(&mut key_data[..]).to_vec())?; //先把密钥按照字节进行0x64异或
// RC4密钥 // RC4密钥
let key_data = Self::unpad(&key_data[..])[17..].to_vec(); //去掉neteasecloudmusic let key_data = Self::unpad(&key_data[..])[17..].to_vec(); //去掉neteasecloudmusic
@ -242,16 +244,16 @@ impl Ncmfile {
let meta_data = { let meta_data = {
let mut meta_data = self.seekread(meta_length)?; //读取源数据 let mut meta_data = self.seekread(meta_length)?; //读取源数据
//字节对0x63进行异或。 //字节对0x63进行异或。
for i in 0..meta_data.len() { for item in &mut meta_data {
meta_data[i] ^= 0x63; *item ^= 0x63;
} }
// base64解密 // base64解密
let mut decode_data = Vec::<u8>::new(); let mut decode_data = Vec::<u8>::new();
let _ = match &base64::engine::general_purpose::STANDARD if base64::engine::general_purpose::STANDARD
.decode_vec(&mut meta_data[22..], &mut decode_data) .decode_vec(&mut meta_data[22..], &mut decode_data)
.is_err()
{ {
Err(_) => return Err(AppError::CannotReadMetaInfo), return Err(AppError::CannotReadMetaInfo);
_ => (),
}; };
// aes128解密 // aes128解密
let aes_data = aes128_to_slice(&NEW_KEY_META, decode_data)?; let aes_data = aes128_to_slice(&NEW_KEY_META, decode_data)?;
@ -284,10 +286,9 @@ impl Ncmfile {
// let filename = standardize_filename(filename); // let filename = standardize_filename(filename);
debug!("文件名:{}", filename.yellow()); debug!("文件名:{}", filename.yellow());
//链级创建输出目录 //链级创建输出目录
match fs::create_dir_all(outputdir) { if fs::create_dir_all(outputdir).is_err() {
Err(_) => return Err(AppError::FileWriteError), return Err(AppError::FileWriteError);
_ => (), }
};
outputdir.join(filename) outputdir.join(filename)
}; };
@ -323,7 +324,7 @@ impl Ncmfile {
trace!("组成密码盒"); trace!("组成密码盒");
let key_box = { let key_box = {
let key_length = key_data.len(); let key_length = key_data.len();
let key_data = Vec::from(key_data); // let key_data = Vec::from(key_data);
let mut key_box = (0..=255).collect::<Vec<u8>>(); let mut key_box = (0..=255).collect::<Vec<u8>>();
let mut temp = 0; let mut temp = 0;
let mut last_byte = 0; let mut last_byte = 0;
@ -331,7 +332,7 @@ impl Ncmfile {
for i in 0..=255 { for i in 0..=255 {
let swap = key_box[i as usize] as u64; let swap = key_box[i as usize] as u64;
temp = (swap + last_byte as u64 + key_data[key_offset as usize] as u64) & 0xFF; temp = (swap + last_byte + key_data[key_offset] as u64) & 0xFF;
key_offset += 1; key_offset += 1;
if key_offset >= key_length { if key_offset >= key_length {
key_offset = 0; key_offset = 0;
@ -357,7 +358,7 @@ impl Ncmfile {
let j = i & 0xFF; let j = i & 0xFF;
chunk[i - 1] ^= key_box[(key_box[j] as usize chunk[i - 1] ^= key_box[(key_box[j] as usize
+ key_box[(key_box[j as usize] as usize + j) & 0xff] as usize) + key_box[(key_box[j] as usize + j) & 0xff] as usize)
& 0xff] & 0xff]
// chunk[i - 1] ^= key_box[(key_box[j] + key_box[(key_box[j as usize] as usize + j as usize) & 0xFF]) & 0xFF]; // chunk[i - 1] ^= key_box[(key_box[j] + key_box[(key_box[j as usize] as usize + j as usize) & 0xFF]) & 0xFF];
} }
@ -385,7 +386,7 @@ impl Ncmfile {
}; };
tag.set_album_cover(cover); //添加封面 tag.set_album_cover(cover); //添加封面
let _ = tag let _ = tag
.write_to_path(&path.to_str().ok_or(AppError::SaveError)?) .write_to_path(path.to_str().ok_or(AppError::SaveError)?)
.map_err(|_| AppError::SaveError); //保存 .map_err(|_| AppError::SaveError); //保存
} }
@ -464,7 +465,9 @@ pub struct Key {
fn convert_to_generic_arrays(input: &[u8]) -> Result<Vec<GenericArray<u8, U16>>, AppError> { fn convert_to_generic_arrays(input: &[u8]) -> Result<Vec<GenericArray<u8, U16>>, AppError> {
// 确保输入的长度是16的倍数 // 确保输入的长度是16的倍数
if input.len() % 16 == 0 {} if input.len() % 16 != 0 {
return Err(AppError::FileDataError);
}
Ok(input Ok(input
.chunks(16) .chunks(16)
@ -484,7 +487,7 @@ fn aes128_to_slice<T: AsRef<[u8]>>(key: &T, blocks: Vec<u8>) -> Result<Vec<u8>,
let mut blocks = convert_to_generic_arrays(&blocks)?; let mut blocks = convert_to_generic_arrays(&blocks)?;
// 初始化密钥 // 初始化密钥
let cipher = Aes128::new(&key); let cipher = Aes128::new(key);
// 开始解密 // 开始解密
cipher.decrypt_blocks(&mut blocks); cipher.decrypt_blocks(&mut blocks);
@ -496,24 +499,24 @@ fn aes128_to_slice<T: AsRef<[u8]>>(key: &T, blocks: Vec<u8>) -> Result<Vec<u8>,
x.push(i.to_owned()); x.push(i.to_owned());
} }
} }
Ok(x.into()) Ok(x)
} }
/// ## 规范文件名称 // ## 规范文件名称
/// 防止创建文件失败 // 防止创建文件失败
/// 符号一一对应: // 符号一一对应:
/// - \ / * ? " : < > | // - \ / * ? " : < > |
/// - _ _ ⟨ ⟩ _ // - _ _ ⟨ ⟩ _
#[allow(dead_code)] // #[allow(dead_code)]
fn standardize_filename(old_fullfilename: String) -> String { // fn standardize_filename(old_fullfilename: String) -> String {
trace!("格式化文件名"); // trace!("格式化文件名");
let mut new_fullfilename = String::from(old_fullfilename); // let mut new_fullfilename = String::from(old_fullfilename);
// debug!("规范文件名:{}", new_fullfilename); // // debug!("规范文件名:{}", new_fullfilename);
let standard = ["\\", "/", "*", "?", "\"", ":", "<", ">", "|"]; // let standard = ["\\", "/", "*", "?", "\"", ":", "<", ">", "|"];
let resolution = ["_", "_", "", "", "", "", "", "", "_"]; // let resolution = ["_", "_", "", "", "", "", "⟨", "⟩", "_"];
for i in 0..standard.len() { // for i in 0..standard.len() {
new_fullfilename = // new_fullfilename =
new_fullfilename.replace(&standard[i].to_string(), &resolution[i].to_string()); // new_fullfilename.replace(&standard[i].to_string(), &resolution[i].to_string());
} // }
new_fullfilename // new_fullfilename
} // }

View File

@ -3,9 +3,9 @@ use std::{path::PathBuf, process::Command};
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
pub fn opendir(dir: PathBuf) { pub fn opendir(dir: PathBuf) {
if let Err(_) = Command::new("explorer") if Command::new("explorer")
.arg(&dir) // <- Specify the directory you'd like to open. .arg(&dir) // <- Specify the directory you'd like to open.
.spawn() .spawn().is_err()
{ {
error!("无法打开输出文件夹:[{}]", dir.display()) error!("无法打开输出文件夹:[{}]", dir.display())
} }
@ -13,18 +13,18 @@ pub fn opendir(dir: PathBuf) {
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
pub fn opendir(dir: PathBuf) { pub fn opendir(dir: PathBuf) {
if let Err(_) = Command::new("open") if Command::new("open")
.arg(&dir) // <- Specify the directory you'd like to open. .arg(&dir) // <- Specify the directory you'd like to open.
.spawn() .spawn().is_err()
{ {
error!("无法打开输出文件夹:[{}]", dir.display()) error!("无法打开输出文件夹:[{}]", dir.display())
} }
} }
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
pub fn opendir(dir: PathBuf) { pub fn opendir(dir: PathBuf) {
if let Err(_) = Command::new("open") if Command::new("open")
.arg(&dir) // <- Specify the directory you'd like to open. .arg(&dir) // <- Specify the directory you'd like to open.
.spawn() .spawn().is_err()
{ {
error!("无法打开输出文件夹:[{}]", dir.display()) error!("无法打开输出文件夹:[{}]", dir.display())
} }

View File

@ -1,9 +1,7 @@
use log::{debug, error, warn}; use log::{debug, error};
use std::path::Path; use std::path::Path;
use walkdir::WalkDir; use walkdir::WalkDir;
use crate::apperror::AppError;
pub fn pathparse(input: Vec<String>) -> Vec<String> { pub fn pathparse(input: Vec<String>) -> Vec<String> {
let mut undumpfile = Vec::new(); // 该列表将存入文件的路径 let mut undumpfile = Vec::new(); // 该列表将存入文件的路径
// 遍历输入的每一个路径参数 // 遍历输入的每一个路径参数

View File

@ -58,8 +58,8 @@ impl Pool {
} }
Pool { Pool {
workers: workers, workers,
max_workers: max_workers, max_workers,
sender: tx, sender: tx,
} }
} }