feat: added glue exports for kwm/qmc

This commit is contained in:
鲁树人
2024-09-07 13:13:39 +01:00
parent e7d8231474
commit 3292ad51ea
16 changed files with 398 additions and 241 deletions

View File

@@ -16,6 +16,7 @@ default = ["console_error_panic_hook"]
[dependencies]
wasm-bindgen = "0.2.84"
anyhow = "1.0.86"
getrandom = { version = "0.2", features = ["js"] }
# The `console_error_panic_hook` crate provides better debugging of panics by
# logging them with `console.error`. This is great for development, but requires

View File

@@ -1,9 +1,70 @@
use crate::errors::map_js_error;
use crate::exports::qmc::JsQMC2;
use umc_kuwo::kwm_v1::CipherV1;
use umc_kuwo::{Cipher, Header};
use wasm_bindgen::prelude::wasm_bindgen;
use wasm_bindgen::JsError;
#[wasm_bindgen(js_name=kuwoDecodeEKey)]
pub fn decode_ekey(ekey: &str) -> Result<String, JsError> {
let ekey = umc_kuwo::des::decode_ekey(ekey, &umc_kuwo::SECRET_KEY).map_err(map_js_error)?;
Ok(ekey)
/// Kuwo KWM file header.
#[wasm_bindgen(js_name=KuwoHeader)]
pub struct JsKuwoHeader(Header);
#[wasm_bindgen(js_class = KuwoHeader)]
impl JsKuwoHeader {
/// Parse the KuWo header (0x400 bytes)
pub fn parse(header: &[u8]) -> Result<JsKuwoHeader, JsError> {
let hdr = Header::from_bytes(header).map_err(map_js_error)?;
Ok(JsKuwoHeader(hdr))
}
/// Get quality id (used for Android Kuwo APP),
/// that can be then used to extract ekey from mmkv db.
#[wasm_bindgen(getter, js_name=qualityId)]
pub fn quality_id(&self) -> u32 {
self.0.get_quality_id()
}
/// Create an instance of cipher (decipher) for decryption
#[wasm_bindgen(js_name=makeCipher)]
pub fn make_cipher(&self, ekey: Option<String>) -> Result<JsCipher, JsError> {
let cipher = self.0.get_cipher(ekey).map_err(map_js_error)?;
Ok(JsCipher(cipher))
}
}
/// Create a decipher instance for "BoDian Music".
#[wasm_bindgen(js_name=kuwoBodianCipherFactory)]
pub fn js_kuwo_bodian_cipher_factory(ekey: &str) -> Result<JsQMC2, JsError> {
let ekey = umc_kuwo::des::decode_ekey(ekey, &umc_kuwo::SECRET_KEY).map_err(map_js_error)?;
JsQMC2::new(ekey.as_str())
}
/// Kuwo KWM v1
#[wasm_bindgen(js_name=KWMCipherV1)]
pub struct JsCipherV1(CipherV1);
#[wasm_bindgen(js_class=KWMCipherV1)]
impl JsCipherV1 {
/// Create a decipher instance for "Kuwo KWM v1".
pub fn decrypt(&self, buffer: &mut [u8], offset: usize) {
self.0.decrypt(buffer, offset)
}
}
/// Create a decipher instance for "Kuwo KWM v2".
#[wasm_bindgen(js_name=kuwoV2CipherFactory)]
pub fn js_kuwo_v2_cipher_factory(ekey: &str) -> Result<JsQMC2, JsError> {
JsQMC2::new(ekey)
}
/// Common V1/V2 wrapper interface, derived from `KuwoHeader.makeCipher`
#[wasm_bindgen(js_name=KWMCipher)]
pub struct JsCipher(Cipher);
#[wasm_bindgen(js_class=KWMCipher)]
impl JsCipher {
/// Decrypt buffer at given offset.
pub fn decrypt(&self, buffer: &mut [u8], offset: usize) {
self.0.decrypt(buffer, offset)
}
}

View File

@@ -1 +1,2 @@
pub mod kuwo;
pub mod qmc;

View File

@@ -0,0 +1,60 @@
use crate::errors::map_js_error;
use umc_qmc::footer::FooterParseError;
use umc_qmc::QMCv2Cipher;
use wasm_bindgen::prelude::wasm_bindgen;
use wasm_bindgen::JsError;
/// QMC1 (qmcflac) decipher, decrypt buffer at given offset.
#[wasm_bindgen(js_name=decryptQMC1)]
pub fn js_decrypt_qmc1(buffer: &mut [u8], offset: usize) {
umc_qmc::v1::decrypt(buffer, offset)
}
/// QMC2 (mgg/mflac) cipher
#[wasm_bindgen(js_name=QMC2)]
pub struct JsQMC2(QMCv2Cipher);
#[wasm_bindgen(js_class=QMC2)]
impl JsQMC2 {
/// Create a new QMC2 (mgg/mflac) cipher instance.
#[wasm_bindgen(constructor)]
pub fn new(ekey: &str) -> Result<JsQMC2, JsError> {
let cipher = QMCv2Cipher::new(ekey.as_bytes()).map_err(map_js_error)?;
Ok(JsQMC2(cipher))
}
/// Decrypt buffer at given offset.
pub fn decrypt(&self, buffer: &mut [u8], offset: usize) {
self.0.decrypt(buffer, offset)
}
}
/// QMC Footer.
#[wasm_bindgen(js_name=QMCFooter)]
pub struct JsQMCFooter(umc_qmc::footer::Metadata);
#[wasm_bindgen(js_class=QMCFooter)]
impl JsQMCFooter {
/// Parse QMC Footer from byte slice.
/// Recommended to slice the last 1024 bytes of the file.
pub fn parse(footer: &[u8]) -> Result<Option<JsQMCFooter>, JsError> {
match umc_qmc::footer::from_byte_slice(footer) {
Ok(Some(metadata)) => Ok(Some(JsQMCFooter(metadata))),
Ok(None) => Ok(None),
Err(FooterParseError::PCv1EKeyTooLarge(_)) => Ok(None),
Err(err) => Err(err.into()),
}
}
/// Get eKey (if embedded)
#[wasm_bindgen(getter)]
pub fn ekey(&self) -> Option<String> {
self.0.ekey.clone()
}
/// Get size of footer
#[wasm_bindgen(getter)]
pub fn size(&self) -> usize {
self.0.size
}
}

View File

@@ -5,17 +5,8 @@ mod utils;
use utils::set_panic_hook;
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
extern "C" {
fn alert(s: &str);
}
/// Init panic hook
#[wasm_bindgen]
pub fn init() {
set_panic_hook();
}
#[wasm_bindgen]
pub fn greet() {
alert("hello world!");
}