diff --git a/vipsong.py b/vipsong.py index e74e374..2659135 100644 --- a/vipsong.py +++ b/vipsong.py @@ -9,6 +9,7 @@ from mutagen.flac import FLAC from mutagen.oggvorbis import OggVorbis from mutagen.id3 import ID3, TIT2, TPE1, TALB, APIC from pydub import AudioSegment +from concurrent.futures import ThreadPoolExecutor NCM_MAGIC = b"CTENFDAM" CORE_KEY = b"hzHRAmso5kInbaxW" @@ -262,16 +263,26 @@ class KgmDecoder: pub_len = len(self.pub_key) out = bytearray(len(audio)) block_count = (len(audio) + BLOCK - 1) // BLOCK - for block_idx in range(block_count): - if block_idx >= pub_len: - raise ValueError("Key file mismatch") + if block_count > pub_len: + raise ValueError("Key file mismatch") + # Pre-compute all 17 phase tables; workers only read from cache + for phase in range(17): + self._phase_tables(phase, own_key, self.pub_key) + # Parallel block decryption: each block is independent + def _decrypt_block(block_idx): phase = block_idx % 17 tables = self._phase_tables(phase, own_key, self.pub_key) pub_value = self.pub_key[block_idx] start = block_idx * BLOCK end = min(start + BLOCK, len(audio)) - for col, offset in enumerate(range(start, end)): - out[offset] = tables[col][pub_value][audio[offset]] + blk = bytearray(end - start) + for col in range(end - start): + blk[col] = tables[col][pub_value][audio[start + col]] + return start, bytes(blk) + max_workers = min(16, max(1, block_count)) + with ThreadPoolExecutor(max_workers=max_workers) as executor: + for start, blk in executor.map(_decrypt_block, range(block_count)): + out[start:start + len(blk)] = blk payload = bytes(out) container = _sniff_container(payload[:64]) return payload, container, {}, None