diff --git a/src/decrypt-worker/crypto/CryptoBase.ts b/src/decrypt-worker/crypto/CryptoBase.ts index 6bf3ae5..38b3352 100644 --- a/src/decrypt-worker/crypto/CryptoBase.ts +++ b/src/decrypt-worker/crypto/CryptoBase.ts @@ -1,3 +1,5 @@ +import type { DecryptCommandOptions } from '~/decrypt-worker/types'; + export interface CryptoBase { cryptoName: string; checkByDecryptHeader: boolean; @@ -8,8 +10,8 @@ export interface CryptoBase { */ overrideExtension?: string; - checkBySignature?: (buffer: ArrayBuffer) => Promise; - decrypt(buffer: ArrayBuffer, blob: Blob): Promise; + checkBySignature?: (buffer: ArrayBuffer, options: DecryptCommandOptions) => Promise; + decrypt(buffer: ArrayBuffer, options: DecryptCommandOptions): Promise; } export type CryptoFactory = () => CryptoBase; diff --git a/src/decrypt-worker/types.ts b/src/decrypt-worker/types.ts new file mode 100644 index 0000000..79ea815 --- /dev/null +++ b/src/decrypt-worker/types.ts @@ -0,0 +1,9 @@ +export interface DecryptCommandOptions { + qmc2Key?: string; +} + +export interface DecryptCommandPayload { + id: string; + blobURI: string; + options: DecryptCommandOptions; +} diff --git a/src/decrypt-worker/worker/handler/decrypt.ts b/src/decrypt-worker/worker/handler/decrypt.ts index 1fc6cf4..1da803c 100644 --- a/src/decrypt-worker/worker/handler/decrypt.ts +++ b/src/decrypt-worker/worker/handler/decrypt.ts @@ -1,5 +1,6 @@ import { Parakeet, fetchParakeet } from '@jixun/libparakeet'; import { timedLogger, withGroupedLogs as withTimeGroupedLogs } from '~/util/logUtils'; +import type { DecryptCommandOptions, DecryptCommandPayload } from '~/decrypt-worker/types'; import { allCryptoFactories } from '../../crypto/CryptoFactory'; import { toArrayBuffer, toBlob } from '~/decrypt-worker/util/buffer'; import { CryptoBase, CryptoFactory } from '~/decrypt-worker/crypto/CryptoBase'; @@ -11,8 +12,13 @@ const TEST_FILE_HEADER_LEN = 4 * 1024 * 1024; class DecryptCommandHandler { private label: string; - constructor(label: string, private parakeet: Parakeet, private blob: Blob, private buffer: ArrayBuffer) { - this.label = `DecryptCommandHandler( ${label} )`; + constructor( + label: string, + private parakeet: Parakeet, + private buffer: ArrayBuffer, + private options: DecryptCommandOptions + ) { + this.label = `DecryptCommandHandler(${label})`; } log(label: string, fn: () => R): R { @@ -42,7 +48,7 @@ class DecryptCommandHandler { } async decryptFile(crypto: CryptoBase) { - if (crypto.checkBySignature && !(await crypto.checkBySignature(this.buffer))) { + if (crypto.checkBySignature && !(await crypto.checkBySignature(this.buffer, this.options))) { return null; } @@ -50,7 +56,7 @@ class DecryptCommandHandler { return null; } - const decrypted = await this.log(`decrypt (${crypto.cryptoName})`, () => crypto.decrypt(this.buffer, this.blob)); + const decrypted = await this.log(`decrypt (${crypto.cryptoName})`, () => crypto.decrypt(this.buffer, this.options)); // Check if we had a successful decryption const audioExt = crypto.overrideExtension ?? (await this.detectAudioExtension(decrypted)); @@ -76,23 +82,21 @@ class DecryptCommandHandler { // Check by decrypt max first 8MiB const decryptedBuffer = await this.log(`${crypto.cryptoName}/decrypt-header-test`, async () => - toArrayBuffer( - await crypto.decrypt(this.buffer.slice(0, TEST_FILE_HEADER_LEN), this.blob.slice(0, TEST_FILE_HEADER_LEN)) - ) + toArrayBuffer(await crypto.decrypt(this.buffer.slice(0, TEST_FILE_HEADER_LEN), this.options)) ); return this.parakeet.detectAudioExtension(decryptedBuffer) !== 'bin'; } } -export const workerDecryptHandler = async ({ id, blobURI }: { id: string; blobURI: string }) => { - const label = `decrypt( ${id} )`; +export const workerDecryptHandler = async ({ id, blobURI, options }: DecryptCommandPayload) => { + const label = `decrypt(${id})`; return withTimeGroupedLogs(label, async () => { const parakeet = await timedLogger(`${label}/init`, fetchParakeet); const blob = await timedLogger(`${label}/fetch-src`, async () => fetch(blobURI).then((r) => r.blob())); const buffer = await timedLogger(`${label}/read-src`, async () => blob.arrayBuffer()); - const handler = new DecryptCommandHandler(id, parakeet, blob, buffer); + const handler = new DecryptCommandHandler(id, parakeet, buffer, options); return handler.decrypt(allCryptoFactories); }); }; diff --git a/src/features/file-listing/fileListingSlice.ts b/src/features/file-listing/fileListingSlice.ts index 1a65793..d1e575b 100644 --- a/src/features/file-listing/fileListingSlice.ts +++ b/src/features/file-listing/fileListingSlice.ts @@ -5,6 +5,7 @@ import { decryptionQueue } from '~/decrypt-worker/client'; import type { DecryptionResult } from '~/decrypt-worker/constants'; import { DecryptErrorType } from '~/decrypt-worker/util/DecryptError'; +import { selectDecryptOptionByFile } from '../settings/settingsSelector'; export enum ProcessState { QUEUED = 'QUEUED', @@ -51,7 +52,8 @@ export const processFile = createAsyncThunk< { fileId: string }, { rejectValue: { message: string; stack?: string } } >('fileListing/processFile', async ({ fileId }, thunkAPI) => { - const file = selectFiles(thunkAPI.getState() as RootState)[fileId]; + const state = thunkAPI.getState() as RootState; + const file = selectFiles(state)[fileId]; if (!file) { const { message, stack } = new Error('ERROR: File not found'); return thunkAPI.rejectWithValue({ message, stack }); @@ -61,7 +63,8 @@ export const processFile = createAsyncThunk< thunkAPI.dispatch(setFileAsProcessing({ id: fileId })); }; - return decryptionQueue.add({ id: fileId, blobURI: file.raw }, onPreProcess); + const options = selectDecryptOptionByFile(state, file.fileName); + return decryptionQueue.add({ id: fileId, blobURI: file.raw, options }, onPreProcess); }); export const fileListingSlice = createSlice({ diff --git a/src/features/settings/settingsSelector.ts b/src/features/settings/settingsSelector.ts new file mode 100644 index 0000000..300d09c --- /dev/null +++ b/src/features/settings/settingsSelector.ts @@ -0,0 +1,11 @@ +import type { DecryptCommandOptions } from '~/decrypt-worker/types'; +import type { RootState } from '~/store'; +import { hasOwn } from '~/util/objects'; + +export const selectDecryptOptionByFile = (state: RootState, name: string): DecryptCommandOptions => { + const qmc2Keys = state.settings.qmc2.keys; + + return { + qmc2Key: hasOwn(qmc2Keys, name) ? qmc2Keys[name] : undefined, + }; +}; diff --git a/src/util/DecryptionQueue.ts b/src/util/DecryptionQueue.ts index 8e9346f..6f65dbd 100644 --- a/src/util/DecryptionQueue.ts +++ b/src/util/DecryptionQueue.ts @@ -1,13 +1,15 @@ +import type { DecryptCommandPayload } from '~/decrypt-worker/types'; import { DECRYPTION_WORKER_ACTION_NAME, DecryptionResult } from '~/decrypt-worker/constants'; + import { ConcurrentQueue } from './ConcurrentQueue'; import { WorkerClientBus } from './WorkerEventBus'; -export class DecryptionQueue extends ConcurrentQueue<{ id: string; blobURI: string }, DecryptionResult> { +export class DecryptionQueue extends ConcurrentQueue { constructor(private workerClientBus: WorkerClientBus, maxQueue?: number) { super(maxQueue); } - async handler(item: { id: string; blobURI: string }): Promise { + async handler(item: DecryptCommandPayload): Promise { return this.workerClientBus.request(DECRYPTION_WORKER_ACTION_NAME.DECRYPT, item); } } diff --git a/src/util/objects.ts b/src/util/objects.ts index 77f700b..ee7fe14 100644 --- a/src/util/objects.ts +++ b/src/util/objects.ts @@ -5,3 +5,5 @@ export function* enumObject(obj: Record | null | void): Generator< } } } + +export const { hasOwn } = Object;