# -*- 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 # 展开一个类型的全部字段 python query_index.py dump # 原始格式还原 (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()