diff options
| author | blasty <blasty@local> | 2026-07-23 02:12:24 +0200 |
|---|---|---|
| committer | blasty <blasty@local> | 2026-07-23 02:12:24 +0200 |
| commit | 938235f7484b791a3828da3905c7c4949b8dba30 (patch) | |
| tree | c13920c817bc77a147e4272e49281cec16ab9ec3 /server/patch_server.py | |
| parent | TODO: M4 progress — string auto-detect + streaming done; struct-expand/unif... (diff) | |
| download | ida-tui-938235f7484b791a3828da3905c7c4949b8dba30.tar.gz ida-tui-938235f7484b791a3828da3905c7c4949b8dba30.tar.xz ida-tui-938235f7484b791a3828da3905c7c4949b8dba30.zip | |
listing: expand struct-typed globals into member rows (M4 item 3)
A struct-typed data item rendered as one dense 'MyS <...>' line. Now the heads
walker emits indented member rows after the summary (from get_udt_details):
+off name type, each a head of kind member at ea+offset. ListingView renders
them indented/dimmed; _line_plain matches for cursor/search.
Verified: a struct global expands to +0 a int / +4 b char[4] / +8 c __int16,
member rows carry field addresses. New listing_struct_expand scenario; listing
suite 22/0 (in-process).
Diffstat (limited to 'server/patch_server.py')
| -rw-r--r-- | server/patch_server.py | 45 |
1 files changed, 41 insertions, 4 deletions
diff --git a/server/patch_server.py b/server/patch_server.py index e283cab..ff5c493 100644 --- a/server/patch_server.py +++ b/server/patch_server.py @@ -305,6 +305,39 @@ def _idatui_unknown_row(ea, size): return row +def _idatui_struct_member_rows(ea): + """Indented member rows for a struct-typed data item at ``ea`` (expansion), + or [] if it isn't a struct. Top-level fields only.""" + import ida_nalt + import ida_typeinf + import idaapi + + tif = ida_typeinf.tinfo_t() + if not (ida_nalt.get_tinfo(tif, ea) and tif.is_udt()): + return [] + udt = ida_typeinf.udt_type_data_t() + if not tif.get_udt_details(udt): + return [] + rows = [] + for m in udt: + off = m.begin() // 8 + try: + mtype = m.type._print() or "" + except Exception: + mtype = "" + try: + sz = int(m.type.get_size()) + if sz == idaapi.BADSIZE: + sz = 0 + except Exception: + sz = 0 + name = m.name or "" + text = f"+{off:X} {name}" + (f" {mtype}" if mtype else "") + rows.append({"ea": hex(ea + off), "kind": "member", "size": sz, + "text": text}) + return rows + + @tool @idasync def heads( @@ -379,10 +412,14 @@ def heads( nxt = ida_bytes.get_item_end(e) return nxt if nxt > e else e + 1 - def _row(e): + def _rows_for(e): if _is_unknown(e): - return _idatui_unknown_row(e, _run_end(e) - e) - return _idatui_head_row(e) + return [_idatui_unknown_row(e, _run_end(e) - e)] + row = _idatui_head_row(e) + out = [row] + if row.get("kind") == "data": + out.extend(_idatui_struct_member_rows(e)) # expand struct fields + return out ea = ida_bytes.get_item_head(start) for _ in range(offset): @@ -394,7 +431,7 @@ def heads( if len(rows) >= count: more = True break - rows.append(_row(ea)) + rows.extend(_rows_for(ea)) # a struct head expands into member rows ea = _advance(ea) cursor = {"next": hex(ea)} if more else {"done": True} return {"addr": str(addr), "heads": rows, "cursor": cursor} |
