Skip to content

Instantly share code, notes, and snippets.

@uyjulian
Created October 20, 2025 14:48
Show Gist options
  • Select an option

  • Save uyjulian/ba9009843223e7e16ae9e5e828dc4ecc to your computer and use it in GitHub Desktop.

Select an option

Save uyjulian/ba9009843223e7e16ae9e5e828dc4ecc to your computer and use it in GitHub Desktop.
#!/usr/bin/env python3
# SPDX-License-Identifier: MIT
# Generates information regarding feature support for Dragon Mechacon firmware.
scmd_support_ranges_start = [0x00, 0x40, 0xC0, 0x80]
# General S-Command support ranges
scmd_support_ranges = {
"5.00" : [0x27, 0x04, 0x00, 0x20],
"5.02" : [0x28, 0x04, 0x00, 0x20],
"5.04" : [0x29, 0x04, 0x00, 0x20],
"5.06" : [0x29, 0x04, 0x00, 0x20],
"5.06.mexico" : [0x37, 0x04, 0x00, 0x20],
"5.10" : [0x36, 0x04, 0x00, 0x20],
"5.12" : [0x29, 0x04, 0x00, 0x20],
"5.14" : [0x3E, 0x04, 0x05, 0x20],
"6.00" : [0x39, 0x04, 0x00, 0x20],
"6.02" : [0x3C, 0x04, 0x00, 0x20],
"6.04" : [0x3C, 0x04, 0x00, 0x20],
"6.06" : [0x3F, 0x04, 0x00, 0x20],
"6.10" : [0x3F, 0x04, 0x00, 0x20],
"6.12" : [0x3F, 0x04, 0x00, 0x20],
}
# S-Command table address
# 4 bytes command handler (lowest bit set -> thumb)
scmd_table_addr = {
"5.00" : 0x33AC0,
"5.02" : 0x34014,
"5.04" : 0x34210,
"5.06" : 0x34218,
"5.06.mexico" : 0x3422C,
"5.10" : 0x3AB0C,
"5.12" : 0x35E08,
"5.14" : 0x3DC90,
"6.00" : 0x35CC0,
"6.02" : 0x36204,
"6.04" : 0x36D04,
"6.06" : 0x37090,
"6.10" : 0x37104,
"6.12" : 0x37F28,
}
# Sub S-Command 03 table address and length
# 4 bytes command handler (lowest bit set -> thumb), 1 byte command number, 1 byte ???, 2 bytes padding
scmd_03_table_addr_len = {
"5.00" : [0x33BEC, 0x38],
"5.02" : [0x34144, 0x38],
"5.04" : [0x34344, 0x39],
"5.06" : [0x3434C, 0x39],
"5.06.mexico" : [0x34398, 0x39],
"5.10" : [0x3AC74, 0x3D],
"5.12" : [0x35F3C, 0x39],
"5.14" : [0x3DE2C, 0x3D],
"6.00" : [0x35E34, 0x3A],
"6.02" : [0x36384, 0x3A],
"6.04" : [0x36E84, 0x3A],
"6.06" : [0x3721C, 0x3A],
"6.10" : [0x37290, 0x3A],
"6.12" : [0x380B4, 0x3A],
}
# TESTMODE command table address and length
# 1 byte command number, 1 byte ???, 2 bytes padding, 4 bytes command handler (lowest bit set -> thumb)
testmode_cmd_addr_len = {
"5.00" : [0x33610, 0x7E],
"5.02" : [0x33B64, 0x7E],
"5.04" : [0x33D50, 0x80],
"5.06" : [0x33D58, 0x80],
"5.06.mexico" : [0x33D6C, 0x80],
"5.10" : [0x3A5C8, 0x8E],
"5.12" : [0x35940, 0x81],
"5.14" : [0x3D73C, 0x90],
"6.00" : [0x35734, 0x86],
"6.02" : [0x35C18, 0x86],
"6.04" : [0x366E4, 0x87],
"6.06" : [0x36A70, 0x87],
"6.10" : [0x36AE4, 0x87],
"6.12" : [0x378E4, 0x87],
}
mecha_bins = {}
for k in sorted(scmd_support_ranges.keys()):
with open(k + ".bin", "rb") as f:
dat = f.read()
if len(dat) != 0x44000:
raise Exception("invalid binary size")
mecha_bins[k] = dat
scmd_support_info = {}
scmd_03_support_info = {}
testmode_cmd_support_info = {}
for k in sorted(mecha_bins.keys()):
v = mecha_bins[k]
scmd_table_addr_cur = scmd_table_addr[k]
scmd_support_ranges_cur = scmd_support_ranges[k]
scmd_handler_addrs = {}
for i1 in range(len(scmd_support_ranges_start)):
st = scmd_support_ranges_start[i1]
for i2 in range(scmd_support_ranges_cur[i1]):
scmd_handler_addrs[st + i2] = int.from_bytes(v[scmd_table_addr_cur:scmd_table_addr_cur + 4], byteorder="little")
scmd_table_addr_cur += 4
scmd_support = {}
for k1 in sorted(scmd_handler_addrs.keys()):
scmd_support[k1] = "/" if scmd_handler_addrs[k1] == scmd_handler_addrs[0] else "+"
scmd_support_info[k] = scmd_support
scmd_03_table_addr_len_cur = scmd_03_table_addr_len[k]
scmd_03_table_addr_cur = scmd_03_table_addr_len_cur[0]
scmd_03_table_len_cur = scmd_03_table_addr_len_cur[1]
scmd_03_handler_addrs = {}
for i in range(scmd_03_table_len_cur):
xscmd_03_table_addr_cur = scmd_03_table_addr_cur + (i * 8)
scmd_03_handler_addrs[int.from_bytes(v[xscmd_03_table_addr_cur + 4:xscmd_03_table_addr_cur + 5], byteorder="little")] = int.from_bytes(v[xscmd_03_table_addr_cur:xscmd_03_table_addr_cur + 4], byteorder="little")
if int.from_bytes(v[xscmd_03_table_addr_cur + 6:xscmd_03_table_addr_cur + 8], byteorder="little") != 0x0000:
raise Exception("malformed scmd 03 table")
scmd_03_support = {}
for k1 in sorted(scmd_03_handler_addrs.keys()):
scmd_03_support[k1] = "+"
scmd_03_support_info[k] = scmd_03_support
testmode_cmd_addr_len_cur = testmode_cmd_addr_len[k]
testmode_cmd_addr_cur = testmode_cmd_addr_len_cur[0]
testmode_cmd_len_cur = testmode_cmd_addr_len_cur[1]
testmode_cmd_handler_addrs = {}
for i in range(testmode_cmd_len_cur):
xtestmode_cmd_addr_cur = testmode_cmd_addr_cur + (i * 8)
testmode_cmd_handler_addrs[int.from_bytes(v[xtestmode_cmd_addr_cur:xtestmode_cmd_addr_cur + 1], byteorder="little")] = int.from_bytes(v[xtestmode_cmd_addr_cur + 4:xtestmode_cmd_addr_cur + 8], byteorder="little")
if int.from_bytes(v[xtestmode_cmd_addr_cur + 2:xtestmode_cmd_addr_cur + 4], byteorder="little") != 0x0000:
raise Exception("malformed testmode table")
testmode_cmd_support = {}
for k1 in sorted(testmode_cmd_handler_addrs.keys()):
testmode_cmd_support[k1] = "+"
testmode_cmd_support_info[k] = testmode_cmd_support
scmd_support_verinfo = {}
scmd_03_support_verinfo = {}
testmode_cmd_verinfo = {}
for k1 in sorted(scmd_support_info.keys()):
v1 = scmd_support_info[k1]
for k2 in sorted(v1.keys()):
xk2 = "0x%02x" % k2
if xk2 not in scmd_support_verinfo:
scmd_support_verinfo[xk2] = {}
scmd_support_verinfo[xk2][k1] = v1[k2]
for k1 in sorted(scmd_03_support_info.keys()):
v1 = scmd_03_support_info[k1]
for k2 in sorted(v1.keys()):
xk2 = "0x%02x" % k2
if xk2 not in scmd_03_support_verinfo:
scmd_03_support_verinfo[xk2] = {}
scmd_03_support_verinfo[xk2][k1] = v1[k2]
for k1 in sorted(testmode_cmd_support_info.keys()):
v1 = testmode_cmd_support_info[k1]
for k2 in sorted(v1.keys()):
xk2 = "0x%02x" % k2
if xk2 not in testmode_cmd_verinfo:
testmode_cmd_verinfo[xk2] = {}
testmode_cmd_verinfo[xk2][k1] = v1[k2]
if False:
import json
dic = {}
dic["scmd_support_verinfo"] = scmd_support_verinfo
dic["scmd_03_support_verinfo"] = scmd_03_support_verinfo
dic["testmode_cmd_verinfo"] = testmode_cmd_verinfo
xdata = json.dumps(dic, indent=4, sort_keys=True)
with open("00out.json", "w") as f:
f.write(xdata)
def write_wikicreole_prologue(arr, header_name="tmp", header_keys=None):
arr.append('{| class="wikitable" style="text-align:center; margin-left:auto; margin-right:auto;"')
arr.append("|+ " + header_name)
arr.append("! V")
for v in header_keys:
arr.append("! " + v)
bg_color = {
"+" : 'style="background:#9EFF9E"',
"-" : 'style="background:#FFC7C7"',
"/" : 'style="background:#FFD"',
}
def write_wikicreole_item(arr, header_keys=None, item_key=None, dic=None):
arr.append("|-")
arr.append("! " + item_key)
elms = []
for v in header_keys:
d = "-"
if v in dic:
d = dic[v]
elms.append(bg_color[d] + " | " + d)
arr.append("| " + (" || ".join(elms)))
def write_wikicreole_epilogue(arr):
arr.append("|}")
wikicreole_data = []
write_wikicreole_prologue(wikicreole_data, header_name="S-Command support", header_keys=sorted(scmd_support_info.keys()))
for k1 in sorted(scmd_support_verinfo.keys()):
write_wikicreole_item(wikicreole_data, header_keys=sorted(scmd_support_info.keys()), item_key=k1, dic=scmd_support_verinfo[k1])
write_wikicreole_epilogue(wikicreole_data)
write_wikicreole_prologue(wikicreole_data, header_name="S-Command 0x03 support", header_keys=sorted(scmd_03_support_info.keys()))
for k1 in sorted(scmd_03_support_verinfo.keys()):
write_wikicreole_item(wikicreole_data, header_keys=sorted(scmd_03_support_info.keys()), item_key=k1, dic=scmd_03_support_verinfo[k1])
write_wikicreole_epilogue(wikicreole_data)
write_wikicreole_prologue(wikicreole_data, header_name="TESTMODE command support", header_keys=sorted(testmode_cmd_support_info.keys()))
for k1 in sorted(testmode_cmd_verinfo.keys()):
write_wikicreole_item(wikicreole_data, header_keys=sorted(testmode_cmd_support_info.keys()), item_key=k1, dic=testmode_cmd_verinfo[k1])
write_wikicreole_epilogue(wikicreole_data)
with open("00out.txt", "w") as f:
f.write("\n".join(wikicreole_data))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment