mirror of
				https://git.unlock-music.dev/um/web.git
				synced 2025-11-04 14:33:29 +08:00 
			
		
		
		
	feat(QMCv2): Allow extraction of songId from QMC2-wasm
(cherry picked from commit 9ca2d852ce713255caeb8424a2724cb936434f18)
This commit is contained in:
		
							parent
							
								
									0af8a0d714
								
							
						
					
					
						commit
						f6c34cd7ba
					
				@ -3,6 +3,7 @@ import { AudioMimeType, GetArrayBuffer, SniffAudioExt } from '@/decrypt/utils';
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import { DecryptResult } from '@/decrypt/entity';
 | 
					import { DecryptResult } from '@/decrypt/entity';
 | 
				
			||||||
import { QmcDeriveKey } from '@/decrypt/qmc_key';
 | 
					import { QmcDeriveKey } from '@/decrypt/qmc_key';
 | 
				
			||||||
 | 
					import { DecryptQMCWasm } from '@/decrypt/qmc_wasm';
 | 
				
			||||||
import { extractQQMusicMeta } from '@/utils/qm_meta';
 | 
					import { extractQQMusicMeta } from '@/utils/qm_meta';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface Handler {
 | 
					interface Handler {
 | 
				
			||||||
@ -41,17 +42,20 @@ export async function Decrypt(file: Blob, raw_filename: string, raw_ext: string)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  const fileBuffer = await GetArrayBuffer(file);
 | 
					  const fileBuffer = await GetArrayBuffer(file);
 | 
				
			||||||
  let musicDecoded: Uint8Array | undefined;
 | 
					  let musicDecoded: Uint8Array | undefined;
 | 
				
			||||||
  let musicID: number | undefined;
 | 
					  let musicID: number | string | undefined;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // todo: wasm decoder doesn't support extract the song id for .mgg1/.mflac0 currently
 | 
					  if (version === 2 && globalThis.WebAssembly) {
 | 
				
			||||||
  // if (version === 2 && globalThis.WebAssembly) {
 | 
					    console.log('qmc: using wasm decoder');
 | 
				
			||||||
  //   console.log('qmc: using wasm decoder');
 | 
					
 | 
				
			||||||
  //   const v2Decrypted = await DecryptQMCWasm(fileBuffer);
 | 
					    const v2Decrypted = await DecryptQMCWasm(fileBuffer);
 | 
				
			||||||
  //   // 如果 v2 检测失败,降级到 v1 再尝试一次
 | 
					    // 若 v2 检测失败,降级到 v1 再尝试一次
 | 
				
			||||||
  //   if (v2Decrypted) {
 | 
					    if (v2Decrypted.success) {
 | 
				
			||||||
  //     musicDecoded = v2Decrypted;
 | 
					      musicDecoded = v2Decrypted.data;
 | 
				
			||||||
  //   }
 | 
					      musicID = v2Decrypted.songId;
 | 
				
			||||||
  // }
 | 
					    } else {
 | 
				
			||||||
 | 
					      console.warn('qmc2-wasm failed with error %s', v2Decrypted.error || '(no error)');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (!musicDecoded) {
 | 
					  if (!musicDecoded) {
 | 
				
			||||||
    // may throw error
 | 
					    // may throw error
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,6 @@
 | 
				
			|||||||
import QMCCryptoModule from '@jixun/qmc2-crypto/QMC2-wasm-bundle';
 | 
					import QMCCryptoModule from '@jixun/qmc2-crypto/QMC2-wasm-bundle';
 | 
				
			||||||
import { MergeUint8Array } from '@/utils/MergeUint8Array';
 | 
					import { MergeUint8Array } from '@/utils/MergeUint8Array';
 | 
				
			||||||
 | 
					import { QMCCrypto } from '@jixun/qmc2-crypto/QMCCrypto';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 检测文件末端使用的缓冲区大小
 | 
					// 检测文件末端使用的缓冲区大小
 | 
				
			||||||
const DETECTION_SIZE = 40;
 | 
					const DETECTION_SIZE = 40;
 | 
				
			||||||
@ -7,14 +8,22 @@ const DETECTION_SIZE = 40;
 | 
				
			|||||||
// 每次处理 2M 的数据
 | 
					// 每次处理 2M 的数据
 | 
				
			||||||
const DECRYPTION_BUF_SIZE = 2 * 1024 * 1024;
 | 
					const DECRYPTION_BUF_SIZE = 2 * 1024 * 1024;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface QMC2DecryptionResult {
 | 
				
			||||||
 | 
					  success: boolean;
 | 
				
			||||||
 | 
					  data: Uint8Array;
 | 
				
			||||||
 | 
					  songId: string | number;
 | 
				
			||||||
 | 
					  error: string;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * 解密一个 QMC2 加密的文件。
 | 
					 * 解密一个 QMC2 加密的文件。
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * 如果检测并解密成功,返回解密后的 Uint8Array 数据。
 | 
					 * 如果检测并解密成功,返回解密后的 Uint8Array 数据。
 | 
				
			||||||
 * @param  {ArrayBuffer} mggBlob 读入的文件 Blob
 | 
					 * @param  {ArrayBuffer} mggBlob 读入的文件 Blob
 | 
				
			||||||
 * @return {Promise<Uint8Array|false>}
 | 
					 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
export async function DecryptQMCWasm(mggBlob: ArrayBuffer) {
 | 
					export async function DecryptQMCWasm(mggBlob: ArrayBuffer): Promise<QMC2DecryptionResult> {
 | 
				
			||||||
 | 
					  const result: QMC2DecryptionResult = { success: false, data: new Uint8Array(), songId: 0, error: '' };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // 初始化模组
 | 
					  // 初始化模组
 | 
				
			||||||
  const QMCCrypto = await QMCCryptoModule();
 | 
					  const QMCCrypto = await QMCCryptoModule();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -34,12 +43,26 @@ export async function DecryptQMCWasm(mggBlob: ArrayBuffer) {
 | 
				
			|||||||
  const position = QMCCrypto.getValue(pDetectionResult, 'i32');
 | 
					  const position = QMCCrypto.getValue(pDetectionResult, 'i32');
 | 
				
			||||||
  const len = QMCCrypto.getValue(pDetectionResult + 4, '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(pDetectionBuf);
 | 
				
			||||||
  QMCCrypto._free(pDetectionResult);
 | 
					  QMCCrypto._free(pDetectionResult);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (!detectOK) {
 | 
					  if (!detectOK) {
 | 
				
			||||||
    return false;
 | 
					    return result;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // 计算解密后文件的大小。
 | 
					  // 计算解密后文件的大小。
 | 
				
			||||||
@ -75,5 +98,7 @@ export async function DecryptQMCWasm(mggBlob: ArrayBuffer) {
 | 
				
			|||||||
  QMCCrypto._free(buf);
 | 
					  QMCCrypto._free(buf);
 | 
				
			||||||
  hCrypto.delete();
 | 
					  hCrypto.delete();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return MergeUint8Array(decryptedParts);
 | 
					  result.data = MergeUint8Array(decryptedParts);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return result;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user