diff options
| author | blasty <blasty@local> | 2026-07-23 20:03:39 +0200 |
|---|---|---|
| committer | blasty <blasty@local> | 2026-07-23 20:03:39 +0200 |
| commit | 4dbb9de9117a85b48f16efbf97477fc58b8283fe (patch) | |
| tree | 255fc47da48ff8c33d15cc98d7ea7da09c582081 /server | |
| parent | unify: the continuous listing is the one code view; deprecate DisasmView (diff) | |
| download | ida-tui-4dbb9de9117a85b48f16efbf97477fc58b8283fe.tar.gz ida-tui-4dbb9de9117a85b48f16efbf97477fc58b8283fe.tar.xz ida-tui-4dbb9de9117a85b48f16efbf97477fc58b8283fe.zip | |
listing: IDA-style function boundary banners in the unified view
Each function gets clear boundary annotations in the continuous listing: a blank
+ '; ==== S U B R O U T I N E ====' separator and a 'name proc near' header
before the entry, and a 'name endp' + rule after the last item. The function
name moves to the proc header (stripped from the inline entry instruction).
Server: heads gains an annotate flag (default off, so DisasmModel/test_domain
stay 1:1 with instructions); when on, _rows_for emits kind=sep/funchdr rows at
function starts/ends. ListingModel requests annotate=true and does NOT index
banner rows by ea, so goto/xref/follow land on real code. ListingView renders
sep (dim) and funchdr (bold gold); new _S_SEP/_S_FUNCHDR styles.
Verified: func_banners 5/0; region_define/listing_view/listing_name_addr/
listing_struct_expand/continuous_view/follow_xrefs/disasm_nav/scroll_restore/
paging green. Needs a supervisor restart (heads tool changed).
Diffstat (limited to 'server')
| -rw-r--r-- | server/patch_server.py | 39 |
1 files changed, 38 insertions, 1 deletions
diff --git a/server/patch_server.py b/server/patch_server.py index ff5c493..99cd584 100644 --- a/server/patch_server.py +++ b/server/patch_server.py @@ -338,6 +338,32 @@ def _idatui_struct_member_rows(ea): return rows +def _idatui_func_header_rows(ea): + """IDA-style subroutine banner rows shown just before a function's entry.""" + import ida_funcs + + name = ida_funcs.get_func_name(ea) or "sub_%X" % ea + bar = "=" * 15 + " S U B R O U T I N E " + "=" * 15 + return [ + {"ea": hex(ea), "kind": "sep", "size": 0, "text": ""}, + {"ea": hex(ea), "kind": "sep", "size": 0, "text": "; " + bar}, + {"ea": hex(ea), "kind": "funchdr", "size": 0, + "text": name + " proc near", "name": name}, + ] + + +def _idatui_func_footer_rows(ea, func): + """End-of-function marker shown just after a function's last item.""" + import ida_funcs + + name = ida_funcs.get_func_name(func.start_ea) or "sub_%X" % func.start_ea + return [ + {"ea": hex(ea), "kind": "funchdr", "size": 0, + "text": name + " endp", "name": name}, + {"ea": hex(ea), "kind": "sep", "size": 0, "text": "; " + "-" * 60}, + ] + + @tool @idasync def heads( @@ -346,6 +372,7 @@ def heads( offset: Annotated[int, "Skip first N heads from addr (default 0)"] = 0, end: Annotated[str, "Optional exclusive end address; default = segment end"] = "", back: Annotated[bool, "Walk backwards: return the count heads ENDING just before addr, in forward order"] = False, + annotate: Annotated[bool, "Emit IDA-style function boundary banner rows (kind sep/funchdr)"] = False, ) -> dict: """Walk item heads from ``addr`` as a flat listing: every head is rendered (code OR data OR undefined) via generate_disasm_line and stepped with @@ -415,10 +442,20 @@ def heads( def _rows_for(e): if _is_unknown(e): return [_idatui_unknown_row(e, _run_end(e) - e)] + func = idaapi.get_func(e) if annotate else None + at_start = func is not None and func.start_ea == e + out = [] + if at_start: + out.extend(_idatui_func_header_rows(e)) row = _idatui_head_row(e) - out = [row] + if at_start: + row = dict(row) + row["name"] = None # the name is shown on the proc header line + out.append(row) if row.get("kind") == "data": out.extend(_idatui_struct_member_rows(e)) # expand struct fields + if func is not None and ida_bytes.get_item_end(e) >= func.end_ea: + out.extend(_idatui_func_footer_rows(e, func)) return out ea = ida_bytes.get_item_head(start) |
