From c25dffa778f7fcc18c500bd0ab155677e2749265 Mon Sep 17 00:00:00 2001 From: Jixun Wu Date: Sat, 17 Jun 2023 03:18:15 +0100 Subject: [PATCH] feat: parse kwm v2 key from mmkv --- .../settings/panels/PanelKWMv2Key.tsx | 45 ++++++++++++++----- src/util/MMKVParser.ts | 28 +++++++++++- 2 files changed, 59 insertions(+), 14 deletions(-) diff --git a/src/features/settings/panels/PanelKWMv2Key.tsx b/src/features/settings/panels/PanelKWMv2Key.tsx index 80a0cb5..b656066 100644 --- a/src/features/settings/panels/PanelKWMv2Key.tsx +++ b/src/features/settings/panels/PanelKWMv2Key.tsx @@ -17,13 +17,17 @@ import { Text, useToast, } from '@chakra-ui/react'; -import { useDispatch, useSelector } from 'react-redux'; -import { kwm2AddKey, kwm2ClearKeys } from '../settingsSlice'; -import { selectStagingKWMv2Keys } from '../settingsSelector'; import { useState } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; import { MdAdd, MdDeleteForever, MdExpandMore, MdFileUpload } from 'react-icons/md'; -import { KWMv2EKeyItem } from './KWMv2/KWMv2EKeyItem'; + import { ImportSecretModal } from '~/components/ImportSecretModal'; +import { MMKVParser } from '~/util/MMKVParser'; + +import { kwm2AddKey, kwm2ClearKeys, kwm2ImportKeys } from '../settingsSlice'; +import { selectStagingKWMv2Keys } from '../settingsSelector'; +import { KWMv2EKeyItem } from './KWMv2/KWMv2EKeyItem'; +import type { StagingKWMv2Key } from '../keyFormats'; export function PanelKWMv2Key() { const toast = useToast(); @@ -33,12 +37,29 @@ export function PanelKWMv2Key() { const addKey = () => dispatch(kwm2AddKey()); const clearAll = () => dispatch(kwm2ClearKeys()); - const handleSecretImport = () => { - toast({ - title: '尚未实现', - isClosable: true, - status: 'error', - }); + const handleSecretImport = async (file: File) => { + let keys: Omit[] | null = null; + if (/cn\.kuwo\.player\.mmkv\.defaultconfig/i.test(file.name)) { + const fileBuffer = await file.arrayBuffer(); + keys = MMKVParser.parseKuwoEKey(new DataView(fileBuffer)); + } + + if (keys) { + dispatch(kwm2ImportKeys(keys)); + setShowImportModal(false); + toast({ + title: `导入完成,共导入了 ${keys.length} 个密钥。`, + description: '记得按下「保存」来应用。', + isClosable: true, + status: 'success', + }); + } else { + toast({ + title: `不支持的文件:${file.name}`, + isClosable: true, + status: 'error', + }); + } }; return ( @@ -82,12 +103,12 @@ export function PanelKWMv2Key() { setShowImportModal(false)} onImport={handleSecretImport} > - 尚未实现 + 文档缺失 ); diff --git a/src/util/MMKVParser.ts b/src/util/MMKVParser.ts index 530f237..943e07a 100644 --- a/src/util/MMKVParser.ts +++ b/src/util/MMKVParser.ts @@ -1,3 +1,4 @@ +import type { StagingKWMv2Key } from '~/features/settings/keyFormats'; import { formatHex } from './formatHex'; const textDecoder = new TextDecoder('utf-8', { ignoreBOM: true }); @@ -85,6 +86,15 @@ export class MMKVParser { return result; } + public skipContainer() { + // Container [ + // len: int, + // data: variant + // ] + const containerLen = this.readInt(); + this.offset += containerLen; + } + public static toStringMap(view: DataView): Map { const mmkv = new MMKVParser(view); const result = new Map(); @@ -96,7 +106,21 @@ export class MMKVParser { return result; } - public static parseKuwoEKey(_view: DataView): unknown[] { - return []; + public static parseKuwoEKey(view: DataView) { + const mmkv = new MMKVParser(view); + const result: Omit[] = []; + while (!mmkv.eof) { + const key = mmkv.readString(); + const idMatch = key.match(/^sec_ekey#(\d+)-(.+)/); + if (!idMatch) { + mmkv.skipContainer(); + continue; + } + + const [_, rid, quality] = idMatch; + const ekey = mmkv.readVariantString(); + result.push({ rid, quality, ekey }); + } + return result; } }