lrc-maker
Version:
歌词滚动姬|可能是你所能见到的最好用的歌词制作工具
77 lines • 3.2 kB
JavaScript
;
const ncmcWorker = self;
const CORE_KEY = Uint8Array.of(0x68, 0x7a, 0x48, 0x52, 0x41, 0x6d, 0x73, 0x6f, 0x35, 0x6b, 0x49, 0x6e, 0x62, 0x61, 0x78, 0x57);
const AES_ECB_DECRYPT = async (keyData, data) => {
const AES = {
name: "AES-CBC",
iv: new Uint8Array(16),
};
const cryptoKey = await crypto.subtle.importKey("raw", keyData, "AES-CBC", false, ["encrypt", "decrypt"]);
const bodyLength = data.byteLength - 16;
const tailBlock = data.slice(bodyLength);
const deTailBlock = await crypto.subtle.decrypt(AES, cryptoKey, tailBlock);
const decrypted = new Uint8Array(bodyLength + deTailBlock.byteLength);
decrypted.set(new Uint8Array(deTailBlock), bodyLength);
const padding = new Uint8Array(16);
padding.fill(16);
const cipher = new Uint8Array(32);
for (let curser = 0; curser < bodyLength; curser += 16) {
const clip = data.slice(curser, curser + 16);
const encryptedPadding = await crypto.subtle.encrypt({
name: "AES-CBC",
iv: clip,
}, cryptoKey, padding);
cipher.set(clip, 0);
cipher.set(new Uint8Array(encryptedPadding).slice(0, 16), 16);
const result = await crypto.subtle.decrypt({ name: "AES-CBC", iv: new ArrayBuffer(16) }, cryptoKey, cipher);
decrypted.set(new Uint8Array(result), curser);
}
return decrypted;
};
ncmcWorker.addEventListener("message", async (ev) => {
const file = ev.data;
const filebuffer = new FileReaderSync().readAsArrayBuffer(file);
const dataview = new DataView(filebuffer);
if (dataview.getUint32(0, true) !== 0x4e455443 || dataview.getUint32(4, true) !== 0x4d414446) {
ncmcWorker.postMessage({ type: "error", payload: "not ncm file" });
ncmcWorker.close();
}
let offset = 10;
const keyDate = (await (() => {
const keyLen = dataview.getUint32(offset, true);
offset += 4;
const data = new Uint8Array(filebuffer, offset, keyLen).map((uint8) => uint8 ^ 0x64);
offset += keyLen;
return AES_ECB_DECRYPT(CORE_KEY, data);
})()).slice(17);
const keyBox = (() => {
const box = new Uint8Array(Array(256).keys());
const keyDataLen = keyDate.length;
let j = 0;
for (let i = 0; i < 256; i++) {
j = (box[i] + j + keyDate[i % keyDataLen]) & 0xff;
[box[i], box[j]] = [box[j], box[i]];
}
return box.map((_, i, arr) => {
i = (i + 1) & 0xff;
const si = arr[i];
const sj = arr[(i + si) & 0xff];
return arr[(si + sj) & 0xff];
});
})();
{
offset += dataview.getUint32(offset, true) + 4;
offset += dataview.getUint32(offset + 5, true) + 13;
}
const decryptedData = (() => {
const data = new Uint8Array(filebuffer, offset);
const dataLength = data.length;
for (let cur = 0; cur < dataLength; ++cur) {
data[cur] ^= keyBox[cur & 0xff];
}
return data;
})();
ncmcWorker.postMessage({ type: "success", payload: decryptedData }, [decryptedData.buffer]);
ncmcWorker.close();
});
//# sourceMappingURL=ncmc-worker.js.map