feat: add mac import option and help text

This commit is contained in:
鲁树人
2023-06-13 00:26:30 +01:00
parent f2c93c9f85
commit 88cf2f972b
10 changed files with 273 additions and 46 deletions

View File

@@ -1,3 +1,4 @@
import { getFileName } from './pathHelper';
import { SQLDatabase, SQLStatic, loadSQL } from './sqlite';
export interface QMAndroidKeyEntry {
@@ -34,7 +35,7 @@ export class DatabaseKeyExtractor {
const keys = db.exec('select file_path, ekey from `audio_file_ekey_table`')[0].values;
return keys.map(([path, key]) => ({
// strip dir name
name: String(path).replace(/.+\//, ''),
name: getFileName(String(path)),
key: String(key),
}));
} finally {

95
src/util/MMKVParser.ts Normal file
View File

@@ -0,0 +1,95 @@
const textDecoder = new TextDecoder('utf-8', { ignoreBOM: true });
export class MMKVParser {
private offset = 8;
private length: number;
constructor(private view: DataView) {
const payloadLength = view.getUint32(0, true);
this.length = 4 + payloadLength;
}
toString() {
const offset = this.offset.toString(16).padStart(8, '0');
const length = this.length.toString(16).padStart(8, '0');
return `<MMKVParser offset=0x${offset} len=0x${length}>`;
}
get eof() {
return this.offset >= this.length;
}
peek() {
return this.view.getUint8(this.offset);
}
public readByte() {
return this.view.getUint8(this.offset++);
}
public readBigInt() {
let value = 0n;
let shift = 0n;
let b: number;
do {
b = this.readByte();
value |= BigInt(b & 0x7f) << shift;
shift += 7n;
} while ((b & 0x80) !== 0);
return value;
}
public readInt() {
const value = this.readBigInt();
if (value > Number.MAX_SAFE_INTEGER) {
throw new Error('Runtime Error: BigInt too large to cast as number');
}
return Number(value);
}
public readBytes(n: number) {
const offset = this.offset;
const end = offset + n;
this.offset = end;
return new Uint8Array(this.view.buffer.slice(offset, end));
}
public readString() {
// String [
// len: int,
// data: byte[int], # utf-8
// ]
const strByteLen = this.readInt();
const data = this.readBytes(strByteLen);
return textDecoder.decode(data).normalize();
}
public readVariantString() {
// Container [
// len: int,
// data: variant
// ]
const containerLen = this.readInt();
const newOffset = this.offset + containerLen;
const result = this.readString();
if (newOffset !== this.offset) {
throw new Error('readVariantString failed: offset does not match');
}
return result;
}
public static toStringMap(view: DataView): Map<string, string> {
const mmkv = new MMKVParser(view);
const result = new Map<string, string>();
while (!mmkv.eof) {
const key = mmkv.readString();
const value = mmkv.readVariantString();
result.set(key, value);
}
return result;
}
}

View File

@@ -0,0 +1,13 @@
import { getFileName } from '../pathHelper';
test('handle linux path', () => {
expect(getFileName('/path/to/file.bin')).toEqual('file.bin');
});
test('handle win32 path', () => {
expect(getFileName('C:\\system32\\file.bin')).toEqual('file.bin');
});
test('handle file name only as well', () => {
expect(getFileName('file.bin')).toEqual('file.bin');
});

3
src/util/pathHelper.ts Normal file
View File

@@ -0,0 +1,3 @@
export function getFileName(path: string) {
return path.replace(/.*[\\/]/, '');
}