From 8475a5bc65a3f8439a012f6c85cda4274c23aa89 Mon Sep 17 00:00:00 2001
From: xhacker-zzz <959220793@qq.com>
Date: Thu, 19 Jan 2023 20:33:23 +0800
Subject: [PATCH 01/11] delete js implementations
---
src/decrypt/kgm.ts | 24 +---
src/decrypt/qmc.test.ts | 29 -----
src/decrypt/qmc.ts | 103 +----------------
src/decrypt/qmc_cipher.test.ts | 117 -------------------
src/decrypt/qmc_cipher.ts | 199 ---------------------------------
src/decrypt/qmc_key.test.ts | 26 -----
src/decrypt/qmc_key.ts | 127 ---------------------
src/decrypt/qmccache.ts | 13 +--
src/utils/tea.test.ts | 73 ------------
src/utils/tea.ts | 80 -------------
10 files changed, 7 insertions(+), 784 deletions(-)
delete mode 100644 src/decrypt/qmc.test.ts
delete mode 100644 src/decrypt/qmc_cipher.test.ts
delete mode 100644 src/decrypt/qmc_cipher.ts
delete mode 100644 src/decrypt/qmc_key.test.ts
delete mode 100644 src/decrypt/qmc_key.ts
delete mode 100644 src/utils/tea.test.ts
delete mode 100644 src/utils/tea.ts
diff --git a/src/decrypt/kgm.ts b/src/decrypt/kgm.ts
index ca3afa6..b6553c9 100644
--- a/src/decrypt/kgm.ts
+++ b/src/decrypt/kgm.ts
@@ -9,7 +9,6 @@ import {
import { parseBlob as metaParseBlob } from 'music-metadata-browser';
import { DecryptResult } from '@/decrypt/entity';
import { DecryptKgmWasm } from '@/decrypt/kgm_wasm';
-import { decryptKgmByteAtOffsetV2, decryptVprByteAtOffset } from '@jixun/kugou-crypto/dist/utils/decryptionHelper';
//prettier-ignore
const VprHeader = [
@@ -29,33 +28,14 @@ export async function Decrypt(file: File, raw_filename: string, raw_ext: string)
} else {
if (!BytesHasPrefix(new Uint8Array(oriData), KgmHeader)) throw Error('Not a valid kgm(a) file!');
}
- let musicDecoded: Uint8Array | undefined;
+ let musicDecoded = new Uint8Array();
if (globalThis.WebAssembly) {
- console.log('kgm: using wasm decoder');
-
const kgmDecrypted = await DecryptKgmWasm(oriData, raw_ext);
if (kgmDecrypted.success) {
musicDecoded = kgmDecrypted.data;
console.log('kgm wasm decoder suceeded');
} else {
- console.warn('KgmWasm failed with error %s', kgmDecrypted.error || '(unknown error)');
- }
- }
-
- if (!musicDecoded) {
- musicDecoded = new Uint8Array(oriData);
- let bHeaderLen = new DataView(musicDecoded.slice(0x10, 0x14).buffer);
- let headerLen = bHeaderLen.getUint32(0, true);
-
- let key1 = Array.from(musicDecoded.slice(0x1c, 0x2c));
- key1.push(0);
-
- musicDecoded = musicDecoded.slice(headerLen);
- let dataLen = musicDecoded.length;
-
- const decryptByte = raw_ext === 'vpr' ? decryptVprByteAtOffset : decryptKgmByteAtOffsetV2;
- for (let i = 0; i < dataLen; i++) {
- musicDecoded[i] = decryptByte(musicDecoded[i], key1, i);
+ throw new Error(kgmDecrypted.error || '(unknown error)');
}
}
diff --git a/src/decrypt/qmc.test.ts b/src/decrypt/qmc.test.ts
deleted file mode 100644
index 73156bd..0000000
--- a/src/decrypt/qmc.test.ts
+++ /dev/null
@@ -1,29 +0,0 @@
-import fs from 'fs';
-import { QmcDecoder } from '@/decrypt/qmc';
-import { BytesEqual } from '@/decrypt/utils';
-
-function loadTestDataDecoder(name: string): {
- cipherText: Uint8Array;
- clearText: Uint8Array;
-} {
- const cipherBody = fs.readFileSync(`./testdata/${name}_raw.bin`);
- const cipherSuffix = fs.readFileSync(`./testdata/${name}_suffix.bin`);
- const cipherText = new Uint8Array(cipherBody.length + cipherSuffix.length);
- cipherText.set(cipherBody);
- cipherText.set(cipherSuffix, cipherBody.length);
- return {
- cipherText,
- clearText: fs.readFileSync(`testdata/${name}_target.bin`),
- };
-}
-
-test('qmc: real file', async () => {
- const cases = ['mflac0_rc4', 'mflac_rc4', 'mflac_map', 'mgg_map', 'qmc0_static'];
- for (const name of cases) {
- const { clearText, cipherText } = loadTestDataDecoder(name);
- const c = new QmcDecoder(cipherText);
- const buf = c.decrypt();
-
- expect(BytesEqual(buf, clearText)).toBeTruthy();
- }
-});
diff --git a/src/decrypt/qmc.ts b/src/decrypt/qmc.ts
index 3154f90..b5b5401 100644
--- a/src/decrypt/qmc.ts
+++ b/src/decrypt/qmc.ts
@@ -1,8 +1,6 @@
-import { QmcMapCipher, QmcRC4Cipher, QmcStaticCipher, QmcStreamCipher } from './qmc_cipher';
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';
@@ -18,7 +16,7 @@ export const HandlerMap: { [key: string]: Handler } = {
mgg1: { ext: 'ogg', version: 2 },
mflac: { ext: 'flac', version: 2 },
mflac0: { ext: 'flac', version: 2 },
- mmp4: { ext: 'mmp4', version: 2 },
+ mmp4: { ext: 'mp4', version: 2 },
// qmcflac / qmcogg:
// 有可能是 v2 加密但混用同一个后缀名。
@@ -52,12 +50,10 @@ export async function Decrypt(file: Blob, raw_filename: string, raw_ext: string)
let { version } = handler;
const fileBuffer = await GetArrayBuffer(file);
- let musicDecoded: Uint8Array | undefined;
+ let musicDecoded = new Uint8Array();
let musicID: number | string | undefined;
if (version === 2 && globalThis.WebAssembly) {
- console.log('qmc: using wasm decoder');
-
const v2Decrypted = await DecryptQmcWasm(fileBuffer, raw_ext);
// 若 v2 检测失败,降级到 v1 再尝试一次
if (v2Decrypted.success) {
@@ -65,18 +61,10 @@ export async function Decrypt(file: Blob, raw_filename: string, raw_ext: string)
musicID = v2Decrypted.songId;
console.log('qmc wasm decoder suceeded');
} else {
- console.warn('QmcWasm failed with error %s', v2Decrypted.error || '(unknown error)');
+ throw new Error(v2Decrypted.error || '(unknown error)');
}
}
- if (!musicDecoded) {
- // may throw error
- console.log('qmc: using js decoder');
- const d = new QmcDecoder(new Uint8Array(fileBuffer));
- musicDecoded = d.decrypt();
- musicID = d.songID;
- }
-
const ext = SniffAudioExt(musicDecoded, handler.ext);
const mime = AudioMimeType[ext];
@@ -98,88 +86,3 @@ export async function Decrypt(file: Blob, raw_filename: string, raw_ext: string)
mime: mime,
};
}
-
-export class QmcDecoder {
- private static readonly BYTE_COMMA = ','.charCodeAt(0);
- private readonly file: Uint8Array;
- private readonly size: number;
- private decoded: boolean = false;
- private audioSize?: number;
- private cipher?: QmcStreamCipher;
-
- public constructor(file: Uint8Array) {
- this.file = file;
- this.size = file.length;
- this.searchKey();
- }
-
- private _songID?: number;
-
- public get songID() {
- return this._songID;
- }
-
- public decrypt(): Uint8Array {
- if (!this.cipher) {
- throw new Error('no cipher found');
- }
- if (!this.audioSize || this.audioSize <= 0) {
- throw new Error('invalid audio size');
- }
- const audioBuf = this.file.subarray(0, this.audioSize);
-
- if (!this.decoded) {
- this.cipher.decrypt(audioBuf, 0);
- this.decoded = true;
- }
-
- return audioBuf;
- }
-
- private searchKey() {
- const last4Byte = this.file.slice(-4);
- const textEnc = new TextDecoder();
- if (textEnc.decode(last4Byte) === 'STag') {
- throw new Error('文件中没有写入密钥,无法解锁,请降级App并重试');
- } else if (textEnc.decode(last4Byte) === 'QTag') {
- const sizeBuf = this.file.slice(-8, -4);
- const sizeView = new DataView(sizeBuf.buffer, sizeBuf.byteOffset);
- const keySize = sizeView.getUint32(0, false);
- this.audioSize = this.size - keySize - 8;
-
- const rawKey = this.file.subarray(this.audioSize, this.size - 8);
- const keyEnd = rawKey.findIndex((v) => v == QmcDecoder.BYTE_COMMA);
- if (keyEnd < 0) {
- throw new Error('invalid key: search raw key failed');
- }
- this.setCipher(rawKey.subarray(0, keyEnd));
-
- const idBuf = rawKey.subarray(keyEnd + 1);
- const idEnd = idBuf.findIndex((v) => v == QmcDecoder.BYTE_COMMA);
- if (keyEnd < 0) {
- throw new Error('invalid key: search song id failed');
- }
- this._songID = parseInt(textEnc.decode(idBuf.subarray(0, idEnd)), 10);
- } else {
- const sizeView = new DataView(last4Byte.buffer, last4Byte.byteOffset);
- const keySize = sizeView.getUint32(0, true);
- if (keySize < 0x400) {
- this.audioSize = this.size - keySize - 4;
- const rawKey = this.file.subarray(this.audioSize, this.size - 4);
- this.setCipher(rawKey);
- } else {
- this.audioSize = this.size;
- this.cipher = new QmcStaticCipher();
- }
- }
- }
-
- private setCipher(keyRaw: Uint8Array) {
- const keyDec = QmcDeriveKey(keyRaw);
- if (keyDec.length > 300) {
- this.cipher = new QmcRC4Cipher(keyDec);
- } else {
- this.cipher = new QmcMapCipher(keyDec);
- }
- }
-}
diff --git a/src/decrypt/qmc_cipher.test.ts b/src/decrypt/qmc_cipher.test.ts
deleted file mode 100644
index 0f1e0b7..0000000
--- a/src/decrypt/qmc_cipher.test.ts
+++ /dev/null
@@ -1,117 +0,0 @@
-import { QmcMapCipher, QmcRC4Cipher, QmcStaticCipher } from '@/decrypt/qmc_cipher';
-import fs from 'fs';
-
-test('static cipher [0x7ff8,0x8000) ', () => {
- //prettier-ignore
- const expected = new Uint8Array([
- 0xD8, 0x52, 0xF7, 0x67, 0x90, 0xCA, 0xD6, 0x4A,
- 0x4A, 0xD6, 0xCA, 0x90, 0x67, 0xF7, 0x52, 0xD8,
- ])
-
- const c = new QmcStaticCipher();
- const buf = new Uint8Array(16);
- c.decrypt(buf, 0x7ff8);
-
- expect(buf).toStrictEqual(expected);
-});
-
-test('static cipher [0,0x10) ', () => {
- //prettier-ignore
- const expected = new Uint8Array([
- 0xC3, 0x4A, 0xD6, 0xCA, 0x90, 0x67, 0xF7, 0x52,
- 0xD8, 0xA1, 0x66, 0x62, 0x9F, 0x5B, 0x09, 0x00,
- ])
-
- const c = new QmcStaticCipher();
- const buf = new Uint8Array(16);
- c.decrypt(buf, 0);
-
- expect(buf).toStrictEqual(expected);
-});
-
-test('map cipher: get mask', () => {
- //prettier-ignore
- const expected = new Uint8Array([
- 0xBB, 0x7D, 0x80, 0xBE, 0xFF, 0x38, 0x81, 0xFB,
- 0xBB, 0xFF, 0x82, 0x3C, 0xFF, 0xBA, 0x83, 0x79,
- ])
- const key = new Uint8Array(256);
- for (let i = 0; i < 256; i++) key[i] = i;
- const buf = new Uint8Array(16);
-
- const c = new QmcMapCipher(key);
- c.decrypt(buf, 0);
- expect(buf).toStrictEqual(expected);
-});
-
-function loadTestDataCipher(name: string): {
- key: Uint8Array;
- cipherText: Uint8Array;
- clearText: Uint8Array;
-} {
- return {
- key: fs.readFileSync(`testdata/${name}_key.bin`),
- cipherText: fs.readFileSync(`testdata/${name}_raw.bin`),
- clearText: fs.readFileSync(`testdata/${name}_target.bin`),
- };
-}
-
-test('map cipher: real file', async () => {
- const cases = ['mflac_map', 'mgg_map'];
- for (const name of cases) {
- const { key, clearText, cipherText } = loadTestDataCipher(name);
- const c = new QmcMapCipher(key);
-
- c.decrypt(cipherText, 0);
-
- expect(cipherText).toStrictEqual(clearText);
- }
-});
-
-test('rc4 cipher: real file', async () => {
- const cases = ['mflac0_rc4', 'mflac_rc4'];
- for (const name of cases) {
- const { key, clearText, cipherText } = loadTestDataCipher(name);
- const c = new QmcRC4Cipher(key);
-
- c.decrypt(cipherText, 0);
-
- expect(cipherText).toStrictEqual(clearText);
- }
-});
-
-test('rc4 cipher: first segment', async () => {
- const cases = ['mflac0_rc4', 'mflac_rc4'];
- for (const name of cases) {
- const { key, clearText, cipherText } = loadTestDataCipher(name);
- const c = new QmcRC4Cipher(key);
-
- const buf = cipherText.slice(0, 128);
- c.decrypt(buf, 0);
- expect(buf).toStrictEqual(clearText.slice(0, 128));
- }
-});
-
-test('rc4 cipher: align block (128~5120)', async () => {
- const cases = ['mflac0_rc4', 'mflac_rc4'];
- for (const name of cases) {
- const { key, clearText, cipherText } = loadTestDataCipher(name);
- const c = new QmcRC4Cipher(key);
-
- const buf = cipherText.slice(128, 5120);
- c.decrypt(buf, 128);
- expect(buf).toStrictEqual(clearText.slice(128, 5120));
- }
-});
-
-test('rc4 cipher: simple block (5120~10240)', async () => {
- const cases = ['mflac0_rc4', 'mflac_rc4'];
- for (const name of cases) {
- const { key, clearText, cipherText } = loadTestDataCipher(name);
- const c = new QmcRC4Cipher(key);
-
- const buf = cipherText.slice(5120, 10240);
- c.decrypt(buf, 5120);
- expect(buf).toStrictEqual(clearText.slice(5120, 10240));
- }
-});
diff --git a/src/decrypt/qmc_cipher.ts b/src/decrypt/qmc_cipher.ts
deleted file mode 100644
index d0d3683..0000000
--- a/src/decrypt/qmc_cipher.ts
+++ /dev/null
@@ -1,199 +0,0 @@
-export interface QmcStreamCipher {
- decrypt(buf: Uint8Array, offset: number): void;
-}
-
-export class QmcStaticCipher implements QmcStreamCipher {
- //prettier-ignore
- private static readonly staticCipherBox: Uint8Array = new Uint8Array([
- 0x77, 0x48, 0x32, 0x73, 0xDE, 0xF2, 0xC0, 0xC8, //0x00
- 0x95, 0xEC, 0x30, 0xB2, 0x51, 0xC3, 0xE1, 0xA0, //0x08
- 0x9E, 0xE6, 0x9D, 0xCF, 0xFA, 0x7F, 0x14, 0xD1, //0x10
- 0xCE, 0xB8, 0xDC, 0xC3, 0x4A, 0x67, 0x93, 0xD6, //0x18
- 0x28, 0xC2, 0x91, 0x70, 0xCA, 0x8D, 0xA2, 0xA4, //0x20
- 0xF0, 0x08, 0x61, 0x90, 0x7E, 0x6F, 0xA2, 0xE0, //0x28
- 0xEB, 0xAE, 0x3E, 0xB6, 0x67, 0xC7, 0x92, 0xF4, //0x30
- 0x91, 0xB5, 0xF6, 0x6C, 0x5E, 0x84, 0x40, 0xF7, //0x38
- 0xF3, 0x1B, 0x02, 0x7F, 0xD5, 0xAB, 0x41, 0x89, //0x40
- 0x28, 0xF4, 0x25, 0xCC, 0x52, 0x11, 0xAD, 0x43, //0x48
- 0x68, 0xA6, 0x41, 0x8B, 0x84, 0xB5, 0xFF, 0x2C, //0x50
- 0x92, 0x4A, 0x26, 0xD8, 0x47, 0x6A, 0x7C, 0x95, //0x58
- 0x61, 0xCC, 0xE6, 0xCB, 0xBB, 0x3F, 0x47, 0x58, //0x60
- 0x89, 0x75, 0xC3, 0x75, 0xA1, 0xD9, 0xAF, 0xCC, //0x68
- 0x08, 0x73, 0x17, 0xDC, 0xAA, 0x9A, 0xA2, 0x16, //0x70
- 0x41, 0xD8, 0xA2, 0x06, 0xC6, 0x8B, 0xFC, 0x66, //0x78
- 0x34, 0x9F, 0xCF, 0x18, 0x23, 0xA0, 0x0A, 0x74, //0x80
- 0xE7, 0x2B, 0x27, 0x70, 0x92, 0xE9, 0xAF, 0x37, //0x88
- 0xE6, 0x8C, 0xA7, 0xBC, 0x62, 0x65, 0x9C, 0xC2, //0x90
- 0x08, 0xC9, 0x88, 0xB3, 0xF3, 0x43, 0xAC, 0x74, //0x98
- 0x2C, 0x0F, 0xD4, 0xAF, 0xA1, 0xC3, 0x01, 0x64, //0xA0
- 0x95, 0x4E, 0x48, 0x9F, 0xF4, 0x35, 0x78, 0x95, //0xA8
- 0x7A, 0x39, 0xD6, 0x6A, 0xA0, 0x6D, 0x40, 0xE8, //0xB0
- 0x4F, 0xA8, 0xEF, 0x11, 0x1D, 0xF3, 0x1B, 0x3F, //0xB8
- 0x3F, 0x07, 0xDD, 0x6F, 0x5B, 0x19, 0x30, 0x19, //0xC0
- 0xFB, 0xEF, 0x0E, 0x37, 0xF0, 0x0E, 0xCD, 0x16, //0xC8
- 0x49, 0xFE, 0x53, 0x47, 0x13, 0x1A, 0xBD, 0xA4, //0xD0
- 0xF1, 0x40, 0x19, 0x60, 0x0E, 0xED, 0x68, 0x09, //0xD8
- 0x06, 0x5F, 0x4D, 0xCF, 0x3D, 0x1A, 0xFE, 0x20, //0xE0
- 0x77, 0xE4, 0xD9, 0xDA, 0xF9, 0xA4, 0x2B, 0x76, //0xE8
- 0x1C, 0x71, 0xDB, 0x00, 0xBC, 0xFD, 0x0C, 0x6C, //0xF0
- 0xA5, 0x47, 0xF7, 0xF6, 0x00, 0x79, 0x4A, 0x11, //0xF8
- ])
-
- public getMask(offset: number) {
- if (offset > 0x7fff) offset %= 0x7fff;
- return QmcStaticCipher.staticCipherBox[(offset * offset + 27) & 0xff];
- }
-
- public decrypt(buf: Uint8Array, offset: number) {
- for (let i = 0; i < buf.length; i++) {
- buf[i] ^= this.getMask(offset + i);
- }
- }
-}
-
-export class QmcMapCipher implements QmcStreamCipher {
- key: Uint8Array;
- n: number;
-
- constructor(key: Uint8Array) {
- if (key.length == 0) throw Error('qmc/cipher_map: invalid key size');
-
- this.key = key;
- this.n = key.length;
- }
-
- private static rotate(value: number, bits: number) {
- let rotate = (bits + 4) % 8;
- let left = value << rotate;
- let right = value >> rotate;
- return (left | right) & 0xff;
- }
-
- decrypt(buf: Uint8Array, offset: number): void {
- for (let i = 0; i < buf.length; i++) {
- buf[i] ^= this.getMask(offset + i);
- }
- }
-
- private getMask(offset: number) {
- if (offset > 0x7fff) offset %= 0x7fff;
-
- const idx = (offset * offset + 71214) % this.n;
- return QmcMapCipher.rotate(this.key[idx], idx & 0x7);
- }
-}
-
-export class QmcRC4Cipher implements QmcStreamCipher {
- private static readonly FIRST_SEGMENT_SIZE = 0x80;
- private static readonly SEGMENT_SIZE = 5120;
-
- S: Uint8Array;
- N: number;
- key: Uint8Array;
- hash: number;
-
- constructor(key: Uint8Array) {
- if (key.length == 0) {
- throw Error('invalid key size');
- }
-
- this.key = key;
- this.N = key.length;
-
- // init seed box
- this.S = new Uint8Array(this.N);
- for (let i = 0; i < this.N; ++i) {
- this.S[i] = i & 0xff;
- }
- let j = 0;
- for (let i = 0; i < this.N; ++i) {
- j = (this.S[i] + j + this.key[i % this.N]) % this.N;
- [this.S[i], this.S[j]] = [this.S[j], this.S[i]];
- }
-
- // init hash base
- this.hash = 1;
- for (let i = 0; i < this.N; i++) {
- let value = this.key[i];
-
- // ignore if key char is '\x00'
- if (!value) continue;
-
- const next_hash = (this.hash * value) >>> 0;
- if (next_hash == 0 || next_hash <= this.hash) break;
-
- this.hash = next_hash;
- }
- }
-
- decrypt(buf: Uint8Array, offset: number): void {
- let toProcess = buf.length;
- let processed = 0;
- const postProcess = (len: number): boolean => {
- toProcess -= len;
- processed += len;
- offset += len;
- return toProcess == 0;
- };
-
- // Initial segment
- if (offset < QmcRC4Cipher.FIRST_SEGMENT_SIZE) {
- const len_segment = Math.min(buf.length, QmcRC4Cipher.FIRST_SEGMENT_SIZE - offset);
- this.encFirstSegment(buf.subarray(0, len_segment), offset);
- if (postProcess(len_segment)) return;
- }
-
- // align segment
- if (offset % QmcRC4Cipher.SEGMENT_SIZE != 0) {
- const len_segment = Math.min(QmcRC4Cipher.SEGMENT_SIZE - (offset % QmcRC4Cipher.SEGMENT_SIZE), toProcess);
- this.encASegment(buf.subarray(processed, processed + len_segment), offset);
- if (postProcess(len_segment)) return;
- }
-
- // Batch process segments
- while (toProcess > QmcRC4Cipher.SEGMENT_SIZE) {
- this.encASegment(buf.subarray(processed, processed + QmcRC4Cipher.SEGMENT_SIZE), offset);
- postProcess(QmcRC4Cipher.SEGMENT_SIZE);
- }
-
- // Last segment (incomplete segment)
- if (toProcess > 0) {
- this.encASegment(buf.subarray(processed), offset);
- }
- }
-
- private encFirstSegment(buf: Uint8Array, offset: number) {
- for (let i = 0; i < buf.length; i++) {
- buf[i] ^= this.key[this.getSegmentKey(offset + i)];
- }
- }
-
- private encASegment(buf: Uint8Array, offset: number) {
- // Initialise a new seed box
- const S = this.S.slice(0);
-
- // Calculate the number of bytes to skip.
- // The initial "key" derived from segment id, plus the current offset.
- const skipLen =
- (offset % QmcRC4Cipher.SEGMENT_SIZE) + this.getSegmentKey(Math.floor(offset / QmcRC4Cipher.SEGMENT_SIZE));
-
- // decrypt the block
- let j = 0;
- let k = 0;
- for (let i = -skipLen; i < buf.length; i++) {
- j = (j + 1) % this.N;
- k = (S[j] + k) % this.N;
- [S[k], S[j]] = [S[j], S[k]];
-
- if (i >= 0) {
- buf[i] ^= S[(S[j] + S[k]) % this.N];
- }
- }
- }
-
- private getSegmentKey(id: number): number {
- const seed = this.key[id % this.N];
- const idx = Math.floor((this.hash / ((id + 1) * seed)) * 100.0);
- return idx % this.N;
- }
-}
diff --git a/src/decrypt/qmc_key.test.ts b/src/decrypt/qmc_key.test.ts
deleted file mode 100644
index 2ec1e96..0000000
--- a/src/decrypt/qmc_key.test.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-import { QmcDeriveKey, simpleMakeKey } from '@/decrypt/qmc_key';
-import fs from 'fs';
-
-test('key dec: make simple key', () => {
- expect(simpleMakeKey(106, 8)).toStrictEqual([0x69, 0x56, 0x46, 0x38, 0x2b, 0x20, 0x15, 0x0b]);
-});
-
-function loadTestDataKeyDecrypt(name: string): {
- cipherText: Uint8Array;
- clearText: Uint8Array;
-} {
- return {
- cipherText: fs.readFileSync(`testdata/${name}_key_raw.bin`),
- clearText: fs.readFileSync(`testdata/${name}_key.bin`),
- };
-}
-
-test('key dec: real file', async () => {
- const cases = ['mflac_map', 'mgg_map', 'mflac0_rc4', 'mflac_rc4'];
- for (const name of cases) {
- const { clearText, cipherText } = loadTestDataKeyDecrypt(name);
- const buf = QmcDeriveKey(cipherText);
-
- expect(buf).toStrictEqual(clearText);
- }
-});
diff --git a/src/decrypt/qmc_key.ts b/src/decrypt/qmc_key.ts
deleted file mode 100644
index b23b15f..0000000
--- a/src/decrypt/qmc_key.ts
+++ /dev/null
@@ -1,127 +0,0 @@
-import { TeaCipher } from '@/utils/tea';
-
-const SALT_LEN = 2;
-const ZERO_LEN = 7;
-
-export function QmcDeriveKey(raw: Uint8Array): Uint8Array {
- const textDec = new TextDecoder();
- let rawDec = Buffer.from(textDec.decode(raw), 'base64');
- let n = rawDec.length;
- if (n < 16) {
- throw Error('key length is too short');
- }
-
- rawDec = decryptV2Key(rawDec);
-
- const simpleKey = simpleMakeKey(106, 8);
- let teaKey = new Uint8Array(16);
- for (let i = 0; i < 8; i++) {
- teaKey[i << 1] = simpleKey[i];
- teaKey[(i << 1) + 1] = rawDec[i];
- }
- const sub = decryptTencentTea(rawDec.subarray(8), teaKey);
- rawDec.set(sub, 8);
- return rawDec.subarray(0, 8 + sub.length);
-}
-
-// simpleMakeKey exported only for unit test
-export function simpleMakeKey(salt: number, length: number): number[] {
- const keyBuf: number[] = [];
- for (let i = 0; i < length; i++) {
- const tmp = Math.tan(salt + i * 0.1);
- keyBuf[i] = 0xff & (Math.abs(tmp) * 100.0);
- }
- return keyBuf;
-}
-
-const mixKey1: Uint8Array = new Uint8Array([ 0x33, 0x38, 0x36, 0x5A, 0x4A, 0x59, 0x21, 0x40, 0x23, 0x2A, 0x24, 0x25, 0x5E, 0x26, 0x29, 0x28 ])
-const mixKey2: Uint8Array = new Uint8Array([ 0x2A, 0x2A, 0x23, 0x21, 0x28, 0x23, 0x24, 0x25, 0x26, 0x5E, 0x61, 0x31, 0x63, 0x5A, 0x2C, 0x54 ])
-
-function decryptV2Key(key: Buffer): Buffer
-{
- const textEnc = new TextDecoder();
- if (key.length < 18 || textEnc.decode(key.slice(0, 18)) !== 'QQMusic EncV2,Key:') {
- return key;
- }
-
- let out = decryptTencentTea(key.slice(18), mixKey1);
- out = decryptTencentTea(out, mixKey2);
- const textDec = new TextDecoder();
- const keyDec = Buffer.from(textDec.decode(out), 'base64');
- let n = keyDec.length;
- if (n < 16) {
- throw Error('EncV2 key decode failed');
- }
-
- return keyDec;
-}
-
-function decryptTencentTea(inBuf: Uint8Array, key: Uint8Array): Uint8Array {
- if (inBuf.length % 8 != 0) {
- throw Error('inBuf size not a multiple of the block size');
- }
- if (inBuf.length < 16) {
- throw Error('inBuf size too small');
- }
-
- const blk = new TeaCipher(key, 32);
-
- const tmpBuf = new Uint8Array(8);
- const tmpView = new DataView(tmpBuf.buffer);
-
- blk.decrypt(tmpView, new DataView(inBuf.buffer, inBuf.byteOffset, 8));
-
- const nPadLen = tmpBuf[0] & 0x7; //只要最低三位
- /*密文格式:PadLen(1byte)+Padding(var,0-7byte)+Salt(2byte)+Body(var byte)+Zero(7byte)*/
- const outLen = inBuf.length - 1 /*PadLen*/ - nPadLen - SALT_LEN - ZERO_LEN;
- const outBuf = new Uint8Array(outLen);
-
- let ivPrev = new Uint8Array(8);
- let ivCur = inBuf.slice(0, 8); // init iv
- let inBufPos = 8;
-
- // 跳过 Padding Len 和 Padding
- let tmpIdx = 1 + nPadLen;
-
- // CBC IV 处理
- const cryptBlock = () => {
- ivPrev = ivCur;
- ivCur = inBuf.slice(inBufPos, inBufPos + 8);
- for (let j = 0; j < 8; j++) {
- tmpBuf[j] ^= ivCur[j];
- }
- blk.decrypt(tmpView, tmpView);
- inBufPos += 8;
- tmpIdx = 0;
- };
-
- // 跳过 Salt
- for (let i = 1; i <= SALT_LEN; ) {
- if (tmpIdx < 8) {
- tmpIdx++;
- i++;
- } else {
- cryptBlock();
- }
- }
-
- // 还原明文
- let outBufPos = 0;
- while (outBufPos < outLen) {
- if (tmpIdx < 8) {
- outBuf[outBufPos] = tmpBuf[tmpIdx] ^ ivPrev[tmpIdx];
- outBufPos++;
- tmpIdx++;
- } else {
- cryptBlock();
- }
- }
-
- // 校验Zero
- for (let i = 1; i <= ZERO_LEN; i++) {
- if (tmpBuf[tmpIdx] != ivPrev[tmpIdx]) {
- throw Error('zero check failed');
- }
- }
- return outBuf;
-}
diff --git a/src/decrypt/qmccache.ts b/src/decrypt/qmccache.ts
index 6a57a94..0b6582b 100644
--- a/src/decrypt/qmccache.ts
+++ b/src/decrypt/qmccache.ts
@@ -17,7 +17,7 @@ import { parseBlob as metaParseBlob } from 'music-metadata-browser';
export async function Decrypt(file: Blob, raw_filename: string, raw_ext: string): Promise {
const buffer = await GetArrayBuffer(file);
- let musicDecoded: Uint8Array | undefined;
+ let musicDecoded = new Uint8Array();
if (globalThis.WebAssembly) {
console.log('qmc: using wasm decoder');
@@ -27,19 +27,10 @@ export async function Decrypt(file: Blob, raw_filename: string, raw_ext: string)
musicDecoded = qmcDecrypted.data;
console.log('qmc wasm decoder suceeded');
} else {
- console.warn('QmcWasm failed with error %s', qmcDecrypted.error || '(unknown error)');
+ throw new Error(qmcDecrypted.error || '(unknown error)');
}
}
- if (!musicDecoded) {
- musicDecoded = new Uint8Array(buffer);
- let length = musicDecoded.length;
- for (let i = 0; i < length; i++) {
- let byte = musicDecoded[i] ^ 0xf4; // xor 0xf4
- byte = ((byte & 0b0011_1111) << 2) | (byte >> 6); // rol 2
- musicDecoded[i] = byte;
- }
- }
let ext = SniffAudioExt(musicDecoded, '');
const newName = SplitFilename(raw_filename);
let audioBlob: Blob;
diff --git a/src/utils/tea.test.ts b/src/utils/tea.test.ts
deleted file mode 100644
index bb3fbee..0000000
--- a/src/utils/tea.test.ts
+++ /dev/null
@@ -1,73 +0,0 @@
-// Copyright 2021 MengYX. All rights reserved.
-//
-// Copyright 2015 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in https://go.dev/LICENSE.
-
-import { TeaCipher } from '@/utils/tea';
-
-test('key size', () => {
- // prettier-ignore
- const testKey = new Uint8Array([
- 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
- 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
- 0x00,
- ])
- expect(() => new TeaCipher(testKey.slice(0, 16))).not.toThrow();
-
- expect(() => new TeaCipher(testKey)).toThrow();
-
- expect(() => new TeaCipher(testKey.slice(0, 15))).toThrow();
-});
-
-// prettier-ignore
-const teaTests = [
- // These were sourced from https://github.com/froydnj/ironclad/blob/master/testing/test-vectors/tea.testvec
- {
- rounds: TeaCipher.numRounds,
- key: new Uint8Array([
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- ]),
- plainText: new Uint8Array([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]),
- cipherText: new Uint8Array([0x41, 0xea, 0x3a, 0x0a, 0x94, 0xba, 0xa9, 0x40]),
- },
- {
- rounds: TeaCipher.numRounds,
- key: new Uint8Array([
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- ]),
- plainText: new Uint8Array([0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]),
- cipherText: new Uint8Array([0x31, 0x9b, 0xbe, 0xfb, 0x01, 0x6a, 0xbd, 0xb2]),
- },
- {
- rounds: 16,
- key: new Uint8Array([
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- ]),
- plainText: new Uint8Array([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]),
- cipherText: new Uint8Array([0xed, 0x28, 0x5d, 0xa1, 0x45, 0x5b, 0x33, 0xc1]),
- },
-];
-
-test('rounds', () => {
- const tt = teaTests[0];
- expect(() => new TeaCipher(tt.key, tt.rounds - 1)).toThrow();
-});
-
-test('encrypt & decrypt', () => {
- for (const tt of teaTests) {
- const c = new TeaCipher(tt.key, tt.rounds);
-
- const buf = new Uint8Array(8);
- const bufView = new DataView(buf.buffer);
-
- c.encrypt(bufView, new DataView(tt.plainText.buffer));
- expect(buf).toStrictEqual(tt.cipherText);
-
- c.decrypt(bufView, new DataView(tt.cipherText.buffer));
- expect(buf).toStrictEqual(tt.plainText);
- }
-});
diff --git a/src/utils/tea.ts b/src/utils/tea.ts
deleted file mode 100644
index 0a93d6a..0000000
--- a/src/utils/tea.ts
+++ /dev/null
@@ -1,80 +0,0 @@
-// Copyright 2021 MengYX. All rights reserved.
-//
-// Copyright 2015 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in https://go.dev/LICENSE.
-
-// TeaCipher is a typescript port to golang.org/x/crypto/tea
-
-// Package tea implements the TEA algorithm, as defined in Needham and
-// Wheeler's 1994 technical report, “TEA, a Tiny Encryption Algorithm”. See
-// http://www.cix.co.uk/~klockstone/tea.pdf for details.
-//
-// TEA is a legacy cipher and its short block size makes it vulnerable to
-// birthday bound attacks (see https://sweet32.info). It should only be used
-// where compatibility with legacy systems, not security, is the goal.
-
-export class TeaCipher {
- // BlockSize is the size of a TEA block, in bytes.
- static readonly BlockSize = 8;
-
- // KeySize is the size of a TEA key, in bytes.
- static readonly KeySize = 16;
-
- // delta is the TEA key schedule constant.
- static readonly delta = 0x9e3779b9;
-
- // numRounds 64 is the standard number of rounds in TEA.
- static readonly numRounds = 64;
-
- k0: number;
- k1: number;
- k2: number;
- k3: number;
- rounds: number;
-
- constructor(key: Uint8Array, rounds: number = TeaCipher.numRounds) {
- if (key.length != 16) {
- throw Error('incorrect key size');
- }
- if ((rounds & 1) != 0) {
- throw Error('odd number of rounds specified');
- }
-
- const k = new DataView(key.buffer);
- this.k0 = k.getUint32(0, false);
- this.k1 = k.getUint32(4, false);
- this.k2 = k.getUint32(8, false);
- this.k3 = k.getUint32(12, false);
- this.rounds = rounds;
- }
-
- encrypt(dst: DataView, src: DataView) {
- let v0 = src.getUint32(0, false);
- let v1 = src.getUint32(4, false);
-
- let sum = 0;
- for (let i = 0; i < this.rounds / 2; i++) {
- sum = sum + TeaCipher.delta;
- v0 += ((v1 << 4) + this.k0) ^ (v1 + sum) ^ ((v1 >>> 5) + this.k1);
- v1 += ((v0 << 4) + this.k2) ^ (v0 + sum) ^ ((v0 >>> 5) + this.k3);
- }
-
- dst.setUint32(0, v0, false);
- dst.setUint32(4, v1, false);
- }
-
- decrypt(dst: DataView, src: DataView) {
- let v0 = src.getUint32(0, false);
- let v1 = src.getUint32(4, false);
-
- let sum = (TeaCipher.delta * this.rounds) / 2;
- for (let i = 0; i < this.rounds / 2; i++) {
- v1 -= ((v0 << 4) + this.k2) ^ (v0 + sum) ^ ((v0 >>> 5) + this.k3);
- v0 -= ((v1 << 4) + this.k0) ^ (v1 + sum) ^ ((v1 >>> 5) + this.k1);
- sum -= TeaCipher.delta;
- }
- dst.setUint32(0, v0, false);
- dst.setUint32(4, v1, false);
- }
-}
From 44418486f6e7e84804015e3214603bcd14da66d1 Mon Sep 17 00:00:00 2001
From: xhacker-zzz <959220793@qq.com>
Date: Fri, 20 Jan 2023 20:27:36 +0800
Subject: [PATCH 02/11] use wasm in npm packages
---
.drone.yml | 5 -
package-lock.json | 58 +++-----
package.json | 7 +-
scripts/build-wasm.sh | 13 --
src/KgmWasm/CMakeLists.txt | 65 ---------
src/KgmWasm/KgmWasm.cpp | 20 ---
src/KgmWasm/KgmWasm.h | 18 ---
src/KgmWasm/README.md | 9 --
src/KgmWasm/build-wasm | 41 ------
src/KgmWasm/kgm.hpp | 112 --------------
src/QmcWasm/CMakeLists.txt | 65 ---------
src/QmcWasm/QmcWasm.cpp | 57 --------
src/QmcWasm/QmcWasm.h | 23 ---
src/QmcWasm/README.md | 9 --
src/QmcWasm/TencentTea.hpp | 289 ------------------------------------
src/QmcWasm/base64.hpp | 207 --------------------------
src/QmcWasm/build-wasm | 41 ------
src/QmcWasm/qmc.hpp | 230 -----------------------------
src/QmcWasm/qmc_cipher.hpp | 290 -------------------------------------
src/QmcWasm/qmc_key.hpp | 217 ---------------------------
src/decrypt/kgm_wasm.ts | 23 +--
src/decrypt/qmc_wasm.ts | 25 ++--
22 files changed, 53 insertions(+), 1771 deletions(-)
delete mode 100755 scripts/build-wasm.sh
delete mode 100644 src/KgmWasm/CMakeLists.txt
delete mode 100644 src/KgmWasm/KgmWasm.cpp
delete mode 100644 src/KgmWasm/KgmWasm.h
delete mode 100644 src/KgmWasm/README.md
delete mode 100755 src/KgmWasm/build-wasm
delete mode 100644 src/KgmWasm/kgm.hpp
delete mode 100644 src/QmcWasm/CMakeLists.txt
delete mode 100644 src/QmcWasm/QmcWasm.cpp
delete mode 100644 src/QmcWasm/QmcWasm.h
delete mode 100644 src/QmcWasm/README.md
delete mode 100644 src/QmcWasm/TencentTea.hpp
delete mode 100644 src/QmcWasm/base64.hpp
delete mode 100755 src/QmcWasm/build-wasm
delete mode 100644 src/QmcWasm/qmc.hpp
delete mode 100644 src/QmcWasm/qmc_cipher.hpp
delete mode 100644 src/QmcWasm/qmc_key.hpp
diff --git a/.drone.yml b/.drone.yml
index 72763ef..dd4d4ef 100644
--- a/.drone.yml
+++ b/.drone.yml
@@ -4,11 +4,6 @@ type: docker
name: default
steps:
- - name: build-wasm
- image: emscripten/emsdk:3.0.0
- commands:
- - ./scripts/build-wasm.sh
-
- name: build
image: node:16.18-bullseye
commands:
diff --git a/package-lock.json b/package-lock.json
index 80e2a66..26ab988 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,18 +1,19 @@
{
"name": "unlock-music",
- "version": "v1.10.3",
+ "version": "1.10.4",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "unlock-music",
- "version": "v1.10.3",
+ "version": "1.10.4",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
"@babel/preset-typescript": "^7.16.5",
- "@jixun/kugou-crypto": "^1.0.3",
"@unlock-music/joox-crypto": "^0.0.1-R5",
+ "@xhacker/kgmwasm": "^1.0.0",
+ "@xhacker/qmcwasm": "^1.0.0",
"base64-js": "^1.5.1",
"browser-id3-writer": "^4.4.0",
"core-js": "^3.16.0",
@@ -2986,22 +2987,6 @@
"regenerator-runtime": "^0.13.3"
}
},
- "node_modules/@jixun/kugou-crypto": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/@jixun/kugou-crypto/-/kugou-crypto-1.0.3.tgz",
- "integrity": "sha512-ZiwSkpIAH8IkFcTfMjdQMpP/xco3iXEdYDEQo4wquYpSAln5RmSed3iBctnpoE6s3X1cxmBGhpCYW6v6vZfs+g==",
- "dependencies": {
- "commander": "^9.2.0"
- }
- },
- "node_modules/@jixun/kugou-crypto/node_modules/commander": {
- "version": "9.2.0",
- "resolved": "https://registry.npmjs.org/commander/-/commander-9.2.0.tgz",
- "integrity": "sha512-e2i4wANQiSXgnrBlIatyHtP1odfUp0BbV5Y5nEGbxtIrStkEOAAzCUirvLBNXHLr7kwLvJl6V+4V3XV9x7Wd9w==",
- "engines": {
- "node": "^12.20.0 || >=14"
- }
- },
"node_modules/@mrmlnc/readdir-enhanced": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz",
@@ -4197,6 +4182,16 @@
"@xtuc/long": "4.2.2"
}
},
+ "node_modules/@xhacker/kgmwasm": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/@xhacker/kgmwasm/-/kgmwasm-1.0.0.tgz",
+ "integrity": "sha512-LnBuEVRJQVyJGJTb0cPZxZDu7Qi4PqDhJLRaRJfG6pSUeZuIoglzHiysyd4XfNHobNnLxG8v1IiNPS/uWwoG0A=="
+ },
+ "node_modules/@xhacker/qmcwasm": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/@xhacker/qmcwasm/-/qmcwasm-1.0.0.tgz",
+ "integrity": "sha512-oE6isNLmCDqIvxJV9KyDVlIzMISQzTj8o1ePWtQ+DhfXLI0hel/DwOIQ3icCikWnfwA/5SDs2hYw5BvrxdJ63g=="
+ },
"node_modules/@xtuc/ieee754": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
@@ -23195,21 +23190,6 @@
"regenerator-runtime": "^0.13.3"
}
},
- "@jixun/kugou-crypto": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/@jixun/kugou-crypto/-/kugou-crypto-1.0.3.tgz",
- "integrity": "sha512-ZiwSkpIAH8IkFcTfMjdQMpP/xco3iXEdYDEQo4wquYpSAln5RmSed3iBctnpoE6s3X1cxmBGhpCYW6v6vZfs+g==",
- "requires": {
- "commander": "^9.2.0"
- },
- "dependencies": {
- "commander": {
- "version": "9.2.0",
- "resolved": "https://registry.npmjs.org/commander/-/commander-9.2.0.tgz",
- "integrity": "sha512-e2i4wANQiSXgnrBlIatyHtP1odfUp0BbV5Y5nEGbxtIrStkEOAAzCUirvLBNXHLr7kwLvJl6V+4V3XV9x7Wd9w=="
- }
- }
- },
"@mrmlnc/readdir-enhanced": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz",
@@ -24256,6 +24236,16 @@
"@xtuc/long": "4.2.2"
}
},
+ "@xhacker/kgmwasm": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/@xhacker/kgmwasm/-/kgmwasm-1.0.0.tgz",
+ "integrity": "sha512-LnBuEVRJQVyJGJTb0cPZxZDu7Qi4PqDhJLRaRJfG6pSUeZuIoglzHiysyd4XfNHobNnLxG8v1IiNPS/uWwoG0A=="
+ },
+ "@xhacker/qmcwasm": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/@xhacker/qmcwasm/-/qmcwasm-1.0.0.tgz",
+ "integrity": "sha512-oE6isNLmCDqIvxJV9KyDVlIzMISQzTj8o1ePWtQ+DhfXLI0hel/DwOIQ3icCikWnfwA/5SDs2hYw5BvrxdJ63g=="
+ },
"@xtuc/ieee754": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
diff --git a/package.json b/package.json
index 8373728..fc70d97 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "unlock-music",
- "version": "1.10.3",
+ "version": "1.10.4",
"ext_build": 0,
"updateInfo": "完善音乐标签编辑功能,支持编辑更多标签",
"license": "MIT",
@@ -21,8 +21,9 @@
},
"dependencies": {
"@babel/preset-typescript": "^7.16.5",
- "@jixun/kugou-crypto": "^1.0.3",
"@unlock-music/joox-crypto": "^0.0.1-R5",
+ "@xhacker/kgmwasm": "^1.0.0",
+ "@xhacker/qmcwasm": "^1.0.0",
"base64-js": "^1.5.1",
"browser-id3-writer": "^4.4.0",
"core-js": "^3.16.0",
@@ -56,4 +57,4 @@
"vue-cli-plugin-element": "^1.0.1",
"vue-template-compiler": "^2.6.14"
}
-}
\ No newline at end of file
+}
diff --git a/scripts/build-wasm.sh b/scripts/build-wasm.sh
deleted file mode 100755
index f0d7922..0000000
--- a/scripts/build-wasm.sh
+++ /dev/null
@@ -1,13 +0,0 @@
-#!/usr/bin/env bash
-
-set -ex
-
-cd "$(git rev-parse --show-toplevel)"
-
-pushd ./src/QmcWasm
-bash build-wasm
-popd
-
-pushd ./src/KgmWasm
-bash build-wasm
-popd
diff --git a/src/KgmWasm/CMakeLists.txt b/src/KgmWasm/CMakeLists.txt
deleted file mode 100644
index 1014b3b..0000000
--- a/src/KgmWasm/CMakeLists.txt
+++ /dev/null
@@ -1,65 +0,0 @@
-# CMakeList.txt : CMake project for KgmWasm, include source and define
-# project specific logic here.
-#
-cmake_minimum_required (VERSION 3.8)
-
-project ("KgmWasm")
-
-set(CMAKE_CXX_STANDARD 14)
-
-include_directories(
- $
-)
-
-# Add source to this project's executable.
-set(RUNTIME_METHODS_LIST
- getValue
- writeArrayToMemory
- UTF8ToString
-)
-list(JOIN RUNTIME_METHODS_LIST "," RUNTIME_METHODS)
-
-set(EMSCRIPTEN_FLAGS
- "--bind"
- "-s NO_DYNAMIC_EXECUTION=1"
- "-s MODULARIZE=1"
- "-s EXPORT_NAME=KgmCryptoModule"
- "-s EXPORTED_RUNTIME_METHODS=${RUNTIME_METHODS}"
-)
-set(EMSCRIPTEN_LEGACY_FLAGS
- ${EMSCRIPTEN_FLAGS}
- "-s WASM=0"
- "--memory-init-file 0"
-)
-set(EMSCRIPTEN_WASM_BUNDLE_FLAGS
- ${EMSCRIPTEN_FLAGS}
- "-s SINGLE_FILE=1"
-)
-
-list(JOIN EMSCRIPTEN_FLAGS " " EMSCRIPTEN_FLAGS_STR)
-list(JOIN EMSCRIPTEN_LEGACY_FLAGS " " EMSCRIPTEN_LEGACY_FLAGS_STR)
-list(JOIN EMSCRIPTEN_WASM_BUNDLE_FLAGS " " EMSCRIPTEN_WASM_BUNDLE_FLAGS_STR)
-
-# Define projects config
-set(WASM_SOURCES
- "KgmWasm.cpp"
-)
-
-add_executable(KgmWasm ${WASM_SOURCES})
-set_target_properties(
- KgmWasm
- PROPERTIES LINK_FLAGS ${EMSCRIPTEN_FLAGS_STR}
-)
-
-add_executable(KgmWasmBundle ${WASM_SOURCES})
-set_target_properties(
- KgmWasmBundle
- PROPERTIES LINK_FLAGS ${EMSCRIPTEN_WASM_BUNDLE_FLAGS_STR}
-)
-
-add_executable(KgmLegacy ${WASM_SOURCES})
-set_target_properties(
- KgmLegacy
- PROPERTIES LINK_FLAGS ${EMSCRIPTEN_LEGACY_FLAGS_STR}
-)
-
diff --git a/src/KgmWasm/KgmWasm.cpp b/src/KgmWasm/KgmWasm.cpp
deleted file mode 100644
index 7901fed..0000000
--- a/src/KgmWasm/KgmWasm.cpp
+++ /dev/null
@@ -1,20 +0,0 @@
-// KgmWasm.cpp : Defines the entry point for the application.
-//
-
-#include "KgmWasm.h"
-
-#include "kgm.hpp"
-
-#include
-#include
-
-size_t preDec(uintptr_t blob, size_t blobSize, std::string ext)
-{
- return PreDec((uint8_t*)blob, blobSize, ext == "vpr");
-}
-
-void decBlob(uintptr_t blob, size_t blobSize, size_t offset)
-{
- Decrypt((uint8_t*)blob, blobSize, offset);
- return;
-}
diff --git a/src/KgmWasm/KgmWasm.h b/src/KgmWasm/KgmWasm.h
deleted file mode 100644
index 0b1d7eb..0000000
--- a/src/KgmWasm/KgmWasm.h
+++ /dev/null
@@ -1,18 +0,0 @@
-// KgmWasm.h : Include file for standard system include files,
-// or project specific include files.
-
-#pragma once
-
-#include
-#include
-
-namespace em = emscripten;
-
-size_t preDec(uintptr_t blob, size_t blobSize, std::string ext);
-void decBlob(uintptr_t blob, size_t blobSize, size_t offset);
-
-EMSCRIPTEN_BINDINGS(QmcCrypto)
-{
- em::function("preDec", &preDec, em::allow_raw_pointers());
- em::function("decBlob", &decBlob, em::allow_raw_pointers());
-}
diff --git a/src/KgmWasm/README.md b/src/KgmWasm/README.md
deleted file mode 100644
index 0ad5092..0000000
--- a/src/KgmWasm/README.md
+++ /dev/null
@@ -1,9 +0,0 @@
-# KgmWasm
-
-## 构建
-
-在 Linux 环境下执行 `bash build-wasm` 即可构建。
-
-## Build
-
-Linux environment required. Build wasm binary by execute `bash build-wasm`.
diff --git a/src/KgmWasm/build-wasm b/src/KgmWasm/build-wasm
deleted file mode 100755
index 599b97c..0000000
--- a/src/KgmWasm/build-wasm
+++ /dev/null
@@ -1,41 +0,0 @@
-#!/usr/bin/env bash
-
-set -e
-
-pushd "$(realpath "$(dirname "$0")")"
-
-CURR_DIR="${PWD}"
-
-BUILD_TYPE="$1"
-if [ -z "$BUILD_TYPE" ]; then
- BUILD_TYPE=Release
-fi
-
-# CI: already had emsdk installed.
-if ! command -v emcc; then
- if [ ! -d ../../build/emsdk ]; then
- git clone https://github.com/emscripten-core/emsdk.git ../../build/emsdk
- fi
-
- pushd ../../build/emsdk
- ./emsdk install 3.0.0
- ./emsdk activate 3.0.0
- source ./emsdk_env.sh
- popd # ../../build/emsdk
-fi
-
-mkdir -p build/wasm
-pushd build/wasm
-emcmake cmake -DCMAKE_BUILD_TYPE="$BUILD_TYPE" ../..
-make -j
-TARGET_FILES="
- KgmLegacy.js
- KgmWasm.js
- KgmWasm.wasm
- KgmWasmBundle.js
-"
-
-cp $TARGET_FILES "${CURR_DIR}/"
-popd # build/wasm
-
-popd
diff --git a/src/KgmWasm/kgm.hpp b/src/KgmWasm/kgm.hpp
deleted file mode 100644
index b3493e5..0000000
--- a/src/KgmWasm/kgm.hpp
+++ /dev/null
@@ -1,112 +0,0 @@
-#include
-
-std::vector VprHeader = {
- 0x05, 0x28, 0xBC, 0x96, 0xE9, 0xE4, 0x5A, 0x43,
- 0x91, 0xAA, 0xBD, 0xD0, 0x7A, 0xF5, 0x36, 0x31 };
-std::vector KgmHeader = {
- 0x7C, 0xD5, 0x32, 0xEB, 0x86, 0x02, 0x7F, 0x4B,
- 0xA8, 0xAF, 0xA6, 0x8E, 0x0F, 0xFF, 0x99, 0x14 };
-std::vector VprMaskDiff = {
- 0x25, 0xDF, 0xE8, 0xA6, 0x75, 0x1E, 0x75, 0x0E,
- 0x2F, 0x80, 0xF3, 0x2D, 0xB8, 0xB6, 0xE3, 0x11, 0x00 };
-
-std::vector MaskV2;
-
-std::vector table1 = {
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x01, 0x21, 0x01, 0x61, 0x01, 0x21, 0x01, 0xe1, 0x01, 0x21, 0x01, 0x61, 0x01, 0x21, 0x01,
- 0xd2, 0x23, 0x02, 0x02, 0x42, 0x42, 0x02, 0x02, 0xc2, 0xc2, 0x02, 0x02, 0x42, 0x42, 0x02, 0x02,
- 0xd3, 0xd3, 0x02, 0x03, 0x63, 0x43, 0x63, 0x03, 0xe3, 0xc3, 0xe3, 0x03, 0x63, 0x43, 0x63, 0x03,
- 0x94, 0xb4, 0x94, 0x65, 0x04, 0x04, 0x04, 0x04, 0x84, 0x84, 0x84, 0x84, 0x04, 0x04, 0x04, 0x04,
- 0x95, 0x95, 0x95, 0x95, 0x04, 0x05, 0x25, 0x05, 0xe5, 0x85, 0xa5, 0x85, 0xe5, 0x05, 0x25, 0x05,
- 0xd6, 0xb6, 0x96, 0xb6, 0xd6, 0x27, 0x06, 0x06, 0xc6, 0xc6, 0x86, 0x86, 0xc6, 0xc6, 0x06, 0x06,
- 0xd7, 0xd7, 0x97, 0x97, 0xd7, 0xd7, 0x06, 0x07, 0xe7, 0xc7, 0xe7, 0x87, 0xe7, 0xc7, 0xe7, 0x07,
- 0x18, 0x38, 0x18, 0x78, 0x18, 0x38, 0x18, 0xe9, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
- 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x08, 0x09, 0x29, 0x09, 0x69, 0x09, 0x29, 0x09,
- 0xda, 0x3a, 0x1a, 0x3a, 0x5a, 0x3a, 0x1a, 0x3a, 0xda, 0x2b, 0x0a, 0x0a, 0x4a, 0x4a, 0x0a, 0x0a,
- 0xdb, 0xdb, 0x1b, 0x1b, 0x5b, 0x5b, 0x1b, 0x1b, 0xdb, 0xdb, 0x0a, 0x0b, 0x6b, 0x4b, 0x6b, 0x0b,
- 0x9c, 0xbc, 0x9c, 0x7c, 0x1c, 0x3c, 0x1c, 0x7c, 0x9c, 0xbc, 0x9c, 0x6d, 0x0c, 0x0c, 0x0c, 0x0c,
- 0x9d, 0x9d, 0x9d, 0x9d, 0x1d, 0x1d, 0x1d, 0x1d, 0x9d, 0x9d, 0x9d, 0x9d, 0x0c, 0x0d, 0x2d, 0x0d,
- 0xde, 0xbe, 0x9e, 0xbe, 0xde, 0x3e, 0x1e, 0x3e, 0xde, 0xbe, 0x9e, 0xbe, 0xde, 0x2f, 0x0e, 0x0e,
- 0xdf, 0xdf, 0x9f, 0x9f, 0xdf, 0xdf, 0x1f, 0x1f, 0xdf, 0xdf, 0x9f, 0x9f, 0xdf, 0xdf, 0x0e, 0x0f,
- 0x00, 0x20, 0x00, 0x60, 0x00, 0x20, 0x00, 0xe0, 0x00, 0x20, 0x00, 0x60, 0x00, 0x20, 0x00, 0xf1
-};
-
-std::vector table2 = {
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x01, 0x23, 0x01, 0x67, 0x01, 0x23, 0x01, 0xef, 0x01, 0x23, 0x01, 0x67, 0x01, 0x23, 0x01,
- 0xdf, 0x21, 0x02, 0x02, 0x46, 0x46, 0x02, 0x02, 0xce, 0xce, 0x02, 0x02, 0x46, 0x46, 0x02, 0x02,
- 0xde, 0xde, 0x02, 0x03, 0x65, 0x47, 0x65, 0x03, 0xed, 0xcf, 0xed, 0x03, 0x65, 0x47, 0x65, 0x03,
- 0x9d, 0xbf, 0x9d, 0x63, 0x04, 0x04, 0x04, 0x04, 0x8c, 0x8c, 0x8c, 0x8c, 0x04, 0x04, 0x04, 0x04,
- 0x9c, 0x9c, 0x9c, 0x9c, 0x04, 0x05, 0x27, 0x05, 0xeb, 0x8d, 0xaf, 0x8d, 0xeb, 0x05, 0x27, 0x05,
- 0xdb, 0xbd, 0x9f, 0xbd, 0xdb, 0x25, 0x06, 0x06, 0xca, 0xca, 0x8e, 0x8e, 0xca, 0xca, 0x06, 0x06,
- 0xda, 0xda, 0x9e, 0x9e, 0xda, 0xda, 0x06, 0x07, 0xe9, 0xcb, 0xe9, 0x8f, 0xe9, 0xcb, 0xe9, 0x07,
- 0x19, 0x3b, 0x19, 0x7f, 0x19, 0x3b, 0x19, 0xe7, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
- 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x08, 0x09, 0x2b, 0x09, 0x6f, 0x09, 0x2b, 0x09,
- 0xd7, 0x39, 0x1b, 0x39, 0x5f, 0x39, 0x1b, 0x39, 0xd7, 0x29, 0x0a, 0x0a, 0x4e, 0x4e, 0x0a, 0x0a,
- 0xd6, 0xd6, 0x1a, 0x1a, 0x5e, 0x5e, 0x1a, 0x1a, 0xd6, 0xd6, 0x0a, 0x0b, 0x6d, 0x4f, 0x6d, 0x0b,
- 0x95, 0xb7, 0x95, 0x7b, 0x1d, 0x3f, 0x1d, 0x7b, 0x95, 0xb7, 0x95, 0x6b, 0x0c, 0x0c, 0x0c, 0x0c,
- 0x94, 0x94, 0x94, 0x94, 0x1c, 0x1c, 0x1c, 0x1c, 0x94, 0x94, 0x94, 0x94, 0x0c, 0x0d, 0x2f, 0x0d,
- 0xd3, 0xb5, 0x97, 0xb5, 0xd3, 0x3d, 0x1f, 0x3d, 0xd3, 0xb5, 0x97, 0xb5, 0xd3, 0x2d, 0x0e, 0x0e,
- 0xd2, 0xd2, 0x96, 0x96, 0xd2, 0xd2, 0x1e, 0x1e, 0xd2, 0xd2, 0x96, 0x96, 0xd2, 0xd2, 0x0e, 0x0f,
- 0x00, 0x22, 0x00, 0x66, 0x00, 0x22, 0x00, 0xee, 0x00, 0x22, 0x00, 0x66, 0x00, 0x22, 0x00, 0xfe
-};
-
-std::vector MaskV2PreDef = {
- 0xB8, 0xD5, 0x3D, 0xB2, 0xE9, 0xAF, 0x78, 0x8C, 0x83, 0x33, 0x71, 0x51, 0x76, 0xA0, 0xCD, 0x37,
- 0x2F, 0x3E, 0x35, 0x8D, 0xA9, 0xBE, 0x98, 0xB7, 0xE7, 0x8C, 0x22, 0xCE, 0x5A, 0x61, 0xDF, 0x68,
- 0x69, 0x89, 0xFE, 0xA5, 0xB6, 0xDE, 0xA9, 0x77, 0xFC, 0xC8, 0xBD, 0xBD, 0xE5, 0x6D, 0x3E, 0x5A,
- 0x36, 0xEF, 0x69, 0x4E, 0xBE, 0xE1, 0xE9, 0x66, 0x1C, 0xF3, 0xD9, 0x02, 0xB6, 0xF2, 0x12, 0x9B,
- 0x44, 0xD0, 0x6F, 0xB9, 0x35, 0x89, 0xB6, 0x46, 0x6D, 0x73, 0x82, 0x06, 0x69, 0xC1, 0xED, 0xD7,
- 0x85, 0xC2, 0x30, 0xDF, 0xA2, 0x62, 0xBE, 0x79, 0x2D, 0x62, 0x62, 0x3D, 0x0D, 0x7E, 0xBE, 0x48,
- 0x89, 0x23, 0x02, 0xA0, 0xE4, 0xD5, 0x75, 0x51, 0x32, 0x02, 0x53, 0xFD, 0x16, 0x3A, 0x21, 0x3B,
- 0x16, 0x0F, 0xC3, 0xB2, 0xBB, 0xB3, 0xE2, 0xBA, 0x3A, 0x3D, 0x13, 0xEC, 0xF6, 0x01, 0x45, 0x84,
- 0xA5, 0x70, 0x0F, 0x93, 0x49, 0x0C, 0x64, 0xCD, 0x31, 0xD5, 0xCC, 0x4C, 0x07, 0x01, 0x9E, 0x00,
- 0x1A, 0x23, 0x90, 0xBF, 0x88, 0x1E, 0x3B, 0xAB, 0xA6, 0x3E, 0xC4, 0x73, 0x47, 0x10, 0x7E, 0x3B,
- 0x5E, 0xBC, 0xE3, 0x00, 0x84, 0xFF, 0x09, 0xD4, 0xE0, 0x89, 0x0F, 0x5B, 0x58, 0x70, 0x4F, 0xFB,
- 0x65, 0xD8, 0x5C, 0x53, 0x1B, 0xD3, 0xC8, 0xC6, 0xBF, 0xEF, 0x98, 0xB0, 0x50, 0x4F, 0x0F, 0xEA,
- 0xE5, 0x83, 0x58, 0x8C, 0x28, 0x2C, 0x84, 0x67, 0xCD, 0xD0, 0x9E, 0x47, 0xDB, 0x27, 0x50, 0xCA,
- 0xF4, 0x63, 0x63, 0xE8, 0x97, 0x7F, 0x1B, 0x4B, 0x0C, 0xC2, 0xC1, 0x21, 0x4C, 0xCC, 0x58, 0xF5,
- 0x94, 0x52, 0xA3, 0xF3, 0xD3, 0xE0, 0x68, 0xF4, 0x00, 0x23, 0xF3, 0x5E, 0x0A, 0x7B, 0x93, 0xDD,
- 0xAB, 0x12, 0xB2, 0x13, 0xE8, 0x84, 0xD7, 0xA7, 0x9F, 0x0F, 0x32, 0x4C, 0x55, 0x1D, 0x04, 0x36,
- 0x52, 0xDC, 0x03, 0xF3, 0xF9, 0x4E, 0x42, 0xE9, 0x3D, 0x61, 0xEF, 0x7C, 0xB6, 0xB3, 0x93, 0x50,
-};
-
-uint8_t getMask(size_t pos) {
- size_t offset = pos >> 4;
- uint8_t value = 0;
- while (offset >= 0x11) {
- value ^= table1[offset % 272];
- offset >>= 4;
- value ^= table2[offset % 272];
- offset >>= 4;
- }
-
- return MaskV2PreDef[pos % 272] ^ value;
-}
-
-std::vector key(17);
-bool isVpr = false;
-
-size_t PreDec(uint8_t* fileData, size_t size, bool iV) {
- uint32_t headerLen = *(uint32_t*)(fileData + 0x10);
- memcpy(key.data(), (fileData + 0x1C), 0x10);
- key[16] = 0;
- isVpr = iV;
- return headerLen;
-}
-
-void Decrypt(uint8_t* fileData, size_t size, size_t offset) {
- for (size_t i = 0; i < size; ++i) {
- uint8_t med8 = key[(i + offset) % 17] ^ fileData[i];
- med8 ^= (med8 & 0xf) << 4;
-
- uint8_t msk8 = getMask(i + offset);
- msk8 ^= (msk8 & 0xf) << 4;
- fileData[i] = med8 ^ msk8;
-
- if (isVpr) {
- fileData[i] ^= VprMaskDiff[(i + offset) % 17];
- }
- }
-}
diff --git a/src/QmcWasm/CMakeLists.txt b/src/QmcWasm/CMakeLists.txt
deleted file mode 100644
index 066268a..0000000
--- a/src/QmcWasm/CMakeLists.txt
+++ /dev/null
@@ -1,65 +0,0 @@
-# CMakeList.txt : CMake project for QmcWasm, include source and define
-# project specific logic here.
-#
-cmake_minimum_required (VERSION 3.8)
-
-project ("QmcWasm")
-
-set(CMAKE_CXX_STANDARD 14)
-
-include_directories(
- $
-)
-
-# Add source to this project's executable.
-set(RUNTIME_METHODS_LIST
- getValue
- writeArrayToMemory
- UTF8ToString
-)
-list(JOIN RUNTIME_METHODS_LIST "," RUNTIME_METHODS)
-
-set(EMSCRIPTEN_FLAGS
- "--bind"
- "-s NO_DYNAMIC_EXECUTION=1"
- "-s MODULARIZE=1"
- "-s EXPORT_NAME=QmcCryptoModule"
- "-s EXPORTED_RUNTIME_METHODS=${RUNTIME_METHODS}"
-)
-set(EMSCRIPTEN_LEGACY_FLAGS
- ${EMSCRIPTEN_FLAGS}
- "-s WASM=0"
- "--memory-init-file 0"
-)
-set(EMSCRIPTEN_WASM_BUNDLE_FLAGS
- ${EMSCRIPTEN_FLAGS}
- "-s SINGLE_FILE=1"
-)
-
-list(JOIN EMSCRIPTEN_FLAGS " " EMSCRIPTEN_FLAGS_STR)
-list(JOIN EMSCRIPTEN_LEGACY_FLAGS " " EMSCRIPTEN_LEGACY_FLAGS_STR)
-list(JOIN EMSCRIPTEN_WASM_BUNDLE_FLAGS " " EMSCRIPTEN_WASM_BUNDLE_FLAGS_STR)
-
-# Define projects config
-set(WASM_SOURCES
- "QmcWasm.cpp"
-)
-
-add_executable(QmcWasm ${WASM_SOURCES})
-set_target_properties(
- QmcWasm
- PROPERTIES LINK_FLAGS ${EMSCRIPTEN_FLAGS_STR}
-)
-
-add_executable(QmcWasmBundle ${WASM_SOURCES})
-set_target_properties(
- QmcWasmBundle
- PROPERTIES LINK_FLAGS ${EMSCRIPTEN_WASM_BUNDLE_FLAGS_STR}
-)
-
-add_executable(QmcLegacy ${WASM_SOURCES})
-set_target_properties(
- QmcLegacy
- PROPERTIES LINK_FLAGS ${EMSCRIPTEN_LEGACY_FLAGS_STR}
-)
-
diff --git a/src/QmcWasm/QmcWasm.cpp b/src/QmcWasm/QmcWasm.cpp
deleted file mode 100644
index f4fc8c0..0000000
--- a/src/QmcWasm/QmcWasm.cpp
+++ /dev/null
@@ -1,57 +0,0 @@
-// QmcWasm.cpp : Defines the entry point for the application.
-//
-
-#include "QmcWasm.h"
-
-#include "qmc.hpp"
-
-#include
-#include
-
-std::string err = "";
-std::string sid = "";
-QmcDecode e;
-
-int preDec(uintptr_t blob, size_t blobSize, std::string ext)
-{
- if (!e.SetBlob((uint8_t*)blob, blobSize))
- {
- err = "cannot allocate memory";
- return -1;
- }
- int tailSize = e.PreDecode(ext);
- if (e.error != "")
- {
- err = e.error;
- return -1;
- }
- sid = e.songId;
- return tailSize;
-}
-
-size_t decBlob(uintptr_t blob, size_t blobSize, size_t offset)
-{
- if (!e.SetBlob((uint8_t*)blob, blobSize))
- {
- err = "cannot allocate memory";
- return 0;
- }
- std::vector decData = e.Decode(offset);
- if (e.error != "")
- {
- err = e.error;
- return 0;
- }
- memcpy((uint8_t*)blob, decData.data(), decData.size());
- return decData.size();
-}
-
-std::string getErr()
-{
- return err;
-}
-
-std::string getSongId()
-{
- return sid;
-}
diff --git a/src/QmcWasm/QmcWasm.h b/src/QmcWasm/QmcWasm.h
deleted file mode 100644
index 6fd63bf..0000000
--- a/src/QmcWasm/QmcWasm.h
+++ /dev/null
@@ -1,23 +0,0 @@
-// QmcWasm.h : Include file for standard system include files,
-// or project specific include files.
-
-#pragma once
-
-#include
-#include
-
-namespace em = emscripten;
-
-int preDec(uintptr_t blob, size_t blobSize, std::string ext);
-size_t decBlob(uintptr_t blob, size_t blobSize, size_t offset);
-std::string getErr();
-std::string getSongId();
-
-EMSCRIPTEN_BINDINGS(QmcCrypto)
-{
- em::function("getErr", &getErr);
- em::function("getSongId", &getSongId);
-
- em::function("preDec", &preDec, em::allow_raw_pointers());
- em::function("decBlob", &decBlob, em::allow_raw_pointers());
-}
diff --git a/src/QmcWasm/README.md b/src/QmcWasm/README.md
deleted file mode 100644
index 035fe65..0000000
--- a/src/QmcWasm/README.md
+++ /dev/null
@@ -1,9 +0,0 @@
-# QmcWasm
-
-## 构建
-
-在 Linux 环境下执行 `bash build-wasm` 即可构建。
-
-## Build
-
-Linux environment required. Build wasm binary by execute `bash build-wasm`.
diff --git a/src/QmcWasm/TencentTea.hpp b/src/QmcWasm/TencentTea.hpp
deleted file mode 100644
index 4f635a7..0000000
--- a/src/QmcWasm/TencentTea.hpp
+++ /dev/null
@@ -1,289 +0,0 @@
-#ifndef QQMUSIC_CPP_TENCENTTEA_HPP
-#define QQMUSIC_CPP_TENCENTTEA_HPP
-
-#include
-#include
-#include
-#include
-#include
-#include
-
-const uint32_t DELTA = 0x9e3779b9;
-
-#define ROUNDS 32
-#define SALT_LEN 2
-#define ZERO_LEN 7
-
-void TeaDecryptECB(uint8_t* src, uint8_t* dst, std::vector key, size_t rounds = ROUNDS) {
- if (key.size() != 16 || (rounds & 1) != 0)
- {
- return;
- }
- uint32_t y, z, sum;
- uint32_t k[4];
- int i;
-
- //now encrypted buf is TCP/IP-endian;
- //TCP/IP network byte order (which is big-endian).
- y = ntohl(*((uint32_t*)src));
- z = ntohl(*((uint32_t*)(src + 4)));
- //std::cout << ntohl(0x0a3aea41);
-
- for (i = 0; i < 4; i++) {
- //key is TCP/IP-endian;
- k[i] = ntohl(*((uint32_t*)(key.data() + i * 4)));
- }
-
- sum = (DELTA * rounds);
- for (i = 0; i < rounds; i++) {
- z -= ((y << 4) + k[2]) ^ (y + sum) ^ ((y >> 5) + k[3]);
- y -= ((z << 4) + k[0]) ^ (z + sum) ^ ((z >> 5) + k[1]);
- sum -= DELTA;
- }
-
- *((uint32_t*)dst) = ntohl(y);
- *((uint32_t*)(dst + 4)) = ntohl(z);
-
- //now plain-text is TCP/IP-endian;
-}
-
-void TeaEncryptECB(uint8_t* src, uint8_t* dst, std::vector key, size_t rounds = ROUNDS) {
- if (key.size() != 16 || (rounds & 1) != 0)
- {
- return;
- }
- uint32_t y, z, sum;
- uint32_t k[4];
- int i;
-
- //now encrypted buf is TCP/IP-endian;
- //TCP/IP network byte order (which is big-endian).
- y = ntohl(*((uint32_t*)src));
- z = ntohl(*((uint32_t*)(src + 4)));
- //std::cout << ntohl(0x0a3aea41);
-
- for (i = 0; i < 4; i++) {
- //key is TCP/IP-endian;
- k[i] = ntohl(*((uint32_t*)(key.data() + i * 4)));
- }
-
- sum = 0;
- for (i = 0; i < rounds; i++) {
- sum += DELTA;
- y += ((z << 4) + k[0]) ^ (z + sum) ^ ((z >> 5) + k[1]);
- z += ((y << 4) + k[2]) ^ (y + sum) ^ ((y >> 5) + k[3]);
- }
-
- *((uint32_t*)dst) = ntohl(y);
- *((uint32_t*)(dst + 4)) = ntohl(z);
-
- //now plain-text is TCP/IP-endian;
-}
-
-/*pKeyΪ16byte*/
-/*
- :nInBufLenΪܵIJ(Body);
- :Ϊܺij(8byteı);
-*/
-/*TEA㷨,CBCģʽ*/
-/*ĸʽ:PadLen(1byte)+Padding(var,0-7byte)+Salt(2byte)+Body(var byte)+Zero(7byte)*/
-int encryptTencentTeaLen(int nInBufLen)
-{
-
- int nPadSaltBodyZeroLen/*PadLen(1byte)+Salt+Body+Zeroij*/;
- int nPadlen;
-
- /*BodyȼPadLen,С賤ȱΪ8byte*/
- nPadSaltBodyZeroLen = nInBufLen/*Body*/ + 1 + SALT_LEN + ZERO_LEN/*PadLen(1byte)+Salt(2byte)+Zero(7byte)*/;
- if ((nPadlen = nPadSaltBodyZeroLen % 8)) /*len=nSaltBodyZeroLen%8*/
- {
- /*ģ80貹0,17,26,...,71*/
- nPadlen = 8 - nPadlen;
- }
-
- return nPadlen;
-}
-
-/*pKeyΪ16byte*/
-/*
- :pInBufΪܵIJ(Body),nInBufLenΪpInBuf;
- :pOutBufΪĸʽ,pOutBufLenΪpOutBufij8byteı;
-*/
-/*TEA㷨,CBCģʽ*/
-/*ĸʽ:PadLen(1byte)+Padding(var,0-7byte)+Salt(2byte)+Body(var byte)+Zero(7byte)*/
-bool encryptTencentTea(std::vector inBuf, std::vector key, std::vector &outBuf)
-{
- srand(time(0));
- int nPadlen = encryptTencentTeaLen(inBuf.size());
- size_t ivCrypt;
- std::vector srcBuf;
- srcBuf.resize(8);
- std::vector ivPlain;
- ivPlain.resize(8);
- int tmpIdx, i, j;
-
- /*ܵһ(8byte),ȡǰ10byte*/
- srcBuf[0] = (((char)rand()) & 0x0f8)/*λPadLen,*/ | (char)nPadlen;
- tmpIdx = 1; /*tmpIdxָsrcBufһλ*/
-
- while (nPadlen--) srcBuf[tmpIdx++] = (char)rand(); /*Padding*/
-
- /*come here, tmpIdx must <= 8*/
-
- for (i = 0; i < 8; i++) ivPlain[i] = 0;
- ivCrypt = 0;//ivPlain /*make zero iv*/
-
- auto outBufPos = 0; /*init outBufPos*/
-
-#define cryptBlock {\
- /*tmpIdx==8*/\
- outBuf.resize(outBuf.size() + 8);\
- for (j = 0; j < 8; j++) /*ǰǰ8byte(iv_cryptָ)*/\
- srcBuf[j] ^= outBuf[j + ivCrypt];\
- /*pOutBufferpInBufferΪ8byte, pKeyΪ16byte*/\
- /**/\
- TeaEncryptECB(srcBuf.data(), outBuf.data()+outBufPos, key, 16);\
- for (j = 0; j < 8; j++) /*ܺǰ8byte(iv_plainָ)*/\
- outBuf[j + outBufPos] ^= ivPlain[j];\
- /*浱ǰiv_plain*/\
- for (j = 0; j < 8; j++) ivPlain[j] = srcBuf[j];\
- /*iv_crypt*/\
- tmpIdx = 0;\
- ivCrypt = outBufPos;\
- outBufPos += 8;\
- }
-
-
- for (i = 1; i <= SALT_LEN;) /*Salt(2byte)*/
- {
- if (tmpIdx < 8)
- {
- srcBuf[tmpIdx++] = (char)rand();
- i++; /*i inc in here*/
- }
- if (tmpIdx == 8)
- {
- cryptBlock
- }
- }
-
- /*tmpIdxָsrcBufһλ*/
-
- auto inBufPos = 0;
- while (inBufPos < inBuf.size())
- {
- if (tmpIdx < 8)
- {
- srcBuf[tmpIdx++] = inBuf[inBufPos];
- inBufPos++;
- }
- if (tmpIdx == 8)
- {
- cryptBlock
- }
- }
-
- /*tmpIdxָsrcBufһλ*/
-
- for (i = 1; i <= ZERO_LEN;)
- {
- if (tmpIdx < 8)
- {
- srcBuf[tmpIdx++] = 0;
- i++; //i inc in here
- }
- if (tmpIdx == 8)
- {
- cryptBlock
- }
- }
- return true;
-#undef cryptBlock
-}
-
-bool decryptTencentTea(std::vector inBuf, std::vector key, std::vector &out) {
- if (inBuf.size() % 8 != 0) {
- return false;
- //inBuf size not a multiple of the block size
- }
- if (inBuf.size() < 16) {
- return false;
- //inBuf size too small
- }
-
- std::vector tmpBuf;
- tmpBuf.resize(8);
-
- TeaDecryptECB(inBuf.data(), tmpBuf.data(), key, 16);
-
- auto nPadLen = tmpBuf[0] & 0x7; //ֻҪλ
- /*ĸʽ:PadLen(1byte)+Padding(var,0-7byte)+Salt(2byte)+Body(var byte)+Zero(7byte)*/
- auto outLen = inBuf.size() - 1 /*PadLen*/ - nPadLen - SALT_LEN - ZERO_LEN;
- std::vector outBuf;
- outBuf.resize(outLen);
-
- std::vector ivPrev;
- ivPrev.resize(8);
- std::vector ivCur;
- ivCur.resize(8);
- for (size_t i = 0; i < 8; i++)
- {
- ivCur[i] = inBuf[i]; // init iv
- }
- auto inBufPos = 8;
-
- // Padding Len Padding
- auto tmpIdx = 1 + nPadLen;
-
- // CBC IV
-#define cryptBlock {\
- ivPrev = ivCur;\
- for (size_t k = inBufPos; k < inBufPos + 8; k++)\
- {\
- ivCur[k - inBufPos] = inBuf[k];\
- }\
- for (size_t j = 0; j < 8; j++) {\
- tmpBuf[j] ^= ivCur[j];\
- }\
- TeaDecryptECB(tmpBuf.data(), tmpBuf.data(), key, 16);\
- inBufPos += 8;\
- tmpIdx = 0;\
- }
-
- // Salt
- for (size_t i = 1; i <= SALT_LEN; ) {
- if (tmpIdx < 8) {
- tmpIdx++;
- i++;
- }
- else {
- cryptBlock
- }
- }
-
- // ԭ
- auto outBufPos = 0;
- while (outBufPos < outLen) {
- if (tmpIdx < 8) {
- outBuf[outBufPos] = tmpBuf[tmpIdx] ^ ivPrev[tmpIdx];
- outBufPos++;
- tmpIdx++;
- }
- else {
- cryptBlock
- }
- }
-
- // УZero
- for (size_t i = 1; i <= ZERO_LEN; i++) {
- if (tmpBuf[i] != ivPrev[i]) {
- return false;
- //zero check failed
- }
- }
- out = outBuf;
- return true;
-#undef cryptBlock
-}
-
-#endif //QQMUSIC_CPP_TENCENTTEA_HPP
diff --git a/src/QmcWasm/base64.hpp b/src/QmcWasm/base64.hpp
deleted file mode 100644
index b3b6aca..0000000
--- a/src/QmcWasm/base64.hpp
+++ /dev/null
@@ -1,207 +0,0 @@
-//
-// Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
-//
-// Distributed under the Boost Software License, Version 1.0. (See accompanying
-// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
-//
-// Official repository: https://github.com/boostorg/beast
-//
-
-/*
- Portions from http://www.adp-gmbh.ch/cpp/common/base64.html
- Copyright notice:
-
- base64.cpp and base64.h
-
- Copyright (C) 2004-2008 Rene Nyffenegger
-
- This source code is provided 'as-is', without any express or implied
- warranty. In no event will the author be held liable for any damages
- arising from the use of this software.
-
- Permission is granted to anyone to use this software for any purpose,
- including commercial applications, and to alter it and redistribute it
- freely, subject to the following restrictions:
-
- 1. The origin of this source code must not be misrepresented; you must not
- claim that you wrote the original source code. If you use this source code
- in a product, an acknowledgment in the product documentation would be
- appreciated but is not required.
-
- 2. Altered source versions must be plainly marked as such, and must not be
- misrepresented as being the original source code.
-
- 3. This notice may not be removed or altered from any source distribution.
-
- Rene Nyffenegger rene.nyffenegger@adp-gmbh.ch
-*/
-
-#ifndef BASE64_HPP
-#define BASE64_HPP
-
-#include
-#include
-#include
-
-namespace base64 {
-
- /// Returns max chars needed to encode a base64 string
- std::size_t constexpr
- encoded_size(std::size_t n)
- {
- return 4 * ((n + 2) / 3);
- }
-
- /// Returns max bytes needed to decode a base64 string
- inline
- std::size_t constexpr
- decoded_size(std::size_t n)
- {
- return n / 4 * 3; // requires n&3==0, smaller
- }
-
- char const*
- get_alphabet()
- {
- static char constexpr tab[] = {
- "ABCDEFGHIJKLMNOP"
- "QRSTUVWXYZabcdef"
- "ghijklmnopqrstuv"
- "wxyz0123456789+/"
- };
- return &tab[0];
- }
-
- signed char const*
- get_inverse()
- {
- static signed char constexpr tab[] = {
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0-15
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 16-31
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, // 32-47
- 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, // 48-63
- -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, // 64-79
- 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, // 80-95
- -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, // 96-111
- 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, // 112-127
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 128-143
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 144-159
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 160-175
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 176-191
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 192-207
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 208-223
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 224-239
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 // 240-255
- };
- return &tab[0];
- }
-
- /** Encode a series of octets as a padded, base64 string.
-
- The resulting string will not be null terminated.
-
- @par Requires
-
- The memory pointed to by `out` points to valid memory
- of at least `encoded_size(len)` bytes.
-
- @return The number of characters written to `out`. This
- will exclude any null termination.
- */
- std::size_t
- encode(void* dest, void const* src, std::size_t len)
- {
- char* out = static_cast(dest);
- char const* in = static_cast(src);
- auto const tab = base64::get_alphabet();
-
- for (auto n = len / 3; n--;)
- {
- *out++ = tab[(in[0] & 0xfc) >> 2];
- *out++ = tab[((in[0] & 0x03) << 4) + ((in[1] & 0xf0) >> 4)];
- *out++ = tab[((in[2] & 0xc0) >> 6) + ((in[1] & 0x0f) << 2)];
- *out++ = tab[in[2] & 0x3f];
- in += 3;
- }
-
- switch (len % 3)
- {
- case 2:
- *out++ = tab[(in[0] & 0xfc) >> 2];
- *out++ = tab[((in[0] & 0x03) << 4) + ((in[1] & 0xf0) >> 4)];
- *out++ = tab[(in[1] & 0x0f) << 2];
- *out++ = '=';
- break;
-
- case 1:
- *out++ = tab[(in[0] & 0xfc) >> 2];
- *out++ = tab[((in[0] & 0x03) << 4)];
- *out++ = '=';
- *out++ = '=';
- break;
-
- case 0:
- break;
- }
-
- return out - static_cast(dest);
- }
-
- /** Decode a padded base64 string into a series of octets.
-
- @par Requires
-
- The memory pointed to by `out` points to valid memory
- of at least `decoded_size(len)` bytes.
-
- @return The number of octets written to `out`, and
- the number of characters read from the input string,
- expressed as a pair.
- */
- std::pair
- decode(void* dest, char const* src, std::size_t len)
- {
- char* out = static_cast(dest);
- auto in = reinterpret_cast(src);
- unsigned char c3[3], c4[4];
- int i = 0;
- int j = 0;
-
- auto const inverse = base64::get_inverse();
-
- while (len-- && *in != '=')
- {
- auto const v = inverse[*in];
- if (v == -1)
- break;
- ++in;
- c4[i] = v;
- if (++i == 4)
- {
- c3[0] = (c4[0] << 2) + ((c4[1] & 0x30) >> 4);
- c3[1] = ((c4[1] & 0xf) << 4) + ((c4[2] & 0x3c) >> 2);
- c3[2] = ((c4[2] & 0x3) << 6) + c4[3];
-
- for (i = 0; i < 3; i++)
- *out++ = c3[i];
- i = 0;
- }
- }
-
- if (i)
- {
- c3[0] = (c4[0] << 2) + ((c4[1] & 0x30) >> 4);
- c3[1] = ((c4[1] & 0xf) << 4) + ((c4[2] & 0x3c) >> 2);
- c3[2] = ((c4[2] & 0x3) << 6) + c4[3];
-
- for (j = 0; j < i - 1; j++)
- *out++ = c3[j];
- }
-
- return { out - static_cast(dest),
- in - reinterpret_cast(src) };
- }
-
-} // base64
-
-#endif
diff --git a/src/QmcWasm/build-wasm b/src/QmcWasm/build-wasm
deleted file mode 100755
index 4cd1640..0000000
--- a/src/QmcWasm/build-wasm
+++ /dev/null
@@ -1,41 +0,0 @@
-#!/usr/bin/env bash
-
-set -e
-
-pushd "$(realpath "$(dirname "$0")")"
-
-CURR_DIR="${PWD}"
-
-BUILD_TYPE="$1"
-if [ -z "$BUILD_TYPE" ]; then
- BUILD_TYPE=Release
-fi
-
-# CI: already had emsdk installed.
-if ! command -v emcc; then
- if [ ! -d ../../build/emsdk ]; then
- git clone https://github.com/emscripten-core/emsdk.git ../../build/emsdk
- fi
-
- pushd ../../build/emsdk
- ./emsdk install 3.0.0
- ./emsdk activate 3.0.0
- source ./emsdk_env.sh
- popd # ../../build/emsdk
-fi
-
-mkdir -p build/wasm
-pushd build/wasm
-emcmake cmake -DCMAKE_BUILD_TYPE="$BUILD_TYPE" ../..
-make -j
-TARGET_FILES="
- QmcLegacy.js
- QmcWasm.js
- QmcWasm.wasm
- QmcWasmBundle.js
-"
-
-cp $TARGET_FILES "${CURR_DIR}/"
-popd # build/wasm
-
-popd
diff --git a/src/QmcWasm/qmc.hpp b/src/QmcWasm/qmc.hpp
deleted file mode 100644
index 3f26ecf..0000000
--- a/src/QmcWasm/qmc.hpp
+++ /dev/null
@@ -1,230 +0,0 @@
-#include
-#include
-#include
-#include
-#include "qmc_key.hpp"
-#include "qmc_cipher.hpp"
-
-class QmcDecode {
-private:
- std::vector blobData;
-
- std::vector rawKeyBuf;
- std::string cipherType = "";
-
- size_t dataOffset = 0;
- size_t keySize = 0;
- int mediaVer = 0;
-
- std::string checkType(std::string fn) {
- if (fn.find(".qmc") < fn.size() || fn.find(".m") < fn.size())
- {
- std::string buf_tag = "";
- for (int i = 4; i > 0; --i)
- {
- buf_tag += *((char*)blobData.data() + blobData.size() - i);
- }
- if (buf_tag == "QTag")
- {
- keySize = ntohl(*(uint32_t*)(blobData.data() + blobData.size() - 8));
- return "QTag";
- }
- else if (buf_tag == "STag")
- {
- return "STag";
- }
- else
- {
- keySize = (*(uint32_t*)(blobData.data() + blobData.size() - 4));
- if (keySize < 0x400)
- {
- return "Map/RC4";
- }
- else
- {
- keySize = 0;
- return "Static";
- }
- }
- }
- else if (fn.find(".cache") < fn.size())
- {
- return "cache";
- }
- else if (fn.find(".tm") < fn.size())
- {
- return "ios";
- }
- else
- {
- return "invalid";
- }
- }
-
- bool parseRawKeyQTag() {
- std::string ketStr = "";
- std::string::size_type index = 0;
- ketStr.append((char*)rawKeyBuf.data(), rawKeyBuf.size());
- index = ketStr.find(",", 0);
- if (index != std::string::npos)
- {
- rawKeyBuf.resize(index);
- }
- else
- {
- return false;
- }
- ketStr = ketStr.substr(index + 1);
- index = ketStr.find(",", 0);
- if (index != std::string::npos)
- {
- this->songId = ketStr.substr(0, index);
- }
- else
- {
- return false;
- }
- ketStr = ketStr.substr(index + 1);
- index = ketStr.find(",", 0);
- if (index == std::string::npos)
- {
- this->mediaVer = std::stoi(ketStr);
- }
- else
- {
- return false;
- }
- return true;
- }
-
- bool readRawKey(size_t tailSize) {
- // get raw key data length
- rawKeyBuf.resize(keySize);
- if (rawKeyBuf.size() != keySize) {
- return false;
- }
- for (size_t i = 0; i < keySize; i++)
- {
- rawKeyBuf[i] = blobData[i + blobData.size() - (tailSize + keySize)];
- }
- return true;
- }
-
- void DecodeStatic();
-
- void DecodeMapRC4();
-
- void DecodeCache();
-
- void DecodeTm();
-
-public:
- bool SetBlob(uint8_t* blob, size_t blobSize) {
- blobData.resize(blobSize);
- if (blobData.size() != blobSize) {
- return false;
- }
- memcpy(blobData.data(), blob, blobSize);
- return true;
- }
-
- int PreDecode(std::string ext) {
- cipherType = checkType(ext);
- size_t tailSize = 0;
- if (cipherType == "invalid" || cipherType == "STag") {
- error = "file is invalid or not supported (Please downgrade your app).";
- return -1;
- }
- if (cipherType == "QTag") {
- tailSize = 8;
- }
- else if (cipherType == "Map/RC4") {
- tailSize = 4;
- }
- if (keySize > 0) {
- if (!readRawKey(tailSize)) {
- error = "cannot read embedded key from file";
- return -1;
- }
- if (tailSize == 8) {
- cipherType = "Map/RC4";
- if (!parseRawKeyQTag()) {
- error = "cannot parse embedded key";
- return -1;
- }
- }
- std::vector tmp;
- if (!QmcDecryptKey(rawKeyBuf, tmp)) {
- error = "cannot decrypt embedded key";
- return -1;
- }
- rawKeyBuf = tmp;
- }
- return keySize + tailSize;
- }
-
- std::vector Decode(size_t offset);
-
- std::string songId = "";
- std::string error = "";
-};
-
-void QmcDecode::DecodeStatic()
-{
- QmcStaticCipher sc;
- sc.proc(blobData, dataOffset);
-}
-
-void QmcDecode::DecodeMapRC4() {
- if (rawKeyBuf.size() > 300)
- {
- QmcRC4Cipher c(rawKeyBuf, 2);
- c.proc(blobData, dataOffset);
- }
- else
- {
- QmcMapCipher c(rawKeyBuf, 2);
- c.proc(blobData, dataOffset);
- }
-}
-
-void QmcDecode::DecodeCache()
-{
- for (size_t i = 0; i < blobData.size(); i++) {
- blobData[i] ^= 0xf4;
- blobData[i] = ((blobData[i] & 0b00111111) << 2) | (blobData[i] >> 6); // rol 2
- }
-}
-
-void QmcDecode::DecodeTm()
-{
- uint8_t const TM_HEADER[] = { 0x00, 0x00, 0x00, 0x20, 0x66, 0x74, 0x79, 0x70 };
- for (size_t cur = dataOffset, i = 0; cur < 8 && i < blobData.size(); ++cur, ++i) {
- blobData[i] = TM_HEADER[dataOffset];
- }
-}
-
-std::vector QmcDecode::Decode(size_t offset)
-{
- dataOffset = offset;
- if (cipherType == "Map/RC4")
- {
- DecodeMapRC4();
- }
- else if (cipherType == "Static")
- {
- DecodeStatic();
- }
- else if (cipherType == "cache")
- {
- DecodeCache();
- }
- else if (cipherType == "ios")
- {
- DecodeTm();
- }
- else {
- error = "File is invalid or encryption type is not supported.";
- }
- return blobData;
-}
diff --git a/src/QmcWasm/qmc_cipher.hpp b/src/QmcWasm/qmc_cipher.hpp
deleted file mode 100644
index 8dc2b18..0000000
--- a/src/QmcWasm/qmc_cipher.hpp
+++ /dev/null
@@ -1,290 +0,0 @@
-#include
-#include
-class QmcStaticCipher {
-private:
- uint8_t staticCipherBox[256] = {
- 0x77, 0x48, 0x32, 0x73, 0xDE, 0xF2, 0xC0, 0xC8, //0x00
- 0x95, 0xEC, 0x30, 0xB2, 0x51, 0xC3, 0xE1, 0xA0, //0x08
- 0x9E, 0xE6, 0x9D, 0xCF, 0xFA, 0x7F, 0x14, 0xD1, //0x10
- 0xCE, 0xB8, 0xDC, 0xC3, 0x4A, 0x67, 0x93, 0xD6, //0x18
- 0x28, 0xC2, 0x91, 0x70, 0xCA, 0x8D, 0xA2, 0xA4, //0x20
- 0xF0, 0x08, 0x61, 0x90, 0x7E, 0x6F, 0xA2, 0xE0, //0x28
- 0xEB, 0xAE, 0x3E, 0xB6, 0x67, 0xC7, 0x92, 0xF4, //0x30
- 0x91, 0xB5, 0xF6, 0x6C, 0x5E, 0x84, 0x40, 0xF7, //0x38
- 0xF3, 0x1B, 0x02, 0x7F, 0xD5, 0xAB, 0x41, 0x89, //0x40
- 0x28, 0xF4, 0x25, 0xCC, 0x52, 0x11, 0xAD, 0x43, //0x48
- 0x68, 0xA6, 0x41, 0x8B, 0x84, 0xB5, 0xFF, 0x2C, //0x50
- 0x92, 0x4A, 0x26, 0xD8, 0x47, 0x6A, 0x7C, 0x95, //0x58
- 0x61, 0xCC, 0xE6, 0xCB, 0xBB, 0x3F, 0x47, 0x58, //0x60
- 0x89, 0x75, 0xC3, 0x75, 0xA1, 0xD9, 0xAF, 0xCC, //0x68
- 0x08, 0x73, 0x17, 0xDC, 0xAA, 0x9A, 0xA2, 0x16, //0x70
- 0x41, 0xD8, 0xA2, 0x06, 0xC6, 0x8B, 0xFC, 0x66, //0x78
- 0x34, 0x9F, 0xCF, 0x18, 0x23, 0xA0, 0x0A, 0x74, //0x80
- 0xE7, 0x2B, 0x27, 0x70, 0x92, 0xE9, 0xAF, 0x37, //0x88
- 0xE6, 0x8C, 0xA7, 0xBC, 0x62, 0x65, 0x9C, 0xC2, //0x90
- 0x08, 0xC9, 0x88, 0xB3, 0xF3, 0x43, 0xAC, 0x74, //0x98
- 0x2C, 0x0F, 0xD4, 0xAF, 0xA1, 0xC3, 0x01, 0x64, //0xA0
- 0x95, 0x4E, 0x48, 0x9F, 0xF4, 0x35, 0x78, 0x95, //0xA8
- 0x7A, 0x39, 0xD6, 0x6A, 0xA0, 0x6D, 0x40, 0xE8, //0xB0
- 0x4F, 0xA8, 0xEF, 0x11, 0x1D, 0xF3, 0x1B, 0x3F, //0xB8
- 0x3F, 0x07, 0xDD, 0x6F, 0x5B, 0x19, 0x30, 0x19, //0xC0
- 0xFB, 0xEF, 0x0E, 0x37, 0xF0, 0x0E, 0xCD, 0x16, //0xC8
- 0x49, 0xFE, 0x53, 0x47, 0x13, 0x1A, 0xBD, 0xA4, //0xD0
- 0xF1, 0x40, 0x19, 0x60, 0x0E, 0xED, 0x68, 0x09, //0xD8
- 0x06, 0x5F, 0x4D, 0xCF, 0x3D, 0x1A, 0xFE, 0x20, //0xE0
- 0x77, 0xE4, 0xD9, 0xDA, 0xF9, 0xA4, 0x2B, 0x76, //0xE8
- 0x1C, 0x71, 0xDB, 0x00, 0xBC, 0xFD, 0x0C, 0x6C, //0xF0
- 0xA5, 0x47, 0xF7, 0xF6, 0x00, 0x79, 0x4A, 0x11 //0xF8
- };
-
- uint8_t getMask(size_t offset) {
- if (offset > 0x7fff) offset %= 0x7fff;
- return staticCipherBox[(offset * offset + 27) & 0xff];
- }
-
-public:
- void proc(std::vector& buf, size_t offset) {
- for (size_t i = 0; i < buf.size(); i++) {
- buf[i] ^= getMask(offset + i);
- }
- }
-};
-
-class QmcMapCipher {
-private:
- std::vector key;
-
- uint8_t rotate(uint8_t value, size_t bits) {
- auto rotate = (bits + 4) % 8;
- auto left = value << rotate;
- auto right = value >> rotate;
- return (left | right) & 0xff;
- }
-
- uint8_t getMask(size_t offset) {
- if (offset > 0x7fff) offset %= 0x7fff;
-
- const auto idx = (offset * offset + 71214) % key.size();
- return rotate(key[idx], idx & 0x7);
- }
-
-public:
- QmcMapCipher(std::vector &argKey, short operation) {
- if (operation == 2)
- {
- if (argKey.size() == 0) {
- return;
- }
- }
- else if (operation == 1)
- {
- const char WordList[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
- srand(time(0));
- uint32_t number = 0;
- while (number > 300 || number == 0)
- {
- number = rand();
- }
- argKey.resize(number);
- for (int i = 0; i < argKey.size(); i++) {
- number = rand();
- argKey[i] = WordList[number % 62];
- }
- }
- else
- {
- return;
- }
-
- key = argKey;
- }
-
- void proc(std::vector& buf, size_t offset) {
- for (size_t i = 0; i < buf.size(); i++) {
- buf[i] ^= getMask(offset + i);
- }
- }
-};
-
-class QmcRC4Cipher {
-public:
- void proc(std::vector& buf, size_t offset) {
- // Macro: common code after each process
-#define postProcess(len) \
- { \
- toProcess -= len; \
- processed += len; \
- offset += len; \
- /* no more data */ \
- if (toProcess == 0) { \
- return; \
- } \
- }
-
- size_t toProcess = buf.size();
- size_t processed = 0;
- std::vector tmpbuf;
-
- // ǰ 128 ֽʹòͬĽܷ
- if (offset < FIRST_SEGMENT_SIZE) {
- size_t len_segment = std::min(FIRST_SEGMENT_SIZE - offset, buf.size());
- tmpbuf.resize(len_segment);
- for (size_t i = 0; i < len_segment; i++)
- {
- tmpbuf[i] = buf[processed + i];
- }
- procFirstSegment(tmpbuf, offset);
- for (size_t i = 0; i < len_segment; i++)
- {
- buf[processed + i] = tmpbuf[i];
- }
- postProcess(len_segment);
- }
-
-
- //
- if (offset % SEGMENT_SIZE != 0) {
- size_t len_segment = std::min(SEGMENT_SIZE - (offset % SEGMENT_SIZE), toProcess);
- tmpbuf.resize(len_segment);
- for (size_t i = 0; i < len_segment; i++)
- {
- tmpbuf[i] = buf[processed + i];
- }
- procASegment(tmpbuf, offset);
- for (size_t i = 0; i < len_segment; i++)
- {
- buf[processed + i] = tmpbuf[i];
- }
- postProcess(len_segment);
- }
-
- // ÿһн
- while (toProcess > SEGMENT_SIZE) {
- tmpbuf.resize(SEGMENT_SIZE);
- for (size_t i = 0; i < SEGMENT_SIZE; i++)
- {
- tmpbuf[i] = buf[processed + i];
- }
- procASegment(tmpbuf, offset);
- for (size_t i = 0; i < SEGMENT_SIZE; i++)
- {
- buf[processed + i] = tmpbuf[i];
- }
- postProcess(SEGMENT_SIZE);
- }
-
- if (toProcess > 0) {
- tmpbuf.resize(toProcess);
- for (size_t i = 0; i < toProcess; i++)
- {
- tmpbuf[i] = buf[processed + i];
- }
- procASegment(tmpbuf, offset);
- for (size_t i = 0; i < toProcess; i++)
- {
- buf[processed + i] = tmpbuf[i];
- }
- }
-
-#undef postProcess
- }
-
- QmcRC4Cipher(std::vector& argKey, short operation) {
- if (operation == 2)
- {
- if (argKey.size() == 0) {
- return;
- }
- }
- else if (operation == 1)
- {
- const char WordList[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
- srand(time(0));
- uint32_t number = 0;
- while (number <= 300 || number >= 512)
- {
- number = rand();
- }
- argKey.resize(number);
- for (int i = 0; i < argKey.size(); i++) {
- number = rand();
- argKey[i] = WordList[number % 62];
- }
- }
- else
- {
- return;
- }
-
- key = argKey;
-
- // init seed box
- S.resize(key.size());
- for (size_t i = 0; i < key.size(); ++i) {
- S[i] = i & 0xff;
- }
- size_t j = 0;
- for (size_t i = 0; i < key.size(); ++i) {
- j = (S[i] + j + key[i % key.size()]) % key.size();
- std::swap(S[i], S[j]);
- }
-
- // init hash base
- hash = 1;
- for (size_t i = 0; i < key.size(); i++) {
- uint8_t value = key[i];
-
- // ignore if key char is '\x00'
- if (!value) continue;
-
- auto next_hash = hash * value;
- if (next_hash == 0 || next_hash <= hash) break;
-
- hash = next_hash;
- }
- }
-
-private:
- const size_t FIRST_SEGMENT_SIZE = 0x80;
- const size_t SEGMENT_SIZE = 5120;
-
- std::vector S;
- std::vector key;
- uint32_t hash = 1;
-
- void procFirstSegment(std::vector& buf, size_t offset) {
- for (size_t i = 0; i < buf.size(); i++) {
- buf[i] ^= key[getSegmentKey(offset + i)];
- }
- }
-
- void procASegment(std::vector& buf, size_t offset) {
- // Initialise a new seed box
- std::vector nS;
- nS = S;
-
- // Calculate the number of bytes to skip.
- // The initial "key" derived from segment id, plus the current offset.
- int64_t skipLen = (offset % SEGMENT_SIZE) + getSegmentKey(int(offset / SEGMENT_SIZE));
-
- // decrypt the block
- size_t j = 0;
- size_t k = 0;
- int i = -skipLen;
- for (; i < (int)buf.size(); i++) {
- j = (j + 1) % key.size();
- k = (nS[j] + k) % key.size();
- std::swap(nS[k], nS[j]);
-
- if (i >= 0) {
- buf[i] ^= nS[(nS[j] + nS[k]) % key.size()];
- }
- }
- }
-
- uint64_t getSegmentKey(int id) {
- auto seed = key[id % key.size()];
- uint64_t idx = ((double)hash / ((id + 1) * seed)) * 100.0;
- return idx % key.size();
- }
-};
diff --git a/src/QmcWasm/qmc_key.hpp b/src/QmcWasm/qmc_key.hpp
deleted file mode 100644
index f3178cd..0000000
--- a/src/QmcWasm/qmc_key.hpp
+++ /dev/null
@@ -1,217 +0,0 @@
-#include"TencentTea.hpp"
-#include "base64.hpp"
-
-void simpleMakeKey(uint8_t salt, int length, std::vector &key_buf) {
- for (size_t i = 0; i < length; ++i) {
- double tmp = tan((float)salt + (double)i * 0.1);
- key_buf[i] = 0xFF & (uint8_t)(fabs(tmp) * 100.0);
- }
-}
-
-std::vector v2KeyPrefix = { 0x51, 0x51, 0x4D, 0x75, 0x73, 0x69, 0x63, 0x20, 0x45, 0x6E, 0x63, 0x56, 0x32, 0x2C, 0x4B, 0x65, 0x79, 0x3A };
-
-bool decryptV2Key(std::vector key, std::vector& outVec)
-{
- if (v2KeyPrefix.size() > key.size())
- {
- return true;
- }
- for (size_t i = 0; i < v2KeyPrefix.size(); i++)
- {
- if (key[i] != v2KeyPrefix[i])
- {
- return true;
- }
- }
-
- std::vector mixKey1 = { 0x33, 0x38, 0x36, 0x5A, 0x4A, 0x59, 0x21, 0x40, 0x23, 0x2A, 0x24, 0x25, 0x5E, 0x26, 0x29, 0x28 };
- std::vector mixKey2 = { 0x2A, 0x2A, 0x23, 0x21, 0x28, 0x23, 0x24, 0x25, 0x26, 0x5E, 0x61, 0x31, 0x63, 0x5A, 0x2C, 0x54 };
-
- std::vector out;
- std::vector tmpKey;
- tmpKey.resize(key.size() - 18);
- for (size_t i = 0; i < tmpKey.size(); i++)
- {
- tmpKey[i] = key[18 + i];
- }
- if (!decryptTencentTea(tmpKey, mixKey1, out))
- {
- outVec.resize(0);
- //EncV2 key decode failed.
- return false;
- }
-
- tmpKey.resize(out.size());
- for (size_t i = 0; i < tmpKey.size(); i++)
- {
- tmpKey[i] = out[i];
- }
- out.resize(0);
- if (!decryptTencentTea(tmpKey, mixKey2, out))
- {
- outVec.resize(0);
- //EncV2 key decode failed.
- return false;
- }
-
- outVec.resize(base64::decoded_size(out.size()));
- auto n = base64::decode(outVec.data(), (const char*)(out.data()), out.size()).first;
-
- if (n < 16)
- {
- outVec.resize(0);
- //EncV2 key size is too small.
- return false;
- }
- outVec.resize(n);
-
- return true;
-}
-
-bool encryptV2Key(std::vector key, std::vector& outVec)
-{
- if (key.size() < 16)
- {
- outVec.resize(0);
- //EncV2 key size is too small.
- return false;
- }
-
- std::vector in;
- in.resize(base64::encoded_size(key.size()));
- auto n = base64::encode(in.data(), (const char*)(key.data()), key.size());
- in.resize(n);
-
- std::vector mixKey1 = { 0x33, 0x38, 0x36, 0x5A, 0x4A, 0x59, 0x21, 0x40, 0x23, 0x2A, 0x24, 0x25, 0x5E, 0x26, 0x29, 0x28 };
- std::vector mixKey2 = { 0x2A, 0x2A, 0x23, 0x21, 0x28, 0x23, 0x24, 0x25, 0x26, 0x5E, 0x61, 0x31, 0x63, 0x5A, 0x2C, 0x54 };
-
- std::vector tmpKey;
- if (!encryptTencentTea(in, mixKey2, tmpKey))
- {
- outVec.resize(0);
- //EncV2 key decode failed.
- return false;
- }
- in.resize(tmpKey.size());
- for (size_t i = 0; i < tmpKey.size(); i++)
- {
- in[i] = tmpKey[i];
- }
- tmpKey.resize(0);
-
- if (!encryptTencentTea(in, mixKey1, tmpKey))
- {
- outVec.resize(0);
- //EncV2 key decode failed.
- return false;
- }
- outVec.resize(tmpKey.size() + 18);
- for (size_t i = 0; i < tmpKey.size(); i++)
- {
- outVec[18 + i] = tmpKey[i];
- }
-
- for (size_t i = 0; i < v2KeyPrefix.size(); i++)
- {
- outVec[i] = v2KeyPrefix[i];
- }
-
- return true;
-}
-
-bool QmcDecryptKey(std::vector raw, std::vector &outVec) {
- std::vector rawDec;
- rawDec.resize(base64::decoded_size(raw.size()));
- auto n = base64::decode(rawDec.data(), (const char*)(raw.data()), raw.size()).first;
- if (n < 16) {
- return false;
- //key length is too short
- }
- rawDec.resize(n);
-
- std::vector tmpIn = rawDec;
- if (!decryptV2Key(tmpIn, rawDec))
- {
- //decrypt EncV2 failed.
- return false;
- }
-
- std::vector simpleKey;
- simpleKey.resize(8);
- simpleMakeKey(106, 8, simpleKey);
- std::vector teaKey;
- teaKey.resize(16);
- for (size_t i = 0; i < 8; i++) {
- teaKey[i << 1] = simpleKey[i];
- teaKey[(i << 1) + 1] = rawDec[i];
- }
- std::vector out;
- std::vector tmpRaw;
- tmpRaw.resize(rawDec.size() - 8);
- for (size_t i = 0; i < tmpRaw.size(); i++)
- {
- tmpRaw[i] = rawDec[8 + i];
- }
- if (decryptTencentTea(tmpRaw, teaKey, out))
- {
- rawDec.resize(8 + out.size());
- for (size_t i = 0; i < out.size(); i++)
- {
- rawDec[8 + i] = out[i];
- }
- outVec = rawDec;
- return true;
- }
- else
- {
- return false;
- }
-}
-
-bool QmcEncryptKey(std::vector raw, std::vector& outVec, bool useEncV2 = true) {
- std::vector simpleKey;
- simpleKey.resize(8);
- simpleMakeKey(106, 8, simpleKey);
- std::vector teaKey;
- teaKey.resize(16);
- for (size_t i = 0; i < 8; i++) {
- teaKey[i << 1] = simpleKey[i];
- teaKey[(i << 1) + 1] = raw[i];
- }
- std::vector out;
- out.resize(raw.size() - 8);
- for (size_t i = 0; i < out.size(); i++)
- {
- out[i] = raw[8 + i];
- }
- std::vector tmpRaw;
- if (encryptTencentTea(out, teaKey, tmpRaw))
- {
- raw.resize(tmpRaw.size() + 8);
- for (size_t i = 0; i < tmpRaw.size(); i++)
- {
- raw[i + 8] = tmpRaw[i];
- }
-
- if (useEncV2)
- {
- std::vector tmpIn = raw;
- if (!encryptV2Key(tmpIn, raw))
- {
- //encrypt EncV2 failed.
- return false;
- }
- }
-
- std::vector rawEnc;
- rawEnc.resize(base64::encoded_size(raw.size()));
- auto n = base64::encode(rawEnc.data(), (const char*)(raw.data()), raw.size());
- rawEnc.resize(n);
- outVec = rawEnc;
- return true;
- }
- else
- {
- return false;
- }
-}
diff --git a/src/decrypt/kgm_wasm.ts b/src/decrypt/kgm_wasm.ts
index da45a38..039c3be 100644
--- a/src/decrypt/kgm_wasm.ts
+++ b/src/decrypt/kgm_wasm.ts
@@ -1,4 +1,5 @@
-import KgmCryptoModule from '@/KgmWasm/KgmWasmBundle';
+import { KgmCrypto } from '@xhacker/kgmwasm/KgmWasmBundle';
+import KgmCryptoModule from '@xhacker/kgmwasm/KgmWasmBundle';
import { MergeUint8Array } from '@/utils/MergeUint8Array';
// 每次处理 2M 的数据
@@ -20,26 +21,26 @@ export async function DecryptKgmWasm(kgmBlob: ArrayBuffer, ext: string): Promise
const result: KGMDecryptionResult = { success: false, data: new Uint8Array(), error: '' };
// 初始化模组
- let KgmCrypto: any;
+ let KgmCryptoObj: KgmCrypto;
try {
- KgmCrypto = await KgmCryptoModule();
+ KgmCryptoObj = await KgmCryptoModule();
} catch (err: any) {
result.error = err?.message || 'wasm 加载失败';
return result;
}
- if (!KgmCrypto) {
+ if (!KgmCryptoObj) {
result.error = 'wasm 加载失败';
return result;
}
// 申请内存块,并文件末端数据到 WASM 的内存堆
let kgmBuf = new Uint8Array(kgmBlob);
- const pQmcBuf = KgmCrypto._malloc(DECRYPTION_BUF_SIZE);
- KgmCrypto.writeArrayToMemory(kgmBuf.slice(0, DECRYPTION_BUF_SIZE), pQmcBuf);
+ const pQmcBuf = KgmCryptoObj._malloc(DECRYPTION_BUF_SIZE);
+ KgmCryptoObj.writeArrayToMemory(kgmBuf.slice(0, DECRYPTION_BUF_SIZE), pQmcBuf);
// 进行解密初始化
- const headerSize = KgmCrypto.preDec(pQmcBuf, DECRYPTION_BUF_SIZE, ext);
+ const headerSize = KgmCryptoObj.preDec(pQmcBuf, DECRYPTION_BUF_SIZE, ext);
console.log(headerSize);
kgmBuf = kgmBuf.slice(headerSize);
@@ -51,14 +52,14 @@ export async function DecryptKgmWasm(kgmBlob: ArrayBuffer, ext: string): Promise
// 解密一些片段
const blockData = new Uint8Array(kgmBuf.slice(offset, offset + blockSize));
- KgmCrypto.writeArrayToMemory(blockData, pQmcBuf);
- KgmCrypto.decBlob(pQmcBuf, blockSize, offset);
- decryptedParts.push(KgmCrypto.HEAPU8.slice(pQmcBuf, pQmcBuf + blockSize));
+ KgmCryptoObj.writeArrayToMemory(blockData, pQmcBuf);
+ KgmCryptoObj.decBlob(pQmcBuf, blockSize, offset);
+ decryptedParts.push(KgmCryptoObj.HEAPU8.slice(pQmcBuf, pQmcBuf + blockSize));
offset += blockSize;
bytesToDecrypt -= blockSize;
}
- KgmCrypto._free(pQmcBuf);
+ KgmCryptoObj._free(pQmcBuf);
result.data = MergeUint8Array(decryptedParts);
result.success = true;
diff --git a/src/decrypt/qmc_wasm.ts b/src/decrypt/qmc_wasm.ts
index c2e06db..f747bf4 100644
--- a/src/decrypt/qmc_wasm.ts
+++ b/src/decrypt/qmc_wasm.ts
@@ -1,4 +1,5 @@
-import QmcCryptoModule from '@/QmcWasm/QmcWasmBundle';
+import { QmcCrypto } from '@xhacker/qmcwasm/QmcWasmBundle';
+import QmcCryptoModule from '@xhacker/qmcwasm/QmcWasmBundle';
import { MergeUint8Array } from '@/utils/MergeUint8Array';
// 每次处理 2M 的数据
@@ -21,32 +22,32 @@ export async function DecryptQmcWasm(qmcBlob: ArrayBuffer, ext: string): Promise
const result: QMCDecryptionResult = { success: false, data: new Uint8Array(), songId: 0, error: '' };
// 初始化模组
- let QmcCrypto: any;
+ let QmcCryptoObj: QmcCrypto;
try {
- QmcCrypto = await QmcCryptoModule();
+ QmcCryptoObj = await QmcCryptoModule();
} catch (err: any) {
result.error = err?.message || 'wasm 加载失败';
return result;
}
- if (!QmcCrypto) {
+ if (!QmcCryptoObj) {
result.error = 'wasm 加载失败';
return result;
}
// 申请内存块,并文件末端数据到 WASM 的内存堆
const qmcBuf = new Uint8Array(qmcBlob);
- const pQmcBuf = QmcCrypto._malloc(DECRYPTION_BUF_SIZE);
- QmcCrypto.writeArrayToMemory(qmcBuf.slice(-DECRYPTION_BUF_SIZE), pQmcBuf);
+ const pQmcBuf = QmcCryptoObj._malloc(DECRYPTION_BUF_SIZE);
+ QmcCryptoObj.writeArrayToMemory(qmcBuf.slice(-DECRYPTION_BUF_SIZE), pQmcBuf);
// 进行解密初始化
ext = '.' + ext;
- const tailSize = QmcCrypto.preDec(pQmcBuf, DECRYPTION_BUF_SIZE, ext);
+ const tailSize = QmcCryptoObj.preDec(pQmcBuf, DECRYPTION_BUF_SIZE, ext);
if (tailSize == -1) {
- result.error = QmcCrypto.getError();
+ result.error = QmcCryptoObj.getErr();
return result;
} else {
- result.songId = QmcCrypto.getSongId();
+ result.songId = QmcCryptoObj.getSongId();
result.songId = result.songId == "0" ? 0 : result.songId;
}
@@ -58,13 +59,13 @@ export async function DecryptQmcWasm(qmcBlob: ArrayBuffer, ext: string): Promise
// 解密一些片段
const blockData = new Uint8Array(qmcBuf.slice(offset, offset + blockSize));
- QmcCrypto.writeArrayToMemory(blockData, pQmcBuf);
- decryptedParts.push(QmcCrypto.HEAPU8.slice(pQmcBuf, pQmcBuf + QmcCrypto.decBlob(pQmcBuf, blockSize, offset)));
+ QmcCryptoObj.writeArrayToMemory(blockData, pQmcBuf);
+ decryptedParts.push(QmcCryptoObj.HEAPU8.slice(pQmcBuf, pQmcBuf + QmcCryptoObj.decBlob(pQmcBuf, blockSize, offset)));
offset += blockSize;
bytesToDecrypt -= blockSize;
}
- QmcCrypto._free(pQmcBuf);
+ QmcCryptoObj._free(pQmcBuf);
result.data = MergeUint8Array(decryptedParts);
result.success = true;
From 87fdf1300e60861c62ff1cd1c2c7f7ddabbed5d0 Mon Sep 17 00:00:00 2001
From: xhacker-zzz <959220793@qq.com>
Date: Fri, 20 Jan 2023 20:28:00 +0800
Subject: [PATCH 03/11] update readme
---
README.md | 15 +--------------
1 file changed, 1 insertion(+), 14 deletions(-)
diff --git a/README.md b/README.md
index 34c10be..74f5aba 100644
--- a/README.md
+++ b/README.md
@@ -50,8 +50,6 @@
### 自行构建
-#### JS部分
-
- 环境要求
- nodejs (v16.x)
- npm
@@ -59,6 +57,7 @@
1. 获取项目源代码后安装相关依赖:
```sh
+ npm install
npm ci
```
@@ -76,15 +75,3 @@
```sh
npm run make-extension
```
-
-#### WASM部分
-
-- 环境要求
- - Linux
- - python3
-
-- 运行此目录下的build-wasm
-
- ```sh
- ./scripts/build-wasm.sh
- ```
From 0867a788c62fd38def03aa9e7eaca6f6b0f41c1f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E9=B2=81=E6=A0=91=E4=BA=BA?=
Date: Tue, 6 Jun 2023 22:00:15 +0100
Subject: [PATCH 04/11] chore: use npmjs url instead of npmmirror
---
package-lock.json | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index 26ab988..efcbfea 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -5712,7 +5712,7 @@
},
"node_modules/caniuse-lite": {
"version": "1.0.30001434",
- "resolved": "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001434.tgz",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001434.tgz",
"integrity": "sha512-aOBHrLmTQw//WFa2rcF1If9fa3ypkC1wzqqiKHgfdrXTWcU8C4gKVZT77eQAPWN1APys3+uQ0Df07rKauXGEYA=="
},
"node_modules/case-sensitive-paths-webpack-plugin": {
@@ -25477,7 +25477,7 @@
},
"caniuse-lite": {
"version": "1.0.30001434",
- "resolved": "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001434.tgz",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001434.tgz",
"integrity": "sha512-aOBHrLmTQw//WFa2rcF1If9fa3ypkC1wzqqiKHgfdrXTWcU8C4gKVZT77eQAPWN1APys3+uQ0Df07rKauXGEYA=="
},
"case-sensitive-paths-webpack-plugin": {
From 82bf23f468942ad99b64ce6875f71687e6b4559a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E9=B2=81=E6=A0=91=E4=BA=BA?=
Date: Fri, 7 Jul 2023 12:45:31 +0100
Subject: [PATCH 05/11] strict type check for ncm metadata #48
---
package-lock.json | 4 ++--
package.json | 2 +-
src/decrypt/ncm.ts | 25 ++++++++++++++++++-------
3 files changed, 21 insertions(+), 10 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index efcbfea..7457ca0 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "unlock-music",
- "version": "1.10.4",
+ "version": "1.10.5",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "unlock-music",
- "version": "1.10.4",
+ "version": "1.10.5",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
diff --git a/package.json b/package.json
index fc70d97..1d4549b 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "unlock-music",
- "version": "1.10.4",
+ "version": "1.10.5",
"ext_build": 0,
"updateInfo": "完善音乐标签编辑功能,支持编辑更多标签",
"license": "MIT",
diff --git a/src/decrypt/ncm.ts b/src/decrypt/ncm.ts
index c2f9851..e075cf1 100644
--- a/src/decrypt/ncm.ts
+++ b/src/decrypt/ncm.ts
@@ -139,7 +139,7 @@ class NcmDecrypt {
} else {
result = JSON.parse(plainText.slice(labelIndex + 1));
}
- if (!!result.albumPic) {
+ if (result.albumPic) {
result.albumPic = result.albumPic.replace('http://', 'https://') + '?param=500y500';
}
return result;
@@ -160,11 +160,20 @@ class NcmDecrypt {
// build artists
let artists: string[] = [];
- if (!!this.oriMeta.artist) {
- this.oriMeta.artist.forEach((arr) => artists.push(arr[0]));
+ if (typeof this.oriMeta.artist === 'string') {
+ // v3.0: artist 现在可能是字符串了?
+ artists.push(this.oriMeta.artist);
+ } else if (Array.isArray(this.oriMeta.artist)) {
+ this.oriMeta.artist.forEach((artist) => {
+ if (typeof artist === 'string') {
+ artists.push(artist);
+ } else if (Array.isArray(artist) && artist[0] && typeof artist[0] === 'string') {
+ artists.push(artist[0]);
+ }
+ });
}
- if (artists.length === 0 && !!info.artist) {
+ if (artists.length === 0 && info.artist) {
artists = info.artist
.split(',')
.map((val) => val.trim())
@@ -180,7 +189,7 @@ class NcmDecrypt {
this.image.buffer = await img.getBufferAsync('image/jpeg');
}
} catch (e) {
- console.log('get cover image failed', e);
+ console.log('fetch cover image failed', e);
}
this.newMeta = { title: info.title, artists, album: this.oriMeta.album, picture: this.image?.buffer };
@@ -226,12 +235,14 @@ class NcmDecrypt {
this.audio = this._getAudio(keyBox);
this.format = this.oriMeta.format || SniffAudioExt(this.audio);
this.mime = AudioMimeType[this.format];
- await this._buildMeta();
+
try {
+ await this._buildMeta();
await this._writeMeta();
} catch (e) {
- console.warn('write meta data failed', e);
+ console.warn('build/write meta failed, skip.', e);
}
+
return this.gatherResult();
}
}
From 6f405b5e06279c0aced7e80b7a7c562ad5a95a88 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E9=B2=81=E6=A0=91=E4=BA=BA?=
Date: Sat, 15 Jul 2023 20:10:07 +0100
Subject: [PATCH 06/11] chore: replace url to old github project (fix #53)
---
extension-manifest.json | 8 ++-
package.json | 4 +-
public/index.html | 106 ++++++++++++++++++++++--------
src/App.vue | 10 +--
src/component/ConfigDialog.vue | 4 +-
src/decrypt/__test__/joox.test.ts | 2 +-
src/view/Home.vue | 23 +++++--
7 files changed, 110 insertions(+), 47 deletions(-)
diff --git a/extension-manifest.json b/extension-manifest.json
index eaa9512..b31cb73 100644
--- a/extension-manifest.json
+++ b/extension-manifest.json
@@ -6,11 +6,13 @@
"128": "./img/icons/msapplication-icon-144x144.png"
},
"description": "在任何设备上解锁已购的加密音乐!",
- "permissions": ["storage"],
+ "permissions": [
+ "storage"
+ ],
"offline_enabled": true,
"options_page": "./index.html",
- "homepage_url": "https://github.com/ix64/unlock-music",
+ "homepage_url": "https://git.unlock-music.dev/um/web",
"browser_action": {
"default_popup": "./popup.html"
}
-}
+}
\ No newline at end of file
diff --git a/package.json b/package.json
index 1d4549b..271a267 100644
--- a/package.json
+++ b/package.json
@@ -7,7 +7,7 @@
"description": "Unlock encrypted music file in browser.",
"repository": {
"type": "git",
- "url": "https://github.com/ix64/unlock-music"
+ "url": "https://git.unlock-music.dev/um/web"
},
"private": true,
"scripts": {
@@ -57,4 +57,4 @@
"vue-cli-plugin-element": "^1.0.1",
"vue-template-compiler": "^2.6.14"
}
-}
+}
\ No newline at end of file
diff --git a/public/index.html b/public/index.html
index 0b295d7..16ced03 100644
--- a/public/index.html
+++ b/public/index.html
@@ -1,39 +1,89 @@
-
-
-
-
-
+
+
+
+
+
音乐解锁
-
-
-
-
-
-
-
+
+
+
+
-
-
-
+
+
+
diff --git a/src/App.vue b/src/App.vue
index 176b755..c52a138 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -5,19 +5,19 @@
@@ -77,7 +77,7 @@ export default {
最近更新
${config.updateInfo}
- 使用提示
+ 使用提示
`,
dangerouslyUseHTMLString: true,
duration: 10000,
diff --git a/src/component/ConfigDialog.vue b/src/component/ConfigDialog.vue
index 8523f02..b0e4155 100644
--- a/src/component/ConfigDialog.vue
+++ b/src/component/ConfigDialog.vue
@@ -43,7 +43,9 @@ form >>> input {
下载该加密文件的 JOOX 应用所记录的设备唯一识别码。
参见:
-
+
获取设备 UUID · unlock-music/joox-crypto Wiki。
diff --git a/src/decrypt/__test__/joox.test.ts b/src/decrypt/__test__/joox.test.ts
index dd0af1d..3960549 100644
--- a/src/decrypt/__test__/joox.test.ts
+++ b/src/decrypt/__test__/joox.test.ts
@@ -22,7 +22,7 @@ describe('decrypt/joox', () => {
album: 'unused',
blob: blob,
artist: 'unused',
- imgUrl: 'https://github.com/unlock-music',
+ imgUrl: 'https://example.unlock-music.dev/',
};
});
diff --git a/src/view/Home.vue b/src/view/Home.vue
index 69a6f5d..579e574 100644
--- a/src/view/Home.vue
+++ b/src/view/Home.vue
@@ -18,7 +18,9 @@
:album="editing_data.album"
:albumartist="editing_data.albumartist"
:genre="editing_data.genre"
- @cancel="showEditDialog = false" @ok="handleEdit">
+ @cancel="showEditDialog = false"
+ @ok="handleEdit"
+ >
@@ -37,14 +39,20 @@
开启后,解锁结果将不会存留于浏览器中,防止内存不足。
- 立即保存
+ 立即保存
-
+
@@ -70,7 +78,7 @@ export default {
return {
showConfigDialog: false,
showEditDialog: false,
- editing_data: { picture: '', title: '', artist: '', album: '', albumartist: '', genre: '', },
+ editing_data: { picture: '', title: '', artist: '', album: '', albumartist: '', genre: '' },
tableData: [],
playing_url: '',
playing_auto: false,
@@ -111,7 +119,7 @@ export default {
errInfo +
',' +
filename +
- ',参考使用提示',
+ ',参考使用提示',
dangerouslyUseHTMLString: true,
duration: 6000,
});
@@ -164,12 +172,13 @@ export default {
console.warn('获取图像失败', this.editing_data.picture);
}
}
- const newMeta = { picture: imageInfo?.buffer,
+ const newMeta = {
+ picture: imageInfo?.buffer,
title: data.title,
artists: data.artist.split(split_regex),
album: data.album,
albumartist: data.albumartist,
- genre: data.genre.split(split_regex)
+ genre: data.genre.split(split_regex),
};
const buffer = Buffer.from(await this.editing_data.blob.arrayBuffer());
const mime = AudioMimeType[this.editing_data.ext] || AudioMimeType.mp3;
From a788f48b6768f35d39cd950ac82cbf5646928fee Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E9=B2=81=E6=A0=91=E4=BA=BA?=
Date: Sat, 15 Jul 2023 20:10:19 +0100
Subject: [PATCH 07/11] docs: update issue templates
---
.gitlab/ISSUE_TEMPLATE/bug-report.md | 31 +++++++++++++++++----------
.gitlab/ISSUE_TEMPLATE/new-feature.md | 20 +++++++++--------
2 files changed, 31 insertions(+), 20 deletions(-)
diff --git a/.gitlab/ISSUE_TEMPLATE/bug-report.md b/.gitlab/ISSUE_TEMPLATE/bug-report.md
index 8181d6a..272c2db 100644
--- a/.gitlab/ISSUE_TEMPLATE/bug-report.md
+++ b/.gitlab/ISSUE_TEMPLATE/bug-report.md
@@ -1,39 +1,48 @@
---
+
name: Bug报告
about: 报告Bug以帮助改进程序
title: ''
-labels: bug
-assignees: ''
+labels:
+- bug
---
* 请按照此模板填写,否则可能立即被关闭
- [x] 我确认已经搜索过Issue不存并确认相同的Issue
-- [x] 我有证据表明这是程序导致的问题(如不确认,可以在[Discussions](https://github.com/ix64/unlock-music/discussions)内提出)
+- [x] 我有证据表明这是程序导致的问题(如不确认,可以通过 Telegram 讨论组 (https://t.me/unlock_music_chat) 进行讨论)
-**Bug描述**
+## Bug描述
简要地复述你遇到的Bug
-**复现方法**
+## 复现方法
描述复现方法,必要时请提供样本文件
-**程序截图或者Console报错信息**
+## 程序截图或浏览器开发者控制台(Console)的报错信息
如果可以请提供二者之一
-
-**环境信息:**
+## 环境信息
- 操作系统和浏览器:
- 程序版本:
- - 获取音乐文件所使用的客户端及其版本信息:
+ - 网页版的地址(如果为非官方部署请注明):
+## 若为文件无法解密,请填写文件获取途径
-**附加信息**
+
-其他能够帮助确认问题的信息
+- 音乐平台名称:芝麻音乐 - https://example.com/
+- 客户端平台与版本号:Windows 客户端,版本号 12.34
+- 受影响的资源链接:https://example.com/song/123456
+
+注意:如果需要会员才能获取该资源,你可能也需要作为附件提交。
+
+## 附加信息
+
+如果有,请提供其他能够帮助确认问题的信息到下方:
diff --git a/.gitlab/ISSUE_TEMPLATE/new-feature.md b/.gitlab/ISSUE_TEMPLATE/new-feature.md
index 8d05080..9e5fe49 100644
--- a/.gitlab/ISSUE_TEMPLATE/new-feature.md
+++ b/.gitlab/ISSUE_TEMPLATE/new-feature.md
@@ -1,26 +1,28 @@
---
+
name: 新功能
about: 对于程序新的想法或建议
title: ''
-labels: enhancement
-assignees: ''
+labels:
+- enhancement
---
-- 请按照此模板填写,否则可能立即被关闭
+
+
-**背景和说明**
+## 背景和说明
-简要说明产生此想法的背景和此想法的具体内容
+
-**实现途径**
+## 实现途径
- 如果没有设计方案,请简要描述实现思路
-- 如果你没有任何的实现思路,请通过[Discussions](https://github.com/ix64/unlock-music/discussions)或者Telegram进行讨论
+- 如果你没有任何的实现思路,请通过 Telegram 讨论组 (https://t.me/unlock_music_chat) 进行讨论
-**附加信息**
+## 附加信息
-更多你想要表达的内容
+
From 573435efc9f38836ceae1e9782fb91ab4a818735 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E9=B2=81=E6=A0=91=E4=BA=BA?=
Date: Sat, 15 Jul 2023 20:22:51 +0100
Subject: [PATCH 08/11] chore: issue template update
---
.gitlab/ISSUE_TEMPLATE/bug-report.md | 7 ++++---
.gitlab/ISSUE_TEMPLATE/new-feature.md | 7 ++++---
2 files changed, 8 insertions(+), 6 deletions(-)
diff --git a/.gitlab/ISSUE_TEMPLATE/bug-report.md b/.gitlab/ISSUE_TEMPLATE/bug-report.md
index 272c2db..3d16e6f 100644
--- a/.gitlab/ISSUE_TEMPLATE/bug-report.md
+++ b/.gitlab/ISSUE_TEMPLATE/bug-report.md
@@ -1,9 +1,10 @@
---
-name: Bug报告
-about: 报告Bug以帮助改进程序
-title: ''
+name: "Bug 报告"
+about: "报告Bug以帮助改进程序"
+title: "[BUG] "
labels:
+
- bug
---
diff --git a/.gitlab/ISSUE_TEMPLATE/new-feature.md b/.gitlab/ISSUE_TEMPLATE/new-feature.md
index 9e5fe49..c7610d4 100644
--- a/.gitlab/ISSUE_TEMPLATE/new-feature.md
+++ b/.gitlab/ISSUE_TEMPLATE/new-feature.md
@@ -1,9 +1,10 @@
---
-name: 新功能
-about: 对于程序新的想法或建议
-title: ''
+name: "新功能"
+about: "对于程序新的想法或建议"
+title: "[新功能] "
labels:
+
- enhancement
---
From 36774c165eef0b6e1ca8819edaf65a5e3b9e2f14 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E9=B2=81=E6=A0=91=E4=BA=BA?=
Date: Sat, 15 Jul 2023 20:59:49 +0100
Subject: [PATCH 09/11] chore: add more template
---
.gitlab/ISSUE_TEMPLATE/bug-crypto-guided.yaml | 76 +++++++++++++++++++
.gitlab/ISSUE_TEMPLATE/bug-report.md | 13 +---
2 files changed, 78 insertions(+), 11 deletions(-)
create mode 100644 .gitlab/ISSUE_TEMPLATE/bug-crypto-guided.yaml
diff --git a/.gitlab/ISSUE_TEMPLATE/bug-crypto-guided.yaml b/.gitlab/ISSUE_TEMPLATE/bug-crypto-guided.yaml
new file mode 100644
index 0000000..233ba24
--- /dev/null
+++ b/.gitlab/ISSUE_TEMPLATE/bug-crypto-guided.yaml
@@ -0,0 +1,76 @@
+name: 解码错误报告 (填表)
+about: 遇到文件解码失败的问题请选择该项。
+title: '[Bug/Crypto] '
+labels:
+ - bug
+ - crypto
+body:
+ - type: textarea
+ id: what-happened
+ attributes:
+ label: 错误描述
+ description: 请描述你所遇到的问题,以及你期待的行为。
+ placeholder: ''
+ value: ''
+ validations:
+ required: true
+ - type: dropdown
+ id: version
+ attributes:
+ label: Unlock Music 版本
+ description: |
+ 能够重现错误的版本,版本号通常在页面底部。
+ 如果不确定,请升级到最新版确认问题是否解决。
+ multiple: true
+ options:
+ - 1.10.5 (仓库最新)
+ - 1.10.3 (官方 DEMO)
+ - 其它(请在错误描述中指定)
+ validations:
+ required: true
+ - type: dropdown
+ id: browsers
+ attributes:
+ label: 产生错误的浏览器
+ multiple: true
+ options:
+ - 火狐 / Firefox
+ - Chrome
+ - Safari
+ - 其它基于 Chromium 的浏览器 (Edge、Brave、Opera 等)
+ - type: dropdown
+ id: music-platform
+ attributes:
+ label: 音乐平台
+ description: |
+ 如果需要报告多个平台的问题,请每个平台提交一个新的 Issue。
+ 请注意:播放器缓存文件不属于该项目支持的文件类型。
+ multiple: false
+ options:
+ - 其它 (请在错误描述指定)
+ - QQ 音乐
+ - Joox (QQ 音乐海外版)
+ - 虾米音乐
+ - 网易云音乐
+ - 酷我音乐
+ - 酷狗音乐
+ - 喜马拉雅
+ - 咪咕 3D
+ validations:
+ required: true
+ - type: textarea
+ id: logs
+ attributes:
+ label: 日志信息
+ description: 如果有,请提供浏览器开发者控制台(Console)的错误日志:
+ render: text
+ - type: checkboxes
+ id: terms
+ attributes:
+ label: 我已经阅读并确认下述内容
+ description: ''
+ options:
+ - label: 我已经检索过 Issue 列表,并确认这是一个为报告过的问题。
+ required: true
+ - label: 我有证据表明这是程序导致的问题(如不确认,可以通过 Telegram 讨论组 (https://t.me/unlock_music_chat) 进行讨论)
+ required: true
diff --git a/.gitlab/ISSUE_TEMPLATE/bug-report.md b/.gitlab/ISSUE_TEMPLATE/bug-report.md
index 3d16e6f..520e534 100644
--- a/.gitlab/ISSUE_TEMPLATE/bug-report.md
+++ b/.gitlab/ISSUE_TEMPLATE/bug-report.md
@@ -1,7 +1,7 @@
---
-name: "Bug 报告"
-about: "报告Bug以帮助改进程序"
+name: "错误报告"
+about: "报告 Bug 以帮助改进程序,非填表。"
title: "[BUG] "
labels:
@@ -14,7 +14,6 @@ labels:
- [x] 我确认已经搜索过Issue不存并确认相同的Issue
- [x] 我有证据表明这是程序导致的问题(如不确认,可以通过 Telegram 讨论组 (https://t.me/unlock_music_chat) 进行讨论)
-
## Bug描述
简要地复述你遇到的Bug
@@ -33,14 +32,6 @@ labels:
- 程序版本:
- 网页版的地址(如果为非官方部署请注明):
-## 若为文件无法解密,请填写文件获取途径
-
-
-
-- 音乐平台名称:芝麻音乐 - https://example.com/
-- 客户端平台与版本号:Windows 客户端,版本号 12.34
-- 受影响的资源链接:https://example.com/song/123456
-
注意:如果需要会员才能获取该资源,你可能也需要作为附件提交。
## 附加信息
From dfe69c8d1f0d42e0a0000dc55926e2cc1cdec9be Mon Sep 17 00:00:00 2001
From: nullptr-0 <959220793@qq.com>
Date: Tue, 29 Aug 2023 00:29:05 +0800
Subject: [PATCH 10/11] bugfix for #36 #50
---
src/decrypt/kgm_wasm.ts | 18 +++++++++---------
src/decrypt/qmc_wasm.ts | 7 ++++---
2 files changed, 13 insertions(+), 12 deletions(-)
diff --git a/src/decrypt/kgm_wasm.ts b/src/decrypt/kgm_wasm.ts
index 039c3be..1a9b021 100644
--- a/src/decrypt/kgm_wasm.ts
+++ b/src/decrypt/kgm_wasm.ts
@@ -2,7 +2,7 @@ import { KgmCrypto } from '@xhacker/kgmwasm/KgmWasmBundle';
import KgmCryptoModule from '@xhacker/kgmwasm/KgmWasmBundle';
import { MergeUint8Array } from '@/utils/MergeUint8Array';
-// 每次处理 2M 的数据
+// 每次可以处理 2M 的数据
const DECRYPTION_BUF_SIZE = 2 *1024 * 1024;
export interface KGMDecryptionResult {
@@ -36,12 +36,12 @@ export async function DecryptKgmWasm(kgmBlob: ArrayBuffer, ext: string): Promise
// 申请内存块,并文件末端数据到 WASM 的内存堆
let kgmBuf = new Uint8Array(kgmBlob);
- const pQmcBuf = KgmCryptoObj._malloc(DECRYPTION_BUF_SIZE);
- KgmCryptoObj.writeArrayToMemory(kgmBuf.slice(0, DECRYPTION_BUF_SIZE), pQmcBuf);
+ const pKgmBuf = KgmCryptoObj._malloc(DECRYPTION_BUF_SIZE);
+ const preDecDataSize = Math.min(DECRYPTION_BUF_SIZE, kgmBlob.byteLength); // 初始化缓冲区大小
+ KgmCryptoObj.writeArrayToMemory(kgmBuf.slice(0, preDecDataSize), pKgmBuf);
// 进行解密初始化
- const headerSize = KgmCryptoObj.preDec(pQmcBuf, DECRYPTION_BUF_SIZE, ext);
- console.log(headerSize);
+ const headerSize = KgmCryptoObj.preDec(pKgmBuf, preDecDataSize, ext);
kgmBuf = kgmBuf.slice(headerSize);
const decryptedParts = [];
@@ -52,14 +52,14 @@ export async function DecryptKgmWasm(kgmBlob: ArrayBuffer, ext: string): Promise
// 解密一些片段
const blockData = new Uint8Array(kgmBuf.slice(offset, offset + blockSize));
- KgmCryptoObj.writeArrayToMemory(blockData, pQmcBuf);
- KgmCryptoObj.decBlob(pQmcBuf, blockSize, offset);
- decryptedParts.push(KgmCryptoObj.HEAPU8.slice(pQmcBuf, pQmcBuf + blockSize));
+ KgmCryptoObj.writeArrayToMemory(blockData, pKgmBuf);
+ KgmCryptoObj.decBlob(pKgmBuf, blockSize, offset);
+ decryptedParts.push(KgmCryptoObj.HEAPU8.slice(pKgmBuf, pKgmBuf + blockSize));
offset += blockSize;
bytesToDecrypt -= blockSize;
}
- KgmCryptoObj._free(pQmcBuf);
+ KgmCryptoObj._free(pKgmBuf);
result.data = MergeUint8Array(decryptedParts);
result.success = true;
diff --git a/src/decrypt/qmc_wasm.ts b/src/decrypt/qmc_wasm.ts
index f747bf4..0ab2dad 100644
--- a/src/decrypt/qmc_wasm.ts
+++ b/src/decrypt/qmc_wasm.ts
@@ -2,7 +2,7 @@ import { QmcCrypto } from '@xhacker/qmcwasm/QmcWasmBundle';
import QmcCryptoModule from '@xhacker/qmcwasm/QmcWasmBundle';
import { MergeUint8Array } from '@/utils/MergeUint8Array';
-// 每次处理 2M 的数据
+// 每次可以处理 2M 的数据
const DECRYPTION_BUF_SIZE = 2 *1024 * 1024;
export interface QMCDecryptionResult {
@@ -38,11 +38,12 @@ export async function DecryptQmcWasm(qmcBlob: ArrayBuffer, ext: string): Promise
// 申请内存块,并文件末端数据到 WASM 的内存堆
const qmcBuf = new Uint8Array(qmcBlob);
const pQmcBuf = QmcCryptoObj._malloc(DECRYPTION_BUF_SIZE);
- QmcCryptoObj.writeArrayToMemory(qmcBuf.slice(-DECRYPTION_BUF_SIZE), pQmcBuf);
+ const preDecDataSize = Math.min(DECRYPTION_BUF_SIZE, qmcBlob.byteLength); // 初始化缓冲区大小
+ QmcCryptoObj.writeArrayToMemory(qmcBuf.slice(-preDecDataSize), pQmcBuf);
// 进行解密初始化
ext = '.' + ext;
- const tailSize = QmcCryptoObj.preDec(pQmcBuf, DECRYPTION_BUF_SIZE, ext);
+ const tailSize = QmcCryptoObj.preDec(pQmcBuf, preDecDataSize, ext);
if (tailSize == -1) {
result.error = QmcCryptoObj.getErr();
return result;
From c311fb7f5d10df45dcf2fb06dc27ad11dd9c04f1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E9=B2=81=E6=A0=91=E4=BA=BA?=
Date: Mon, 28 Aug 2023 18:27:28 +0100
Subject: [PATCH 11/11] 1.10.6
---
package-lock.json | 4 ++--
package.json | 4 ++--
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index 7457ca0..48d7266 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "unlock-music",
- "version": "1.10.5",
+ "version": "1.10.6",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "unlock-music",
- "version": "1.10.5",
+ "version": "1.10.6",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
diff --git a/package.json b/package.json
index 271a267..7e9d9f6 100644
--- a/package.json
+++ b/package.json
@@ -1,8 +1,8 @@
{
"name": "unlock-music",
- "version": "1.10.5",
+ "version": "1.10.6",
"ext_build": 0,
- "updateInfo": "完善音乐标签编辑功能,支持编辑更多标签",
+ "updateInfo": "修正文件过小的情况下酷狗 / QQ解密错误问题",
"license": "MIT",
"description": "Unlock encrypted music file in browser.",
"repository": {