-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathview_hex.py
More file actions
208 lines (184 loc) · 9.29 KB
/
Copy pathview_hex.py
File metadata and controls
208 lines (184 loc) · 9.29 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
import cv2
import numpy as np
from hex_render import (render as hex_render, render_status, STAT_H, HDR_H,
ROW_H, BPR, MIN_W, col_from_x, X_ASCII, CW)
from hex_search import (find_all, extract_strings, parse_search_input, build_search_set)
from key_codes import (is_escape, is_enter, is_backspace, is_printable,
is_arrow_up, is_arrow_down, is_page_up, is_page_down,
is_home, is_end)
from lsb_filter import apply_filter
_F = cv2.FONT_HERSHEY_PLAIN
FBAR_H = 38
_st = {}
def _clamp(v, lo, hi): return max(lo, min(hi, v))
def _fstate():
return {"mode":"1BIT","R":True,"G":True,"B":True,"enabled":True}
def _hstate():
return {"scroll_row":0,"highlight_offset":-1,
"search_mode":False,"search_text":"","search_results":[],
"search_result_idx":-1,"search_result_set":set(),
"string_mode":False,"string_list":[],"string_idx":0,
"string_input":"4","status_msg":"Ready","str_byte_set":set()}
def _apply(raw, fst):
if not fst["enabled"]: return raw
return apply_filter(raw, fst["mode"], {"R":fst["R"],"G":fst["G"],"B":fst["B"]})
# ── filter bar ─────────────────────────────────────────────────────────────────
def _fbtn_defs():
y,h=5,26
def b(l,x,w,tag): return {"label":l,"x":x,"y":y,"w":w,"h":h,"tag":tag}
return [b("Filter",8,70,"TOGGLE"),b("1-BIT",86,62,"1BIT"),
b("2-BIT",156,62,"2BIT"),b("3-BIT",226,62,"3BIT"),
b("R",304,32,"R"),b("G",344,32,"G"),b("B",384,32,"B")]
def _draw_fbar(canvas, fst, ww):
cv2.rectangle(canvas,(0,0),(ww,FBAR_H),(14,14,22),-1)
cv2.line(canvas,(0,FBAR_H-1),(ww,FBAR_H-1),(40,40,56),1)
for btn in _fbtn_defs():
tag=btn["tag"]; lbl=btn["label"]
bx=btn["x"]; by=btn["y"]; bw=btn["w"]; bh=btn["h"]
tc=(160,160,175)
if tag=="TOGGLE":
color=(0,110,18) if fst["enabled"] else (70,20,20)
tc=(180,255,180) if fst["enabled"] else (180,70,70)
lbl="Filter ON" if fst["enabled"] else "Filter OFF"
elif tag in ("1BIT","2BIT","3BIT"):
color=(0,100,16) if fst["mode"]==tag else (36,36,48)
if fst["mode"]==tag: tc=(210,255,210)
elif tag in ("R","G","B"):
on={"R":(18,18,140),"G":(18,140,18),"B":(140,18,18)}
color=on[tag] if fst[tag] else (32,32,40)
tc=(255,255,255) if fst[tag] else (55,55,65)
else: color=(36,36,48)
cv2.rectangle(canvas,(bx,by),(bx+bw,by+bh),color,-1)
cv2.rectangle(canvas,(bx,by),(bx+bw,by+bh),(80,80,100),1)
ts=cv2.getTextSize(lbl,_F,0.85,1)[0]
cv2.putText(canvas,lbl,(bx+(bw-ts[0])//2,by+(bh+ts[1])//2),_F,0.85,tc,1)
info=f"{len(_st.get('data',b''))} bytes"
cv2.putText(canvas,info,(ww-120,FBAR_H-8),_F,0.85,(55,55,75),1)
def _hit_fbar(x, y):
if y >= FBAR_H: return None
for btn in _fbtn_defs():
bx=btn["x"]; by=btn["y"]; bw=btn["w"]; bh=btn["h"]
if bx<=x<=bx+bw and by<=y<=by+bh: return btn["tag"]
return None
# ── hex state helpers ──────────────────────────────────────────────────────────
def _nav_to(hs, off, total_rows, vis):
hs["scroll_row"]=_clamp(off//BPR - vis//2, 0, max(0,total_rows-vis))
hs["highlight_offset"]=off
def _commit_search(hs, data):
pat=parse_search_input(hs["search_text"])
if not pat: hs["status_msg"]="Empty pattern"; return
res=find_all(data,pat)
hs["search_results"]=res
hs["search_result_set"]=build_search_set(res,len(pat))
hs["status_msg"]=f"{len(res)} match(es) for: {hs['search_text']}"
hs["search_result_idx"]=0 if res else -1
def _step_search(hs, d, vis, total_rows):
res=hs["search_results"]
if not res: return
idx=(hs["search_result_idx"]+d)%len(res)
hs["search_result_idx"]=idx
_nav_to(hs,res[idx],total_rows,vis)
def _commit_strings(hs, data):
try: ml=max(1,int(hs["string_input"])) if hs["string_input"] else 4
except: ml=4
sl=extract_strings(data,ml); hs["string_list"]=sl; hs["string_idx"]=0
if sl:
bset=set()
for off,s in sl:
for i in range(len(s)): bset.add(off+i)
hs["str_byte_set"]=bset; hs["status_msg"]=f"{len(sl)} string(s) >= {ml} bytes"
else:
hs["str_byte_set"]=set(); hs["status_msg"]="No strings found"
def _str_nav(hs, d, vis, total_rows):
sl=hs["string_list"]
if not sl: return
idx=_clamp(hs["string_idx"]+d,0,len(sl)-1)
hs["string_idx"]=idx; _nav_to(hs,sl[idx][0],total_rows,vis)
hs["highlight_offset"]=sl[idx][0]
def _handle_key(k, hs, data, vis, total_rows):
sm=hs["search_mode"]; stm=hs["string_mode"]; has_str=bool(hs["string_list"])
if stm and not has_str:
if is_escape(k): hs["string_mode"]=False; hs["string_input"]="4"; hs["status_msg"]="Ready"
elif is_enter(k): _commit_strings(hs,data)
elif is_backspace(k): hs["string_input"]=hs["string_input"][:-1]
elif 48<=k<=57: hs["string_input"]+=chr(k)
return
if stm and has_str:
if is_escape(k) or k in (ord('q'),ord('Q')):
hs["string_mode"]=False; hs["string_list"]=[]; hs["str_byte_set"]=set(); hs["status_msg"]="Ready"
elif k in (ord('n'),ord('N')): _str_nav(hs,1,vis,total_rows)
elif k in (ord('p'),ord('P')): _str_nav(hs,-1,vis,total_rows)
return
if sm:
if is_escape(k): hs["search_mode"]=False; hs["search_text"]=""
elif is_enter(k): _commit_search(hs,data); hs["search_mode"]=False
elif is_backspace(k): hs["search_text"]=hs["search_text"][:-1]
elif is_printable(k): hs["search_text"]+=chr(k)
return
sr=hs["scroll_row"]
if is_arrow_down(k): hs["scroll_row"]=_clamp(sr+1,0,max(0,total_rows-vis))
elif is_arrow_up(k): hs["scroll_row"]=_clamp(sr-1,0,max(0,total_rows-vis))
elif is_page_down(k): hs["scroll_row"]=_clamp(sr+vis,0,max(0,total_rows-vis))
elif is_page_up(k): hs["scroll_row"]=_clamp(sr-vis,0,max(0,total_rows-vis))
elif is_home(k): hs["scroll_row"]=0
elif is_end(k): hs["scroll_row"]=max(0,total_rows-vis)
elif k==ord('/'): hs["search_mode"]=True; hs["search_text"]=""
elif k in (ord('n'),ord('N')): _step_search(hs,1 if k==ord('n') else -1,vis,total_rows)
elif k in (ord('s'),ord('S')): hs["string_mode"]=True; hs["string_list"]=[]; hs["string_input"]="4"; hs["str_byte_set"]=set()
# ── public ────────────────────────────────────────────────────────────────────
def on_enter(frame):
_st.clear()
fst=_fstate(); hs=_hstate()
filtered=_apply(frame,fst)
data=filtered.flatten().tobytes()
hs["status_msg"]=f"Frame: {frame.shape[1]}x{frame.shape[0]} | {len(data)} bytes"
_st.update({"raw":frame,"fst":fst,"hs":hs,"data":data,"dirty":False})
def render(canvas, ww, wh, events):
fst=_st["fst"]; hs=_st["hs"]
if _st["dirty"]:
_st["dirty"]=False
filtered=_apply(_st["raw"],fst)
_st["data"]=filtered.flatten().tobytes()
hs.update({"scroll_row":0,"highlight_offset":-1,
"search_results":[],"search_result_set":set(),
"str_byte_set":set(),"string_list":[]})
hs["status_msg"]=(f"Frame: {_st['raw'].shape[1]}x{_st['raw'].shape[0]}"
f" | {len(_st['data'])} bytes"
f" ({'filter: '+fst['mode'] if fst['enabled'] else 'raw'})")
data=_st["data"]
hex_h=wh-FBAR_H
vis=max(1,(hex_h-HDR_H-STAT_H)//ROW_H)
total_rows=max(1,(len(data)+BPR-1)//BPR)
for ev,x,y,flags in events["mouse"]:
if ev==cv2.EVENT_LBUTTONDOWN:
ftag=_hit_fbar(x,y)
if ftag:
if ftag=="TOGGLE": fst["enabled"]=not fst["enabled"]; _st["dirty"]=True
elif ftag in ("1BIT","2BIT","3BIT"): fst["mode"]=ftag; _st["dirty"]=True
elif ftag in ("R","G","B"): fst[ftag]=not fst[ftag]; _st["dirty"]=True
else:
adj_y=y-FBAR_H
if HDR_H<=adj_y<hex_h-STAT_H:
row=hs["scroll_row"]+(adj_y//ROW_H)
col=col_from_x(x)
if col==-1 and X_ASCII<x<X_ASCII+17*CW: col=(x-X_ASCII-CW)//CW
if 0<=col<BPR:
off=row*BPR+col
if 0<=off<len(data):
hs["highlight_offset"]=off
hs["status_msg"]=f"Offset 0x{off:08X} ({off}) value: 0x{data[off]:02X} ({data[off]})"
elif ev==cv2.EVENT_MOUSEWHEEL:
sr=hs["scroll_row"]
hs["scroll_row"]=_clamp(sr-(1 if flags>0 else -1)*3,0,max(0,total_rows-vis))
k=events["key"]
if k != -1:
if (is_escape(k) or k in (ord('q'),ord('Q'))) and not hs["search_mode"] and not hs["string_mode"]:
return {"to":"scrub"}
_handle_key(k,hs,data,vis,total_rows)
sub=np.zeros((hex_h,ww,3),dtype=np.uint8)
hex_canvas,_,_=hex_render(data,hs,ww,hex_h)
render_status(hex_canvas,hs,hex_h,ww)
sub[:]=hex_canvas
canvas[FBAR_H:]=sub
_draw_fbar(canvas,fst,ww)
return None