diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..457a03c
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,15 @@
+## 变更记录
+
+### 版本 2.1.0
+
+- 减少了一些重复代码的使用,删除了大量不再使用的代码
+- 优化了判断是否为 QMCv1 文件的逻辑、QMCv2 文件的主密钥探测逻辑
+- `libtakiyasha.qmc.QMCv2` 的 `open()` 和 `save()` 现在可接受多个混淆密钥,通过关键字参数 `garble_keys` 在需要时传入。
+ - **因此,上述方法中原来的关键字参数 `garble_key1` 和 `garble_key2` 已经被干掉了,请及时修改你的工具链。**
+ - 如果提供此参数,需要提供一个产生至少一个混淆密钥(类字节对象)的可迭代对象(例如列表),且混淆密钥的顺序必须正确。
+
+在[这里](https://github.com/nukemiko/libtakiyasha/compare/2.1.0rc2...2.1.0)查看更详细的变更记录。
+
+### 版本 2.0.1 至 2.1.0rc2
+
+在[这里](https://github.com/nukemiko/libtakiyasha/compare/2.0.1...2.1.0rc2)查看更详细的变更记录。
diff --git a/README.md b/README.md
index d80df11..8ea13c3 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,7 @@ LibTakiyasha 是一个 Python 音频加密/解密工具库(当然也可用于
**本项目是以学习和技术研究的初衷创建的,修改、再分发时请遵循 [License](LICENSE)。**
-本项目的设计灵感,以及部分解密方案,来源于同类项目:
+本项目的设计灵感,以及部分解密方案,来源于同类项目:
- [Unlock Music Project - CLI Edition](https://git.unlock-music.dev/um/cli)
- [parakeet-rs/libparakeet](https://github.com/parakeet-rs/libparakeet)
@@ -19,37 +19,39 @@ LibTakiyasha 是一个 Python 音频加密/解密工具库(当然也可用于
---
+## 新变化?
+
+请参阅[变更记录](CHANGELOG.md)。
+
+(如果你是在 PyPI 上浏览本项目,它可能会出现在页面的底部,[点按此处跳转](#变更记录)。)
+
## 特性
- 使用纯 Python 代码编写
- - **兼容 Python 3.8 及后续版本**,兼容多种 Python 解释器实现(见下文 [#性能测试](#性能测试))
+ - **兼容 Python 3.8 及后续版本**,兼容多种 Python 解释器实现
+ - 可在[此处](https://github.com/nukemiko/libtakiyasha/wiki/%E6%80%A7%E8%83%BD%E8%A1%A8%E7%8E%B0)查看具体兼容哪些实现
- 易于阅读,方便 Python 爱好者学习
- (包括依赖库)无任何 C/C++ 扩展模块,跨平台性强
+- 支持四种加密文件:
+ - 网易云音乐加密文件 `.ncm`
+ - QQ 音乐加密文件 QMCv1 `.qmc[0-9]`、`.qmcflac`、`.qmcogg`、`.qmcra` 等
+ - QQ 音乐加密文件 QMCv2 `.mflac[0-9]`、`.mgg[0-9]` 等
+ - 酷狗音乐加密文件 KGM/VPR `.kgm`、`.vpr`
+ - 不支持创建新加密文件
+ - 酷我音乐加密文件 `.kwm`
+ - 更多信息,请参见[此处](https://github.com/nukemiko/libtakiyasha/wiki/%E6%94%AF%E6%8C%81%E7%9A%84%E6%A0%BC%E5%BC%8F%E4%BB%A5%E5%8F%8A%E6%89%80%E9%9C%80%E7%9A%84%E5%8F%82%E6%95%B0)
-### 性能测试
+### 性能表现
-由于 Python 语言自身原因,LibTakiyasha 相较于同类项目,运行速度较慢。因此我们使用不同解释器实现,对常用操作做了一些性能测试:
+参见[此处](https://github.com/nukemiko/libtakiyasha/wiki/%E6%80%A7%E8%83%BD%E8%A1%A8%E7%8E%B0)。
-| 操作 | 测试大小 | Python 3.10.9 (CPython) | Python 3.8.12 (Pyston 2.3.5) | Python 3.9.16 (PyPy 7.3.11) |
-| :------------: | :------: | :---------------------: | :--------------------------: | :-------------------------: |
-| NCM 加密 | 36.8 MiB | 4.159s | 2.159s | 1.366s |
-| NCM 解密 | 36.8 MiB | 4.393s | 2.360s | 1.480s |
-| QMCv1 加密 | 36.8 MiB | 3.841s | 2.116s | 1.594s |
-| QMCv1 解密 | 36.8 MiB | 3.813s | 2.331s | 1.406s |
-| QMCv2 掩码加密 | 36.8 MiB | 4.065s | 2.201s | 1.727s |
-| QMCv2 掩码解密 | 36.8 MiB | 3.990s | 2.200s | 1.848s |
-| QMCv2 RC4 加密 | 36.8 MiB | 12.820s | 5.596s | 2.717s |
-| QMCv2 RC4 解密 | 36.8 MiB | 12.588s | 5.913s | 2.552s |
-| KGM 解密 | 64.4 MiB | 49.014s | 22.053s | 8.376s |
-| VPR 解密 | 87.9 MiB | 70.030s | 32.252s | 11.902s |
-
-仅在你对速度有要求时,可以考虑在调用 LibTakiyasha 时使用 PyPy/Pyston 解释器。
+## 安装
-一般情况下,建议使用官方解释器实现(CPython)。
+可用的最新版本:2.1.0,[GitHub 发布页面](https://github.com/nukemiko/libtakiyasha/releases/tag/2.1.0),[PyPI](https://pypi.org/project/libtakiyasha/2.1.0/)
-## 安装
+### 安装方式
-可用的最新版本:2.1.0rc2,可前往[发布页面](https://github.com/nukemiko/libtakiyasha/releases/tag/2.1.0rc2)或 [PyPI](https://pypi.org/project/libtakiyasha/2.1.0rc2/) 下载。
+- 使用 `pip`,通过 PyPI 安装最新版本:`python -m pip install -U libtakiyasha`
如果你要下载其他版本:
@@ -61,8 +63,14 @@ LibTakiyasha 是一个 Python 音频加密/解密工具库(当然也可用于
LibTakiyasha 依赖以下包,均可从 PyPI 获取:
-- [pyaes](https://pypi.org/project/pyaes/)
-- [mutagen](https://pypi.org/project/mutagen/)
+- [pyaes](https://pypi.org/project/pyaes/) - 用于加解密 NCM 文件内嵌的主密钥和元数据
+- [mutagen](https://pypi.org/project/mutagen/) - 用于以 `mutagen` 可接受的形式导出 NCM 文件内嵌的元数据
+
+## 如何使用?
+
+在[这里](https://github.com/nukemiko/libtakiyasha/wiki/%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8%E5%8F%8A%E7%A4%BA%E4%BE%8B)可以找到使用方法和示例。
+
+同时,在[本项目的 Wiki 主页](https://github.com/nukemiko/libtakiyasha/wiki)可以找到其他一些可能对你有用的东西。
## 常见问题
@@ -73,78 +81,3 @@ LibTakiyasha 依赖以下包,均可从 PyPI 获取:
- LibTakiyasha 是一个加解密库,当然需要为用户提供自定义密钥的权利
- 为了保护本项目不受美国数字千年版权法(DMCA)影响,避免仓库被误杀
- 因此,本仓库所有 1.x 及更早版本的提交和发布版本都已删除。
-
-> 如何使用?
-
-当你 `import libtakiyasha` 时,`libtakiyasha` 下有四个子模块 `ncm`、`qmc`、`kgmvpr`、`kwm` 会被自动导入。这些子模块下各有一个加密文件对象类(`qmc` 除外,有两个),和一个 `probe` 开头的探测函数(`qmc` 除外,有三个),用于确认目标文件是否被该模块支持:
-
-| 模块 | 加密文件对象类 | 探测函数 |
-| :-------------------: | :----------------: | :-----------------------------------------------: |
-| `libtakiyasha.ncm` | `NCM` | `probe_ncm()` |
-| `libtakiyasha.qmc` | `QMCv1` 和 `QMCv2` | `probe_qmc()`、`probe_qmcv1()` 和 `probe_qmcv2()` |
-| `libtakiyasha.kgmvpr` | `KGMorVPR` | `probe_kgmvpr()` |
-| `libtakiyasha.kwm` | `KWM` | `probe_kwm()` |
-
-每个探测函数都会返回一个内含两个元素的元组:
-
-- 第一个元素为文件路径或文件对象,取决于探测函数收到的参数;
-- 在探测到受支持的文件时,第二个元素为文件的信息,否则为 `None`
-
-每一个加密文件对象都可以按照普通文件对象对待(拥有 `read()`、`write()`、`seek()` 等方法),也拥有一个 `save()` 方法,以便将该加密文件对象保存到文件。
-
-以 `libtakiyasha.ncm.NCM` 为例,以下是简单的使用示例:
-
-- 要想打开外部加密文件,或新建空加密文件,使用对应加密文件对象类的构造器方法 `open()` 或 `new()`:
-
- ```pycon
- >>> # 打开外部加密文件
- >>> ncmfile_from_open = libtakiyasha.ncm.NCM.open('/path/to/ncmmfile.ncm', core_key=..., tag_key=...)
- >>> ncmfile_from_open
- , source '/path/to/ncmfile.ncm'>
- >>> # 新建空加密文件对象
- >>> ncmfile_new = libtakiyasha.ncm.NCM.new()
- >>> ncmfile_new
-
- >>>
- ```
-
-- 从加密文件中读取和写入数据:
-
- ```pycon
- >>> # 读取 16 字节
- >>> ncmfile_from_open.read(16)
- b'fLaC\\x00\\x00\\x00"\\x12\\x00\\x12\\x00\\x00\\x07)\\x00'
- >>> # 读取一行数据,直到下一个换行符 \n
- >>> ncmfile_from_open.readline()
- b'\xc4B\xf0\x00\xb6\xe14A\x86nz.\x97\xa8\xe3\xbe\x1d\xb7\xb02?u&\x03\x00\t\x90\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x12\x00\x00\x00\x00\x00\x00\x01V\x00\x00\x00\x00\x00\x00\x01\xd6`\x12\x00\x00\x00\x00\x00\x00\x02\xac\x00\x00\x00\x00\x00\x00\x04\x92B\x12\x00\x00\x00\x00\x00\x00\x04\x02\x00\x00\x00\x00\x00\x00\x07\x0f\xb2\x12\x00\x00\x00\x00\x00\x00\x05X\x00\x00\x00\x00\x00\x00\t\xd4\x8c\x12\x00\x00\x00\x00\x00\x00\x06\xae\x00\x00\x00\x00\x00\x00\x0c\xa3\xb6\x12\x00\x00\x00\x00\x00\x00\x08\x04\x00\x00\x00\x00\x00\x00\x0f|\x90\x12\x00\x00\x00\x00\x00\x00\tZ\x00\x00\x00\x00\x00\x00\x12^T\x12\x00\x00\x00\x00\x00\x00\n'
- >>>
- >>> # 使用 for 循环按照固定大小迭代加密文件对象
- >>> ncmfile_from_open.seek(0, 0)
- 0
- >>> for blk in ncmfile_from_open:
- ... print(len(blk))
- ...
- 8192
- 8192
- 8192
- 8192
- 8192
- 8192
- [...]
- >>> # 向加密文件对象写入数据
- >>> ncmfile_from_open.seek(0, 2)
- 36137109
- >>> ncmfile_from_open.write(b'Now I writing something...')
- 26
- >>>
- ```
-
-- 保存加密文件对象到文件:
-
- ```pycon
- >>> # 如果该 NCM 对象不是从文件打开的,还需要 filething 参数
- >>> ncmfile_from_open.save(core_key=..., tag_key=...)
- >>>
- ```
-
-有关每个加密文件的操作示例,请使用 `help()` 查看对应加密文件对象类的文档。
diff --git a/pyproject.toml b/pyproject.toml
index 082dc21..0246bba 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -37,5 +37,5 @@ where = ["src"]
[tool.setuptools.dynamic]
dependencies = { file = ["requirements.txt"] }
-readme = { file = ["README.md"], content-type = 'text/markdown' }
+readme = { file = ["README.md", "CHANGELOG.md"], content-type = 'text/markdown' }
version = { file = "src/libtakiyasha/VERSION" }
diff --git a/src/libtakiyasha/VERSION b/src/libtakiyasha/VERSION
index 51c0fcb..7ec1d6d 100644
--- a/src/libtakiyasha/VERSION
+++ b/src/libtakiyasha/VERSION
@@ -1 +1 @@
-2.1.0rc2
+2.1.0