Huge Rewrite!
This commit is contained in:
71
Standalone/DecryptHDD.py
Normal file
71
Standalone/DecryptHDD.py
Normal file
@@ -0,0 +1,71 @@
|
||||
# 解密从 HDD 抓包得到的数据
|
||||
# 兼容 PRiSM 和 CN 2024
|
||||
|
||||
# 完全 Standalone,不依赖于其他文件
|
||||
|
||||
import base64
|
||||
import zlib
|
||||
from Crypto.Cipher import AES
|
||||
from Crypto.Util.Padding import unpad, pad
|
||||
|
||||
# 密钥和 IV
|
||||
# CN 2024
|
||||
aesKey2024 = "n7bx6:@Fg_:2;5E89Phy7AyIcpxEQ:R@"
|
||||
aesIV2024 = ";;KjR1C3hgB1ovXa"
|
||||
|
||||
# 国际服 PRiSM
|
||||
aesKeyPrism = "A;mv5YUpHBK3YxTy5KB^[;5]C2AL50Bq"
|
||||
aesIVPrism = "9FM:sd9xA91X14v]"
|
||||
|
||||
class AESPKCS7:
|
||||
# 实现了 maimai 通讯所用的 AES 加密的类
|
||||
def __init__(self, key: str, iv: str):
|
||||
self.key = key.encode('utf-8')
|
||||
self.iv = iv.encode('utf-8')
|
||||
self.mode = AES.MODE_CBC
|
||||
# 加密
|
||||
def encrypt(self, content: bytes) -> bytes:
|
||||
cipher = AES.new(self.key, self.mode, self.iv)
|
||||
content_padded = pad(content, AES.block_size)
|
||||
encrypted_bytes = cipher.encrypt(content_padded)
|
||||
return encrypted_bytes
|
||||
# 解密
|
||||
def decrypt(self, encrypted_content: bytes) -> str:
|
||||
cipher = AES.new(self.key, self.mode, self.iv)
|
||||
decrypted_padded = cipher.decrypt(encrypted_content)
|
||||
decrypted = unpad(decrypted_padded, AES.block_size)
|
||||
return decrypted
|
||||
|
||||
def main_sdga():
|
||||
# 填入你的想解密的数据的 base64 编码
|
||||
base64_encoded_data = "KSGm2qo7qVHz1wrK15PckYC5/kLjKcTtEXOgHeHt1Xn6DPdo3pltoPLADHpe8+Wq"
|
||||
|
||||
aes = AESPKCS7(aesKeyPrism, aesIVPrism)
|
||||
|
||||
# 首先解码 base64
|
||||
decodedData = base64.b64decode(base64_encoded_data)
|
||||
# 然后解密数据,PRiSM 是先压缩再加密(which is 正确做法)
|
||||
decryptedData = aes.decrypt(decodedData)
|
||||
# 解压数据
|
||||
decompressedData = zlib.decompress(decryptedData)
|
||||
|
||||
print(str(decompressedData))
|
||||
|
||||
def main_sdgb():
|
||||
# 填入你的想解密的数据的 base64 编码
|
||||
base64_encoded_data = "eJyrTVvpuGwCR32OdodwtVXZ7/Ofmfhin7k/K61q3XNoad1rAPGwECU="
|
||||
|
||||
aes = AESPKCS7(aesKey2024, aesIV2024)
|
||||
|
||||
# 首先解码 base64
|
||||
decodedData = base64.b64decode(base64_encoded_data)
|
||||
# 然后解压数据,CN 2024 是加密后再压缩(纯傻逼
|
||||
decompressedData = zlib.decompress(decodedData)
|
||||
# 最后解密数据
|
||||
decryptedData = aes.decrypt(decompressedData)
|
||||
|
||||
print(str(decryptedData))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main_sdga()
|
||||
52
Standalone/Script_GenerateMusicDB.py
Normal file
52
Standalone/Script_GenerateMusicDB.py
Normal file
@@ -0,0 +1,52 @@
|
||||
# 感谢伟大的 Diving-Fish 让我被迫直面恐惧写这个逼玩意
|
||||
|
||||
import xml.dom.minidom as minidom
|
||||
from pathlib import Path
|
||||
import json
|
||||
from loguru import logger
|
||||
|
||||
def makeMusicDBJson():
|
||||
'''
|
||||
从 HDD 的文件来生成 music_db.json
|
||||
推荐的是如果要国服用 那就用国际服的文件来生成
|
||||
免得国服每次更新还要重新生成太麻烦
|
||||
'''
|
||||
# 记得改
|
||||
A000_DIR = Path('H:\PRiSM\Package\Sinmai_Data\StreamingAssets\A000')
|
||||
OPTION_DIR = Path('H:\PRiSM\Package\Sinmai_Data\StreamingAssets')
|
||||
|
||||
music_db: dict[str, dict[str, str | int]] = {}
|
||||
DEST_PATH = Path('../Data/musicDB.json')
|
||||
|
||||
music_folders = [f for f in (A000_DIR / 'music').iterdir() if f.is_dir()]
|
||||
for option_dir in OPTION_DIR.iterdir():
|
||||
if (option_dir / 'music').exists():
|
||||
music_folders.extend([f for f in (option_dir / 'music').iterdir() if f.is_dir()])
|
||||
|
||||
for folder in music_folders:
|
||||
xml_path = (folder / 'Music.xml')
|
||||
if xml_path.exists():
|
||||
xml = minidom.parse(xml_path.as_posix())
|
||||
data = xml.getElementsByTagName('MusicData')[0]
|
||||
music_id = data.getElementsByTagName('name')[0].getElementsByTagName('id')[0].firstChild.data
|
||||
music_name = data.getElementsByTagName('name')[0].getElementsByTagName('str')[0].firstChild.data
|
||||
music_version = data.getElementsByTagName('AddVersion')[0].getElementsByTagName('id')[0].firstChild.data
|
||||
music_db[music_id] = {
|
||||
"name": music_name,
|
||||
"version": int(music_version)
|
||||
}
|
||||
logger.debug(f'Found {len(music_db)} music data')
|
||||
serialized = '{\n'
|
||||
sorted_keys = sorted(music_db.keys(), key=lambda x: int(x))
|
||||
for key in sorted_keys:
|
||||
value = music_db[key]
|
||||
serialized += f' "{key}": {json.dumps(value, ensure_ascii=False)},\n'
|
||||
serialized = serialized[:-2] + '\n}'
|
||||
|
||||
with open(DEST_PATH, 'w', encoding='utf-8') as f:
|
||||
f.write(serialized)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
makeMusicDBJson()
|
||||
print('Done.')
|
||||
105
Standalone/UI.py
Normal file
105
Standalone/UI.py
Normal file
@@ -0,0 +1,105 @@
|
||||
import sys
|
||||
import json
|
||||
|
||||
from PyQt6.QtWidgets import (
|
||||
QApplication, QMainWindow, QWidget, QVBoxLayout, QLineEdit, QTextEdit, QPushButton, QLabel, QHBoxLayout
|
||||
)
|
||||
from PyQt6.QtCore import Qt
|
||||
|
||||
# 将当前目录的父目录加入到 sys.path 中
|
||||
from pathlib import Path
|
||||
current_dir = Path(__file__).resolve().parent
|
||||
parent_dir = current_dir.parent
|
||||
sys.path.append(str(parent_dir))
|
||||
|
||||
from API_TitleServer import *
|
||||
|
||||
def sendRequest(requestText:str, apiNameText:str, uid:int) -> str:
|
||||
try:
|
||||
data = json.loads(requestText)
|
||||
data = json.dumps(data)
|
||||
except:
|
||||
return "给出的输入不是有效的 JSON"
|
||||
try:
|
||||
result = apiSDGB(data, apiNameText, uid)
|
||||
except Exception as e:
|
||||
return "请求失败:" + str(e)
|
||||
return result
|
||||
|
||||
|
||||
class ApiTester(QMainWindow):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
# 主窗口设定
|
||||
self.setWindowTitle("舞萌DX 2024 API 测试器")
|
||||
self.resize(640, 400)
|
||||
# 布局
|
||||
mainWidget = QWidget()
|
||||
self.setCentralWidget(mainWidget)
|
||||
MainLayout = QVBoxLayout(mainWidget)
|
||||
|
||||
# 目标 API 输入框布局
|
||||
TargetAPILayout = QHBoxLayout()
|
||||
# API 输入框
|
||||
self.TargetAPIInputBox = QLineEdit()
|
||||
self.TargetAPIInputBox.setPlaceholderText("指定 API")
|
||||
TargetAPILayout.addWidget(self.TargetAPIInputBox)
|
||||
# API 后缀标签
|
||||
TargetAPILabel = QLabel("MaimaiChn")
|
||||
TargetAPILayout.addWidget(TargetAPILabel)
|
||||
# 添加到主布局
|
||||
MainLayout.addLayout(TargetAPILayout)
|
||||
|
||||
# UA额外信息输入框
|
||||
self.AgentExtraInputBox = QLineEdit()
|
||||
self.AgentExtraInputBox.setPlaceholderText("指定附加信息(UID或狗号)")
|
||||
MainLayout.addWidget(self.AgentExtraInputBox)
|
||||
|
||||
# 请求输入框
|
||||
self.RequestInputBox = QTextEdit()
|
||||
self.RequestInputBox.setPlaceholderText("此处填入请求")
|
||||
MainLayout.addWidget(self.RequestInputBox)
|
||||
# 发送按钮
|
||||
SendRequestButton = QPushButton("发送!")
|
||||
SendRequestButton.clicked.connect(self.prepareRequest)
|
||||
MainLayout.addWidget(SendRequestButton)
|
||||
# 响应输出框
|
||||
self.ResponseTextBox = QTextEdit()
|
||||
self.ResponseTextBox.setPlaceholderText("此处显示输出")
|
||||
self.ResponseTextBox.setReadOnly(True)
|
||||
MainLayout.addWidget(self.ResponseTextBox)
|
||||
|
||||
|
||||
|
||||
# 布局设定
|
||||
MainLayout.setContentsMargins(5, 5, 5, 5)
|
||||
MainLayout.setSpacing(5)
|
||||
MainLayout.setAlignment(Qt.AlignmentFlag.AlignTop)
|
||||
|
||||
|
||||
def prepareRequest(self):
|
||||
# 发送请求用
|
||||
try:
|
||||
RequestDataString = self.RequestInputBox.toPlainText()
|
||||
TargetAPIString = self.TargetAPIInputBox.text()
|
||||
AgentExtraString = int(self.AgentExtraInputBox.text())
|
||||
except:
|
||||
self.ResponseTextBox.setPlainText("输入无效")
|
||||
return
|
||||
|
||||
Result = sendRequest(RequestDataString, TargetAPIString, AgentExtraString)
|
||||
|
||||
# 显示出输出
|
||||
self.ResponseTextBox.setPlainText(Result)
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = QApplication(sys.argv)
|
||||
# Set proper style for each OS
|
||||
#if sys.platform == "win32":
|
||||
# app.setStyle("windowsvista")
|
||||
#else:
|
||||
# app.setStyle("Fusion")
|
||||
window = ApiTester()
|
||||
window.show()
|
||||
sys.exit(app.exec())
|
||||
Reference in New Issue
Block a user