Skip to content

Instantly share code, notes, and snippets.

@Airtnp
Last active November 12, 2025 02:53
Show Gist options
  • Select an option

  • Save Airtnp/ca3831f5269fefc7e8416b56c0e1d1ac to your computer and use it in GitHub Desktop.

Select an option

Save Airtnp/ca3831f5269fefc7e8416b56c0e1d1ac to your computer and use it in GitHub Desktop.
import hashlib
import mst_card_text_pb2
import json
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad, pad
from protodeep.lib import guess_schema
from Crypto.Protocol.KDF import PBKDF2 # From PyCryptodome
from Crypto.Hash import HMAC, SHA1 # PBKDF2 default hash = HMAC-SHA1
from collections import OrderedDict
from pathlib import Path
from typing import Dict
# Dumped from Memory
KEY = bytes.fromhex("C5 C5 15 6C 00 03 AD EE 8C 17 AE 24 92 94 2D 5A 1A 77 E7 EF 46 8D 77 7D FA B8 88 4A 4E 08 76 E6")
IV = bytes.fromhex("84 E7 6B 8C 7C 40 5C 6E 96 68 CB CB A3 F8 05 08")
# Also in stringliterals.json
PASSWORD = "sbiVmG&$P0qDu1$sqT$4r+T96lSFEpDD"
ITERATION_COUNT = 0x3e8 # 1000
# PrivateImplementationDetails
SALT = bytes.fromhex("00 01 03 02 07 05 06 F1 F0 EE 21 22 46")
def decrypt(data):
return unpad(AES.new(KEY, AES.MODE_CBC, IV).decrypt(data), 16)
def encrypt(data):
return AES.new(KEY, AES.MODE_CBC, IV).encrypt(pad(data, 16))
def generate_key():
KEY_LEN = 0x20
IV_LEN = 0x10
total_size = KEY_LEN + IV_LEN
derived = PBKDF2(PASSWORD.encode('utf-8'), SALT, dkLen=total_size, count=ITERATION_COUNT, hmac_hash_module=SHA1)
key = derived[:KEY_LEN]
iv = derived[KEY_LEN:]
return key, iv
def serialize_card_text_file(src: Path, dst_folder: Path, translation_map: Dict[str, str]):
with open(src,"rb") as f:
ct = f.read()
pt = decrypt(ct)
card_text_data = mst_card_text_pb2.Schema()
card_text_data.ParseFromString(pt)
for e in card_text_data.entries:
if e.text.value in translation_map and translation_map[e.text.value] != "":
e.text.value = translation_map[e.text.value]
# md5('mst_card_text')
with open(dst_folder / '870e4daec54c1d63dee345925f4378fc.dat', 'wb') as f:
f.write(encrypt(card_text_data.SerializeToString()))
def write_translation_template(src: Path, dst: Path, translation_map: Dict[str, str]):
with open(src,"rb") as f:
ct = f.read()
pt = decrypt(ct)
card_text_data = mst_card_text_pb2.Schema()
card_text_data.ParseFromString(pt)
text_lines = OrderedDict()
for e in card_text_data.entries:
if e.text.value in translation_map and translation_map[e.text.value] != "":
text_lines[e.text.value] = translation_map[e.text.value]
else:
text_lines[e.text.value] = ""
# md5('mst_card_text')
with open(dst, 'w', encoding='utf-8') as f:
f.write(json.dumps(text_lines, indent=2, ensure_ascii=False))
if __name__ == '__main__':
with open('./zh/translation.json', 'r', encoding='utf-8') as f:
translation_map = json.loads(f.read())
serialize_card_text_file(Path('./mst_card_text.dat'), Path('./zh/'), translation_map)
# write_translation_template(Path('./mst_card_text.dat'), Path('./translation.json'), {})
import hashlib
import json
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad, pad
from protodeep.lib import guess_schema
from Crypto.Protocol.KDF import PBKDF2 # From PyCryptodome
from Crypto.Hash import HMAC, SHA1 # PBKDF2 default hash = HMAC-SHA1
from tqdm import tqdm
# Dumped from Memory
KEY = bytes.fromhex("C5 C5 15 6C 00 03 AD EE 8C 17 AE 24 92 94 2D 5A 1A 77 E7 EF 46 8D 77 7D FA B8 88 4A 4E 08 76 E6")
IV = bytes.fromhex("84 E7 6B 8C 7C 40 5C 6E 96 68 CB CB A3 F8 05 08")
# Also in stringliterals.json
PASSWORD = "sbiVmG&$P0qDu1$sqT$4r+T96lSFEpDD"
ITERATION_COUNT = 0x3e8 # 1000
# PrivateImplementationDetails
SALT = bytes.fromhex("00 01 03 02 07 05 06 F1 F0 EE 21 22 46")
def decrypt(data):
return unpad(AES.new(KEY, AES.MODE_CBC, IV).decrypt(data), 16)
def encrypt(data):
return AES.new(KEY, AES.MODE_CBC, IV).encrypt(pad(data, 16))
def generate_key():
KEY_LEN = 0x20
IV_LEN = 0x10
total_size = KEY_LEN + IV_LEN
derived = PBKDF2(PASSWORD.encode('utf-8'), SALT, dkLen=total_size, count=ITERATION_COUNT, hmac_hash_module=SHA1)
key = derived[:KEY_LEN]
iv = derived[KEY_LEN:]
return key, iv
if __name__ == '__main__':
# https://master-dmplays-jp.cdn-dena.com/latest_Win.dat
with open("latest_Win.dat","rb") as f:
ct = f.read()
pt = decrypt(ct)
with open("Test.dat", 'wb') as f:
f.write(encrypt(pt))
schema = guess_schema(pt)
md5_mapping = {}
for v in schema.values['2']:
md5_mapping[v['1']['1']] = hashlib.md5(v['1']['1'].encode("utf-8")).hexdigest()
for name, m in tqdm(md5_mapping.items()):
with open(f'C:\\Users\\<your_pc>\\AppData\\Roaming\\AndApp\\GameData\\<>\\<>\\data\\a7843688bc3abfa77b4331970f135680\\{m}.dat', 'rb') as f:
schema = guess_schema(decrypt(f.read()))
schema.export_protofile(f"./Proto/proto/{name}.proto")
syntax = "proto3";
message Schema {
message HashStr {
string value = 1;
}
HashStr hash = 1;
message Data {
message CardTextId {
int64 value = 1;
}
CardTextId id = 1;
message CardTextStr {
string value = 1;
}
CardTextStr text = 2;
message LineFeedDecision {
int64 value = 1;
}
LineFeedDecision line_feed_decision = 3;
}
repeated Data entries = 2;
}
# -*- coding: utf-8 -*-
# Generated by the protocol buffer compiler. DO NOT EDIT!
# NO CHECKED-IN PROTOBUF GENCODE
# source: mst_card_text.proto
# Protobuf Python Version: 6.32.1
"""Generated protocol buffer code."""
from google.protobuf import descriptor as _descriptor
from google.protobuf import descriptor_pool as _descriptor_pool
from google.protobuf import runtime_version as _runtime_version
from google.protobuf import symbol_database as _symbol_database
from google.protobuf.internal import builder as _builder
_runtime_version.ValidateProtobufRuntimeVersion(
_runtime_version.Domain.PUBLIC,
6,
32,
1,
'',
'mst_card_text.proto'
)
# @@protoc_insertion_point(imports)
_sym_db = _symbol_database.Default()
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x13mst_card_text.proto\"\xcf\x02\n\x06Schema\x12\x1d\n\x04hash\x18\x01 \x01(\x0b\x32\x0f.Schema.HashStr\x12\x1d\n\x07\x65ntries\x18\x02 \x03(\x0b\x32\x0c.Schema.Data\x1a\x18\n\x07HashStr\x12\r\n\x05value\x18\x01 \x01(\t\x1a\xec\x01\n\x04\x44\x61ta\x12#\n\x02id\x18\x01 \x01(\x0b\x32\x17.Schema.Data.CardTextId\x12&\n\x04text\x18\x02 \x01(\x0b\x32\x18.Schema.Data.CardTextStr\x12\x39\n\x12line_feed_decision\x18\x03 \x01(\x0b\x32\x1d.Schema.Data.LineFeedDecision\x1a\x1b\n\nCardTextId\x12\r\n\x05value\x18\x01 \x01(\x03\x1a\x1c\n\x0b\x43\x61rdTextStr\x12\r\n\x05value\x18\x01 \x01(\t\x1a!\n\x10LineFeedDecision\x12\r\n\x05value\x18\x01 \x01(\x03\x62\x06proto3')
_globals = globals()
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'mst_card_text_pb2', _globals)
if not _descriptor._USE_C_DESCRIPTORS:
DESCRIPTOR._loaded_options = None
_globals['_SCHEMA']._serialized_start=24
_globals['_SCHEMA']._serialized_end=359
_globals['_SCHEMA_HASHSTR']._serialized_start=96
_globals['_SCHEMA_HASHSTR']._serialized_end=120
_globals['_SCHEMA_DATA']._serialized_start=123
_globals['_SCHEMA_DATA']._serialized_end=359
_globals['_SCHEMA_DATA_CARDTEXTID']._serialized_start=267
_globals['_SCHEMA_DATA_CARDTEXTID']._serialized_end=294
_globals['_SCHEMA_DATA_CARDTEXTSTR']._serialized_start=296
_globals['_SCHEMA_DATA_CARDTEXTSTR']._serialized_end=324
_globals['_SCHEMA_DATA_LINEFEEDDECISION']._serialized_start=326
_globals['_SCHEMA_DATA_LINEFEEDDECISION']._serialized_end=359
# @@protoc_insertion_point(module_scope)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment