mirror of
https://git.um-react.app/um/lib_um_crypto_rust.git
synced 2026-03-08 04:29:54 +00:00
[xmly] feat #5: implement xmly decipher
This commit is contained in:
@@ -5,6 +5,7 @@ pub mod kgm;
|
||||
pub mod ncm;
|
||||
pub mod qmc1;
|
||||
pub mod qmc2;
|
||||
pub mod xmly;
|
||||
|
||||
#[derive(Subcommand)]
|
||||
pub enum Commands {
|
||||
@@ -18,4 +19,6 @@ pub enum Commands {
|
||||
KGM(kgm::ArgsKGM),
|
||||
#[command(name = "joox")]
|
||||
JOOX(joox::ArgsJoox),
|
||||
#[command(name = "xmly")]
|
||||
XMLY(xmly::ArgsXimalaya),
|
||||
}
|
||||
|
||||
94
um_cli/src/cmd/xmly.rs
Normal file
94
um_cli/src/cmd/xmly.rs
Normal file
@@ -0,0 +1,94 @@
|
||||
use crate::Cli;
|
||||
use anyhow::{bail, Result};
|
||||
use clap::Args;
|
||||
use std::ffi::OsStr;
|
||||
use std::fs::File;
|
||||
use std::io;
|
||||
use std::io::{BufReader, BufWriter, Read, Seek, SeekFrom, Write};
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[derive(Debug, Clone, clap::ValueEnum)]
|
||||
enum XimalayaType {
|
||||
/// Android: *.x2m
|
||||
X2M,
|
||||
/// Android: *.x3m
|
||||
X3M,
|
||||
/// PC: *.xm
|
||||
XM,
|
||||
}
|
||||
|
||||
/// Decrypt a X2M/X3M/XM file (Ximalaya)
|
||||
#[derive(Args)]
|
||||
pub struct ArgsXimalaya {
|
||||
/// Path to output file, e.g. /export/Music/song.flac
|
||||
#[clap(short, long)]
|
||||
output: PathBuf,
|
||||
|
||||
/// Path to input file, e.g. /export/Music/song.x3m
|
||||
#[arg(name = "input")]
|
||||
input: PathBuf,
|
||||
|
||||
#[clap(short = 't', long = "type")]
|
||||
file_type: Option<XimalayaType>,
|
||||
}
|
||||
|
||||
impl ArgsXimalaya {
|
||||
pub fn run(&self, cli: &Cli) -> Result<i32> {
|
||||
let file_type = match &self.file_type {
|
||||
Some(x) => x.clone(),
|
||||
None => match self.input.extension().and_then(|ext: &OsStr| ext.to_str()) {
|
||||
Some("x2m") => XimalayaType::X2M,
|
||||
Some("x3m") => XimalayaType::X3M,
|
||||
Some("xm") => XimalayaType::XM,
|
||||
Some(ext) => bail!("invalid ext: {ext}"),
|
||||
_ => bail!("ext not found"),
|
||||
},
|
||||
};
|
||||
let mut read_stream = BufReader::with_capacity(cli.buffer_size, File::open(&self.input)?);
|
||||
let mut write_stream =
|
||||
BufWriter::with_capacity(cli.buffer_size, File::create(&self.output)?);
|
||||
|
||||
match file_type {
|
||||
XimalayaType::X2M | XimalayaType::X3M => {
|
||||
let android_type = match file_type {
|
||||
XimalayaType::X2M => umc_xmly::android::FileType::X2M,
|
||||
XimalayaType::X3M => umc_xmly::android::FileType::X3M,
|
||||
_ => bail!("this should not happen"),
|
||||
};
|
||||
let mut header = [0u8; 0x400];
|
||||
read_stream.read_exact(&mut header)?;
|
||||
umc_xmly::android::decrypt_android(android_type, &mut header);
|
||||
write_stream.write_all(&header)?;
|
||||
io::copy(&mut read_stream, &mut write_stream)?;
|
||||
}
|
||||
XimalayaType::XM => {
|
||||
let mut header = vec![0u8; 1024];
|
||||
read_stream.read_exact(&mut header)?;
|
||||
let xm_file = match umc_xmly::pc::Header::from_buffer(&header) {
|
||||
Ok(hdr) => hdr,
|
||||
Err(umc_xmly::XmlyError::MetadataTooSmall(n)) => {
|
||||
let old_size = header.len();
|
||||
header.resize(n, 0);
|
||||
read_stream.read_exact(&mut header[old_size..])?;
|
||||
umc_xmly::pc::Header::from_buffer(&header)?
|
||||
}
|
||||
Err(err) => bail!("failed to parse file: {err}"),
|
||||
};
|
||||
|
||||
// Copy header
|
||||
write_stream.write_all(xm_file.copy_m4a_header().as_slice())?;
|
||||
|
||||
// Process encrypted data
|
||||
read_stream.seek(SeekFrom::Start(xm_file.data_start_offset as u64))?;
|
||||
let mut header = vec![0u8; xm_file.encrypted_header_size];
|
||||
read_stream.read_exact(&mut header[..])?;
|
||||
write_stream.write_all(xm_file.decrypt(&mut header[..])?)?;
|
||||
|
||||
// Copy rest of the file
|
||||
io::copy(&mut read_stream, &mut write_stream)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
@@ -32,6 +32,7 @@ fn run_command(cli: &Cli) -> Result<i32> {
|
||||
Some(Commands::NCM(cmd)) => cmd.run(&cli),
|
||||
Some(Commands::KGM(cmd)) => cmd.run(&cli),
|
||||
Some(Commands::JOOX(cmd)) => cmd.run(&cli),
|
||||
Some(Commands::XMLY(cmd)) => cmd.run(&cli),
|
||||
None => {
|
||||
// https://github.com/clap-rs/clap/issues/3857#issuecomment-1161796261
|
||||
todo!("implement a sensible default command, similar to um/cli");
|
||||
|
||||
Reference in New Issue
Block a user