Compare commits
No commits in common. "62d21ad39378096b5ddb67b3339946b173c52308" and "1b116a8db3576c868238d611153faa7f9805cff4" have entirely different histories.
62d21ad393
...
1b116a8db3
@ -24,7 +24,7 @@
|
|||||||
"@emotion/styled": "^11.11.0",
|
"@emotion/styled": "^11.11.0",
|
||||||
"@reduxjs/toolkit": "^2.0.1",
|
"@reduxjs/toolkit": "^2.0.1",
|
||||||
"@um/libparakeet": "0.4.5",
|
"@um/libparakeet": "0.4.5",
|
||||||
"@unlock-music/crypto": "0.0.0-alpha.13",
|
"@unlock-music/crypto": "0.0.0-alpha.12",
|
||||||
"framer-motion": "^10.16.16",
|
"framer-motion": "^10.16.16",
|
||||||
"nanoid": "^5.0.4",
|
"nanoid": "^5.0.4",
|
||||||
"radash": "^11.0.0",
|
"radash": "^11.0.0",
|
||||||
|
@ -42,8 +42,8 @@ importers:
|
|||||||
specifier: 0.4.5
|
specifier: 0.4.5
|
||||||
version: 0.4.5
|
version: 0.4.5
|
||||||
'@unlock-music/crypto':
|
'@unlock-music/crypto':
|
||||||
specifier: 0.0.0-alpha.13
|
specifier: 0.0.0-alpha.12
|
||||||
version: 0.0.0-alpha.13
|
version: 0.0.0-alpha.12
|
||||||
framer-motion:
|
framer-motion:
|
||||||
specifier: ^10.16.16
|
specifier: ^10.16.16
|
||||||
version: 10.16.16(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
version: 10.16.16(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||||
@ -1927,8 +1927,8 @@ packages:
|
|||||||
'@ungap/structured-clone@1.2.0':
|
'@ungap/structured-clone@1.2.0':
|
||||||
resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==}
|
resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==}
|
||||||
|
|
||||||
'@unlock-music/crypto@0.0.0-alpha.13':
|
'@unlock-music/crypto@0.0.0-alpha.12':
|
||||||
resolution: {integrity: sha512-4afez9SjfY5EN17JsifXCc50dGDIImBGXnoxQbBQkB4TguJt2ePWCDS8UbnBhnfeAQUypWBC6qicOQSwVM36hw==, tarball: https://git.unlock-music.dev/api/packages/um/npm/%40unlock-music%2Fcrypto/-/0.0.0-alpha.13/crypto-0.0.0-alpha.13.tgz}
|
resolution: {integrity: sha512-Q24cq653CmD8sj/D1M6wHYtXJIX3YIgnvbPtO+aHnY07J0ZXvkqNh+6a3hBrGGLYzcSWioAw2xxf2rFEQ3q35A==, tarball: https://git.unlock-music.dev/api/packages/um/npm/%40unlock-music%2Fcrypto/-/0.0.0-alpha.12/crypto-0.0.0-alpha.12.tgz}
|
||||||
|
|
||||||
'@vitejs/plugin-react@4.2.1':
|
'@vitejs/plugin-react@4.2.1':
|
||||||
resolution: {integrity: sha512-oojO9IDc4nCUUi8qIR11KoQm0XFFLIwsRBwHRR4d/88IWghn1y6ckz/bJ8GHDCsYEJee8mDzqtJxh15/cisJNQ==}
|
resolution: {integrity: sha512-oojO9IDc4nCUUi8qIR11KoQm0XFFLIwsRBwHRR4d/88IWghn1y6ckz/bJ8GHDCsYEJee8mDzqtJxh15/cisJNQ==}
|
||||||
@ -6118,7 +6118,7 @@ snapshots:
|
|||||||
|
|
||||||
'@ungap/structured-clone@1.2.0': {}
|
'@ungap/structured-clone@1.2.0': {}
|
||||||
|
|
||||||
'@unlock-music/crypto@0.0.0-alpha.13': {}
|
'@unlock-music/crypto@0.0.0-alpha.12': {}
|
||||||
|
|
||||||
'@vitejs/plugin-react@4.2.1(vite@5.0.10(@types/node@20.10.5)(sass@1.69.5)(terser@5.27.0))':
|
'@vitejs/plugin-react@4.2.1(vite@5.0.10(@types/node@20.10.5)(sass@1.69.5)(terser@5.27.0))':
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -4,8 +4,6 @@ import type { DecryptCommandOptions } from '~/decrypt-worker/types.ts';
|
|||||||
import { QQMusicV1Decipher, QQMusicV2Decipher } from '~/decrypt-worker/decipher/QQMusic.ts';
|
import { QQMusicV1Decipher, QQMusicV2Decipher } from '~/decrypt-worker/decipher/QQMusic.ts';
|
||||||
import { KuwoMusicDecipher } from '~/decrypt-worker/decipher/KuwoMusic.ts';
|
import { KuwoMusicDecipher } from '~/decrypt-worker/decipher/KuwoMusic.ts';
|
||||||
import { KugouMusicDecipher } from '~/decrypt-worker/decipher/KugouMusic.ts';
|
import { KugouMusicDecipher } from '~/decrypt-worker/decipher/KugouMusic.ts';
|
||||||
import { XimalayaAndroidDecipher, XimalayaPCDecipher } from '~/decrypt-worker/decipher/Ximalaya.ts';
|
|
||||||
import { XiamiDecipher } from '~/decrypt-worker/decipher/XiamiMusic.ts';
|
|
||||||
|
|
||||||
export enum Status {
|
export enum Status {
|
||||||
OK = 0,
|
OK = 0,
|
||||||
@ -48,11 +46,8 @@ export const allCryptoFactories: DecipherFactory[] = [
|
|||||||
// KWMv1 (*.kwm)
|
// KWMv1 (*.kwm)
|
||||||
KuwoMusicDecipher.make,
|
KuwoMusicDecipher.make,
|
||||||
|
|
||||||
// Ximalaya PC (*.xm)
|
|
||||||
XimalayaPCDecipher.make,
|
|
||||||
|
|
||||||
// Xiami (*.xm)
|
// Xiami (*.xm)
|
||||||
XiamiDecipher.make,
|
// XiamiCrypto.make,
|
||||||
|
|
||||||
/// File with a fixed footer goes second
|
/// File with a fixed footer goes second
|
||||||
|
|
||||||
@ -72,8 +67,8 @@ export const allCryptoFactories: DecipherFactory[] = [
|
|||||||
QQMusicV1Decipher.create,
|
QQMusicV1Decipher.create,
|
||||||
|
|
||||||
// Ximalaya (Android)
|
// Ximalaya (Android)
|
||||||
XimalayaAndroidDecipher.makeX2M,
|
// XimalayaAndroidCrypto.makeX2M,
|
||||||
XimalayaAndroidDecipher.makeX3M,
|
// XimalayaAndroidCrypto.makeX3M,
|
||||||
|
|
||||||
// QingTingFM (Android)
|
// QingTingFM (Android)
|
||||||
// QingTingFM$Device.make,
|
// QingTingFM$Device.make,
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { DecipherInstance, DecipherOK, DecipherResult, Status } from '~/decrypt-worker/Deciphers';
|
import { DecipherInstance, DecipherOK, DecipherResult, Status } from '~/decrypt-worker/Deciphers';
|
||||||
import { KuGou } from '@unlock-music/crypto';
|
import { KuGouDecipher, KuGouHeader } from '@unlock-music/crypto';
|
||||||
import type { DecryptCommandOptions } from '~/decrypt-worker/types.ts';
|
import type { DecryptCommandOptions } from '~/decrypt-worker/types.ts';
|
||||||
import { chunkBuffer } from '~/decrypt-worker/util/buffer.ts';
|
import { chunkBuffer } from '~/decrypt-worker/util/buffer.ts';
|
||||||
|
|
||||||
@ -7,10 +7,12 @@ export class KugouMusicDecipher implements DecipherInstance {
|
|||||||
cipherName = 'Kugou';
|
cipherName = 'Kugou';
|
||||||
|
|
||||||
async decrypt(buffer: Uint8Array, _options: DecryptCommandOptions): Promise<DecipherResult | DecipherOK> {
|
async decrypt(buffer: Uint8Array, _options: DecryptCommandOptions): Promise<DecipherResult | DecipherOK> {
|
||||||
let kgm: KuGou | undefined;
|
let kgm: KuGouDecipher | undefined;
|
||||||
|
let header: KuGouHeader | undefined;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
kgm = KuGou.from_header(buffer.subarray(0, 0x400));
|
header = KuGouHeader.parse(buffer.subarray(0, 0x400));
|
||||||
|
kgm = new KuGouDecipher(header);
|
||||||
|
|
||||||
const audioBuffer = new Uint8Array(buffer.subarray(0x400));
|
const audioBuffer = new Uint8Array(buffer.subarray(0x400));
|
||||||
for (const [block, offset] of chunkBuffer(audioBuffer)) {
|
for (const [block, offset] of chunkBuffer(audioBuffer)) {
|
||||||
@ -24,6 +26,7 @@ export class KugouMusicDecipher implements DecipherInstance {
|
|||||||
};
|
};
|
||||||
} finally {
|
} finally {
|
||||||
kgm?.free();
|
kgm?.free();
|
||||||
|
header?.free();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,28 +0,0 @@
|
|||||||
import { DecipherInstance, DecipherOK, DecipherResult, Status } from '~/decrypt-worker/Deciphers.ts';
|
|
||||||
import { Xiami } from '@unlock-music/crypto';
|
|
||||||
import { chunkBuffer } from '~/decrypt-worker/util/buffer.ts';
|
|
||||||
|
|
||||||
export class XiamiDecipher implements DecipherInstance {
|
|
||||||
cipherName = 'Xiami (XM)';
|
|
||||||
|
|
||||||
async decrypt(buffer: Uint8Array): Promise<DecipherResult | DecipherOK> {
|
|
||||||
const xm = Xiami.from_header(buffer.subarray(0, 0x10));
|
|
||||||
const { copyPlainLength } = xm;
|
|
||||||
const audioBuffer = buffer.slice(0x10);
|
|
||||||
|
|
||||||
for (const [block] of chunkBuffer(audioBuffer.subarray(copyPlainLength))) {
|
|
||||||
xm.decrypt(block);
|
|
||||||
}
|
|
||||||
xm.free();
|
|
||||||
|
|
||||||
return {
|
|
||||||
cipherName: this.cipherName,
|
|
||||||
status: Status.OK,
|
|
||||||
data: audioBuffer,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static make() {
|
|
||||||
return new XiamiDecipher();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,71 +0,0 @@
|
|||||||
import { DecipherInstance, DecipherOK, DecipherResult, Status } from '~/decrypt-worker/Deciphers';
|
|
||||||
import type { DecryptCommandOptions } from '~/decrypt-worker/types.ts';
|
|
||||||
import { decryptX2MHeader, decryptX3MHeader, XmlyPC } from '@unlock-music/crypto';
|
|
||||||
import { isDataLooksLikeAudio } from '~/decrypt-worker/util/audioType.ts';
|
|
||||||
import { UnsupportedSourceFile } from '~/decrypt-worker/util/DecryptError.ts';
|
|
||||||
|
|
||||||
export class XimalayaAndroidDecipher implements DecipherInstance {
|
|
||||||
cipherName: string;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private decipher: (buffer: Uint8Array) => void,
|
|
||||||
private cipherType: string,
|
|
||||||
) {
|
|
||||||
this.cipherName = `Ximalaya (Android, ${cipherType})`;
|
|
||||||
}
|
|
||||||
|
|
||||||
async decrypt(buffer: Uint8Array, _options: DecryptCommandOptions): Promise<DecipherResult | DecipherOK> {
|
|
||||||
// Detect with first 0x400 bytes
|
|
||||||
const slice = buffer.slice(0, 0x400);
|
|
||||||
this.decipher(slice);
|
|
||||||
if (!isDataLooksLikeAudio(slice)) {
|
|
||||||
throw new UnsupportedSourceFile(`Not a Xmly android file (${this.cipherType})`);
|
|
||||||
}
|
|
||||||
const result = new Uint8Array(buffer);
|
|
||||||
result.set(slice, 0);
|
|
||||||
return {
|
|
||||||
cipherName: this.cipherName,
|
|
||||||
status: Status.OK,
|
|
||||||
data: result,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static makeX2M() {
|
|
||||||
return new XimalayaAndroidDecipher(decryptX2MHeader, 'X2M');
|
|
||||||
}
|
|
||||||
|
|
||||||
public static makeX3M() {
|
|
||||||
return new XimalayaAndroidDecipher(decryptX3MHeader, 'X3M');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class XimalayaPCDecipher implements DecipherInstance {
|
|
||||||
cipherName = 'Ximalaya (PC)';
|
|
||||||
|
|
||||||
async decrypt(buffer: Uint8Array, _options: DecryptCommandOptions): Promise<DecipherResult | DecipherOK> {
|
|
||||||
// Detect with first 0x400 bytes
|
|
||||||
const headerSize = XmlyPC.getHeaderSize(buffer.subarray(0, 1024));
|
|
||||||
const xm = new XmlyPC(buffer.subarray(0, headerSize));
|
|
||||||
const { audioHeader, encryptedHeaderOffset, encryptedHeaderSize } = xm;
|
|
||||||
const plainAudioDataOffset = encryptedHeaderOffset + encryptedHeaderSize;
|
|
||||||
const plainAudioDataLength = buffer.byteLength - plainAudioDataOffset;
|
|
||||||
const encryptedAudioPart = buffer.slice(encryptedHeaderOffset, plainAudioDataOffset);
|
|
||||||
const encryptedAudioPartLen = xm.decrypt(encryptedAudioPart);
|
|
||||||
const audioSize = audioHeader.byteLength + encryptedAudioPartLen + plainAudioDataLength;
|
|
||||||
xm.free();
|
|
||||||
|
|
||||||
const result = new Uint8Array(audioSize);
|
|
||||||
result.set(audioHeader);
|
|
||||||
result.set(encryptedAudioPart, audioHeader.byteLength);
|
|
||||||
result.set(buffer.subarray(plainAudioDataOffset), audioHeader.byteLength + encryptedAudioPartLen);
|
|
||||||
return {
|
|
||||||
status: Status.OK,
|
|
||||||
data: result,
|
|
||||||
cipherName: this.cipherName,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static make() {
|
|
||||||
return new XimalayaPCDecipher();
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user