From 55772dec318a8b571ba88506fd5f6ce82bc93d6b Mon Sep 17 00:00:00 2001 From: Jixun Wu Date: Sat, 25 Dec 2021 12:07:14 +0000 Subject: [PATCH] feat(QMCv2): Allow extraction of songId from QMC2-wasm (cherry picked from commit 9ca2d852ce713255caeb8424a2724cb936434f18) --- src/decrypt/qmc.ts | 26 +++++++++++++++----------- src/decrypt/qmc_wasm.ts | 33 +++++++++++++++++++++++++++++---- 2 files changed, 44 insertions(+), 15 deletions(-) diff --git a/src/decrypt/qmc.ts b/src/decrypt/qmc.ts index d790d41..a04c901 100644 --- a/src/decrypt/qmc.ts +++ b/src/decrypt/qmc.ts @@ -3,6 +3,7 @@ import { AudioMimeType, GetArrayBuffer, SniffAudioExt } from '@/decrypt/utils'; import { DecryptResult } from '@/decrypt/entity'; import { QmcDeriveKey } from '@/decrypt/qmc_key'; +import { DecryptQMCWasm } from '@/decrypt/qmc_wasm'; import { extractQQMusicMeta } from '@/utils/qm_meta'; interface Handler { @@ -41,17 +42,20 @@ export async function Decrypt(file: Blob, raw_filename: string, raw_ext: string) const fileBuffer = await GetArrayBuffer(file); let musicDecoded: Uint8Array | undefined; - let musicID: number | undefined; - - // todo: wasm decoder doesn't support extract the song id for .mgg1/.mflac0 currently - // if (version === 2 && globalThis.WebAssembly) { - // console.log('qmc: using wasm decoder'); - // const v2Decrypted = await DecryptQMCWasm(fileBuffer); - // // 如果 v2 检测失败,降级到 v1 再尝试一次 - // if (v2Decrypted) { - // musicDecoded = v2Decrypted; - // } - // } + let musicID: number | string | undefined; + + if (version === 2 && globalThis.WebAssembly) { + console.log('qmc: using wasm decoder'); + + const v2Decrypted = await DecryptQMCWasm(fileBuffer); + // 若 v2 检测失败,降级到 v1 再尝试一次 + if (v2Decrypted.success) { + musicDecoded = v2Decrypted.data; + musicID = v2Decrypted.songId; + } else { + console.warn('qmc2-wasm failed with error %s', v2Decrypted.error || '(no error)'); + } + } if (!musicDecoded) { // may throw error diff --git a/src/decrypt/qmc_wasm.ts b/src/decrypt/qmc_wasm.ts index bec030e..e83ead3 100644 --- a/src/decrypt/qmc_wasm.ts +++ b/src/decrypt/qmc_wasm.ts @@ -1,5 +1,6 @@ import QMCCryptoModule from '@jixun/qmc2-crypto/QMC2-wasm-bundle'; import { MergeUint8Array } from '@/utils/MergeUint8Array'; +import { QMCCrypto } from '@jixun/qmc2-crypto/QMCCrypto'; // 检测文件末端使用的缓冲区大小 const DETECTION_SIZE = 40; @@ -7,14 +8,22 @@ const DETECTION_SIZE = 40; // 每次处理 2M 的数据 const DECRYPTION_BUF_SIZE = 2 * 1024 * 1024; +export interface QMC2DecryptionResult { + success: boolean; + data: Uint8Array; + songId: string | number; + error: string; +} + /** * 解密一个 QMC2 加密的文件。 * * 如果检测并解密成功,返回解密后的 Uint8Array 数据。 * @param {ArrayBuffer} mggBlob 读入的文件 Blob - * @return {Promise} */ -export async function DecryptQMCWasm(mggBlob: ArrayBuffer) { +export async function DecryptQMCWasm(mggBlob: ArrayBuffer): Promise { + const result: QMC2DecryptionResult = { success: false, data: new Uint8Array(), songId: 0, error: '' }; + // 初始化模组 const QMCCrypto = await QMCCryptoModule(); @@ -34,12 +43,26 @@ export async function DecryptQMCWasm(mggBlob: ArrayBuffer) { const position = QMCCrypto.getValue(pDetectionResult, 'i32'); const len = QMCCrypto.getValue(pDetectionResult + 4, 'i32'); + result.success = detectOK; + result.error = QMCCrypto.UTF8ToString( + pDetectionResult + QMCCrypto.offsetof_error_msg(), + QMCCrypto.sizeof_error_msg(), + ); + const songId = QMCCrypto.UTF8ToString(pDetectionResult + QMCCrypto.offsetof_song_id(), QMCCrypto.sizeof_song_id()); + if (!songId) { + console.debug('qmc2-wasm: songId not found'); + } else if (/^\d+$/.test(songId)) { + result.songId = songId; + } else { + console.warn('qmc2-wasm: Invalid songId: %s', songId); + } + // 释放内存 QMCCrypto._free(pDetectionBuf); QMCCrypto._free(pDetectionResult); if (!detectOK) { - return false; + return result; } // 计算解密后文件的大小。 @@ -75,5 +98,7 @@ export async function DecryptQMCWasm(mggBlob: ArrayBuffer) { QMCCrypto._free(buf); hCrypto.delete(); - return MergeUint8Array(decryptedParts); + result.data = MergeUint8Array(decryptedParts); + + return result; }