203 lines
7.1 KiB
Python
203 lines
7.1 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""
|
|
query_index.py — 查询 dump SQLite 索引。
|
|
|
|
用法:
|
|
python query_index.py stats
|
|
python query_index.py type <名称子串> # 按类型名搜索 (LIKE)
|
|
python query_index.py short <短名> # 按可读短名精确/模糊
|
|
python query_index.py base <基类子串> # 按基类搜索
|
|
python query_index.py field <字段名子串> # 全局搜字段名
|
|
python query_index.py ftype <字段类型子串> # 按字段类型搜 (如 List<)
|
|
python query_index.py show <type_id 或 完整类型名> # 展开一个类型的全部字段
|
|
python query_index.py dump <type_id> # 原始格式还原 (offset + 标记)
|
|
|
|
选项:
|
|
--limit N 默认 20
|
|
--db PATH 默认 dump_index.db
|
|
"""
|
|
import argparse
|
|
import os
|
|
import sqlite3
|
|
import sys
|
|
|
|
DB_DEFAULT = r"F:\desktop\CFramework\dump_index.db"
|
|
|
|
|
|
def connect(db):
|
|
if not os.path.exists(db):
|
|
sys.exit(f"[!] 索引不存在: {db}\n 先运行: python build_index.py")
|
|
return sqlite3.connect(db)
|
|
|
|
|
|
def cmd_stats(con):
|
|
for q, label in [
|
|
("SELECT COUNT(*) FROM types", "types"),
|
|
("SELECT COUNT(*) FROM fields", "fields"),
|
|
("SELECT kind, COUNT(*) FROM types GROUP BY kind", "types by kind"),
|
|
]:
|
|
print(f"{label}: {con.execute(q).fetchall()}")
|
|
# 字段最多/最少的前几个类型
|
|
print("\ntop 10 types by field count:")
|
|
rows = con.execute(
|
|
"""SELECT t.id, t.kind, t.name, COUNT(f.id) c FROM types t
|
|
LEFT JOIN fields f ON f.type_id = t.id
|
|
GROUP BY t.id ORDER BY c DESC LIMIT 10"""
|
|
).fetchall()
|
|
for r in rows:
|
|
print(f" [{r[0]:>5}] {r[1]:<9} {r[2][:60]:<60} fields={r[3]}")
|
|
|
|
|
|
def cmd_types(con, needle, limit):
|
|
like = f"%{needle}%"
|
|
rows = con.execute(
|
|
"""SELECT id, kind, name, base_type,
|
|
(SELECT COUNT(*) FROM fields f WHERE f.type_id=t.id) AS fc
|
|
FROM types t WHERE name LIKE ? OR short LIKE ?
|
|
ORDER BY fc DESC LIMIT ?""",
|
|
(like, like, limit),
|
|
).fetchall()
|
|
print(f"{len(rows)} match(es) for type name '{needle}':")
|
|
for r in rows:
|
|
print(f" [{r[0]:>5}] {r[1]:<9} {r[2][:70]:<70} base={r[3] or '-'} fields={r[4]}")
|
|
|
|
|
|
def cmd_base(con, needle, limit):
|
|
rows = con.execute(
|
|
"""SELECT id, kind, name, base_type,
|
|
(SELECT COUNT(*) FROM fields f WHERE f.type_id=t.id) AS fc
|
|
FROM types t WHERE base_type LIKE ? ORDER BY fc DESC LIMIT ?""",
|
|
(f"%{needle}%", limit),
|
|
).fetchall()
|
|
print(f"{len(rows)} type(s) with base like '{needle}':")
|
|
for r in rows:
|
|
print(f" [{r[0]:>5}] {r[1]:<9} {r[2][:70]:<70} base={r[3] or '-'} fields={r[4]}")
|
|
|
|
|
|
def cmd_fields(con, needle, limit):
|
|
rows = con.execute(
|
|
"""SELECT f.id, t.kind, t.name, f.offset_hex, f.name, f.field_type,
|
|
f.is_static, f.is_const
|
|
FROM fields f JOIN types t ON t.id = f.type_id
|
|
WHERE f.name LIKE ? ORDER BY f.name LIMIT ?""",
|
|
(f"%{needle}%", limit),
|
|
).fetchall()
|
|
print(f"{len(rows)} field(s) named like '{needle}':")
|
|
for r in rows:
|
|
flags = ("S" if r[6] else "") + ("C" if r[7] else "")
|
|
print(f" [{r[0]:>6}] {r[1]:<9} {r[2][:48]:<48} [+{r[3]:>4}]{flags:<3} {r[4]} : {r[5]}")
|
|
|
|
|
|
def cmd_ftype(con, needle, limit):
|
|
rows = con.execute(
|
|
"""SELECT f.id, t.kind, t.name, f.offset_hex, f.name, f.field_type
|
|
FROM fields f JOIN types t ON t.id = f.type_id
|
|
WHERE f.field_type LIKE ? ORDER BY f.name LIMIT ?""",
|
|
(f"%{needle}%", limit),
|
|
).fetchall()
|
|
print(f"{len(rows)} field(s) with type like '{needle}':")
|
|
for r in rows:
|
|
print(f" [{r[0]:>6}] {r[1]:<9} {r[2][:48]:<48} [+{r[3]:>4}] {r[4]} : {r[5]}")
|
|
|
|
|
|
def resolve_type(con, arg):
|
|
"""arg 可以是数字 id 或类型名子串; 返回唯一类型或 None"""
|
|
if arg.isdigit():
|
|
row = con.execute(
|
|
"SELECT id, kind, name, base_type, line_no FROM types WHERE id=?",
|
|
(int(arg),),
|
|
).fetchone()
|
|
if row:
|
|
return row
|
|
rows = con.execute(
|
|
"""SELECT id, kind, name, base_type, line_no FROM types
|
|
WHERE name = ? OR short = ? LIMIT 5""",
|
|
(arg, arg),
|
|
).fetchall()
|
|
if len(rows) == 1:
|
|
return rows[0]
|
|
if len(rows) > 1:
|
|
print(f"[!] '{arg}' 有 {len(rows)} 个匹配, 用 id 精确定位:")
|
|
for r in rows:
|
|
print(f" [{r[0]}] {r[1]} {r[2]}")
|
|
return None
|
|
# 子串兜底
|
|
rows = con.execute(
|
|
"SELECT id, kind, name, base_type, line_no FROM types WHERE name LIKE ? LIMIT 10",
|
|
(f"%{arg}%",),
|
|
).fetchall()
|
|
if len(rows) == 1:
|
|
return rows[0]
|
|
if len(rows) > 1:
|
|
print(f"[!] '{arg}' 有 {len(rows)} 个匹配:")
|
|
for r in rows[:10]:
|
|
print(f" [{r[0]}] {r[1]} {r[2]}")
|
|
return None
|
|
|
|
|
|
def cmd_show(con, arg):
|
|
t = resolve_type(con, arg)
|
|
if not t:
|
|
sys.exit(f"[!] 未找到类型: {arg}")
|
|
tid, kind, name, base, line = t
|
|
print(f"== [{tid}] {kind} {name} : {base or '-'} (line {line}) ==")
|
|
rows = con.execute(
|
|
"""SELECT offset_hex, is_static, is_const, name, field_type
|
|
FROM fields WHERE type_id=? ORDER BY offset_dec, id""",
|
|
(tid,),
|
|
).fetchall()
|
|
if not rows:
|
|
print(" (无字段)")
|
|
for off, st, cs, fname, ftype in rows:
|
|
flags = ("[S]" if st else "") + ("[C]" if cs else "")
|
|
print(f" +{off:<5} {flags:<4} {fname} : {ftype}")
|
|
print(f" -- {len(rows)} fields --")
|
|
|
|
|
|
def cmd_dump(con, arg):
|
|
"""按 dump.txt 原始格式还原类型"""
|
|
t = resolve_type(con, arg)
|
|
if not t:
|
|
sys.exit(f"[!] 未找到类型: {arg}")
|
|
tid, kind, name, base, line = t
|
|
print(f"[{kind.title()}] {name} : {base or ''}")
|
|
for off, st, cs, fname, ftype in con.execute(
|
|
"""SELECT offset_hex, is_static, is_const, name, field_type
|
|
FROM fields WHERE type_id=? ORDER BY offset_dec, id""",
|
|
(tid,),
|
|
):
|
|
flags = ("[S]" if st else "") + ("[C]" if cs else "")
|
|
print(f" [{off}]{flags} {fname} : {ftype}")
|
|
|
|
|
|
def main():
|
|
ap = argparse.ArgumentParser(description="dump.txt SQLite 索引查询")
|
|
ap.add_argument("cmd", choices=["stats", "type", "short", "base", "field", "ftype", "show", "dump"])
|
|
ap.add_argument("needle", nargs="?", default="")
|
|
ap.add_argument("--limit", type=int, default=20)
|
|
ap.add_argument("--db", default=DB_DEFAULT)
|
|
args = ap.parse_args()
|
|
|
|
con = connect(args.db)
|
|
if args.cmd == "stats":
|
|
cmd_stats(con)
|
|
elif args.cmd == "type":
|
|
cmd_types(con, args.needle, args.limit)
|
|
elif args.cmd == "short":
|
|
cmd_types(con, args.needle, args.limit) # short 也走 name+short LIKE
|
|
elif args.cmd == "base":
|
|
cmd_base(con, args.needle, args.limit)
|
|
elif args.cmd == "field":
|
|
cmd_fields(con, args.needle, args.limit)
|
|
elif args.cmd == "ftype":
|
|
cmd_ftype(con, args.needle, args.limit)
|
|
elif args.cmd == "show":
|
|
cmd_show(con, args.needle)
|
|
elif args.cmd == "dump":
|
|
cmd_dump(con, args.needle)
|
|
con.close()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|