Add mg3d & Fix Bugs
parent
a5de3829db
commit
fee35d4a95
@ -0,0 +1,71 @@
|
||||
import { Decrypt as RawDecrypt } from './raw';
|
||||
import { GetArrayBuffer } from '@/decrypt/utils';
|
||||
import { DecryptResult } from '@/decrypt/entity';
|
||||
|
||||
const segmentSize = 0x20;
|
||||
|
||||
function isPrintableAsciiChar(ch: number) {
|
||||
return ch >= 0x20 && ch <= 0x7E;
|
||||
}
|
||||
|
||||
function isUpperHexChar(ch: number) {
|
||||
return (ch >= 0x30 && ch <= 0x39) || (ch >= 0x41 && ch <= 0x46);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Buffer} data
|
||||
* @param {Buffer} key
|
||||
* @param {boolean} copy
|
||||
* @returns Buffer
|
||||
*/
|
||||
function decryptSegment(data: Uint8Array, key: Uint8Array) {
|
||||
for (let i = 0; i < data.byteLength; i++) {
|
||||
data[i] -= key[i % segmentSize];
|
||||
}
|
||||
return Buffer.from(data);
|
||||
}
|
||||
|
||||
export async function Decrypt(file: File, raw_filename: string): Promise<DecryptResult> {
|
||||
const buf = new Uint8Array(await GetArrayBuffer(file));
|
||||
|
||||
// 咪咕编码的 WAV 文件有很多“空洞”内容,尝试密钥。
|
||||
const header = buf.slice(0, 0x100);
|
||||
const bytesRIFF = Buffer.from('RIFF', 'ascii');
|
||||
const bytesWaveFormat = Buffer.from('WAVEfmt ', 'ascii');
|
||||
const possibleKeys = [];
|
||||
|
||||
for (let i = segmentSize; i < segmentSize * 20; i += segmentSize) {
|
||||
const possibleKey = buf.slice(i, i + segmentSize);
|
||||
if (!possibleKey.every(isUpperHexChar)) continue;
|
||||
|
||||
const tempHeader = decryptSegment(header, possibleKey);
|
||||
if (tempHeader.slice(0, 4).compare(bytesRIFF)) continue;
|
||||
if (tempHeader.slice(8, 16).compare(bytesWaveFormat)) continue;
|
||||
|
||||
// fmt chunk 大小可以是 16 / 18 / 40。
|
||||
const fmtChunkSize = tempHeader.readUInt32LE(0x10);
|
||||
if (![16, 18, 40].includes(fmtChunkSize)) continue;
|
||||
|
||||
// 下一个 chunk
|
||||
const firstDataChunkOffset = 0x14 + fmtChunkSize;
|
||||
const chunkName = tempHeader.slice(firstDataChunkOffset, firstDataChunkOffset + 4);
|
||||
if (!chunkName.every(isPrintableAsciiChar)) continue;
|
||||
|
||||
const secondDataChunkOffset = firstDataChunkOffset + 8 + tempHeader.readUInt32LE(firstDataChunkOffset + 4);
|
||||
if (secondDataChunkOffset <= header.byteLength) {
|
||||
const secondChunkName = tempHeader.slice(secondDataChunkOffset, secondDataChunkOffset + 4);
|
||||
if (!secondChunkName.every(isPrintableAsciiChar)) continue;
|
||||
}
|
||||
|
||||
possibleKeys.push(Buffer.from(possibleKey).toString('ascii'));
|
||||
}
|
||||
|
||||
if (possibleKeys.length <= 0) {
|
||||
throw new Error(`ERROR: no suitable key discovered`);
|
||||
}
|
||||
|
||||
const decryptionKey = Buffer.from(possibleKeys[0], 'ascii');
|
||||
decryptSegment(buf, decryptionKey);
|
||||
const musicData = new Blob([buf], { type: 'audio/x-wav' });
|
||||
return await RawDecrypt(musicData, raw_filename, 'wav', false);
|
||||
}
|
Loading…
Reference in New Issue