kaitai-struct-visualizer 0.5 → 0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/LICENSE +4 -4
- data/README.md +88 -28
- data/bin/ksdump +96 -0
- data/bin/ksv +53 -7
- data/lib/kaitai/console_ansi.rb +103 -98
- data/lib/kaitai/console_windows.rb +175 -230
- data/lib/kaitai/struct/visualizer/hex_viewer.rb +250 -241
- data/lib/kaitai/struct/visualizer/ks_error_matcher.rb +44 -0
- data/lib/kaitai/struct/visualizer/ksy_compiler.rb +243 -0
- data/lib/kaitai/struct/visualizer/node.rb +241 -225
- data/lib/kaitai/struct/visualizer/obj_to_h.rb +40 -0
- data/lib/kaitai/struct/visualizer/parser.rb +40 -0
- data/lib/kaitai/struct/visualizer/tree.rb +179 -198
- data/lib/kaitai/struct/visualizer/version.rb +3 -1
- data/lib/kaitai/struct/visualizer/visualizer.rb +10 -31
- data/lib/kaitai/struct/visualizer.rb +4 -1
- data/lib/kaitai/tui.rb +85 -94
- metadata +58 -27
- data/kaitai-struct-visualizer.gemspec +0 -37
- data/lib/kaitai/struct/visualizer/visualizer_main.rb +0 -42
- data/lib/kaitai/struct/visualizer/visualizer_ruby.rb +0 -18
@@ -1,253 +1,234 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'benchmark'
|
2
4
|
|
3
5
|
require 'kaitai/struct/visualizer/version'
|
4
6
|
require 'kaitai/struct/visualizer/node'
|
5
7
|
require 'kaitai/struct/visualizer/hex_viewer'
|
8
|
+
require 'kaitai/struct/visualizer/ks_error_matcher'
|
6
9
|
|
7
10
|
module Kaitai::Struct::Visualizer
|
8
|
-
class Tree
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
11
|
+
class Tree
|
12
|
+
def initialize(ui, st)
|
13
|
+
@ui = ui
|
14
|
+
@st = st
|
15
|
+
@root = Node.new(self, st, 0)
|
16
|
+
@root.id = '[root]'
|
17
|
+
|
18
|
+
@cur_io = nil
|
19
|
+
@hv = HexViewer.new(ui, nil, self)
|
20
|
+
@hv_hidden = false
|
21
|
+
|
22
|
+
recalc_sizes
|
23
|
+
|
24
|
+
@cur_line = 0
|
25
|
+
@cur_shift = 0
|
26
|
+
@do_exit = false
|
27
|
+
|
28
|
+
@ui.on_resize = proc { |redraw_needed|
|
29
|
+
recalc_sizes
|
30
|
+
redraw if redraw_needed
|
31
|
+
@hv.redraw if redraw_needed
|
32
|
+
}
|
33
|
+
end
|
26
34
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
35
|
+
def recalc_sizes
|
36
|
+
@max_scr_ln = @ui.rows - 3
|
37
|
+
@hv.shift_x = @ui.cols - HexViewer.line_width - 1
|
38
|
+
end
|
31
39
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
40
|
+
def run
|
41
|
+
loop do
|
42
|
+
t = redraw
|
43
|
+
|
44
|
+
if @cur_node.nil? && !@cur_line.nil?
|
45
|
+
# gone beyond the end of the tree
|
46
|
+
@cur_line = @root.height - 1
|
47
|
+
clamp_cursor
|
48
|
+
redraw
|
49
|
+
end
|
38
50
|
|
39
|
-
|
40
|
-
|
51
|
+
raise '@cur_line is undetermined' if @cur_line.nil?
|
52
|
+
raise '@cur_node is undetermined' if @cur_node.nil?
|
41
53
|
|
42
|
-
|
43
|
-
|
44
|
-
|
54
|
+
thv = Benchmark.realtime do
|
55
|
+
unless @hv_hidden
|
56
|
+
hv_update_io
|
45
57
|
|
46
|
-
|
47
|
-
if (@hv.addr < @cur_node.pos1) or (@hv.addr >= @cur_node.pos2)
|
58
|
+
if !@cur_node.pos1.nil? && ((@hv.addr < @cur_node.pos1) || (@hv.addr >= @cur_node.pos2))
|
48
59
|
@hv.addr = @cur_node.pos1
|
49
60
|
@hv.ensure_visible
|
50
61
|
end
|
51
|
-
end
|
52
62
|
|
53
|
-
|
54
|
-
|
55
|
-
|
63
|
+
@hv.redraw
|
64
|
+
regs = highlight_regions(4)
|
65
|
+
@hv.highlight(regs)
|
66
|
+
end
|
56
67
|
end
|
57
|
-
}
|
58
68
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
begin
|
65
|
-
process_keypress
|
66
|
-
rescue EOFError => e
|
67
|
-
@ui.message_box_exception(e)
|
68
|
-
rescue Kaitai::Struct::Stream::UnexpectedDataError => e
|
69
|
-
@ui.message_box_exception(e)
|
70
|
-
end
|
69
|
+
@ui.goto(0, @max_scr_ln + 1)
|
70
|
+
printf 'all: %d, tree: %d, tree_draw: %d, hexview: %d, ln: %d, ', (t + thv) * 1e6, t * 1e6, @draw_time * 1e6, thv * 1e6, @ln
|
71
|
+
puts "highlight = #{@cur_node.pos1}..#{@cur_node.pos2}"
|
72
|
+
# puts "keypress: #{c.inspect}"
|
71
73
|
|
72
|
-
|
74
|
+
begin
|
75
|
+
process_keypress
|
76
|
+
rescue Kaitai::Struct::Visualizer::KSErrorMatcher => e
|
77
|
+
@ui.message_box_exception(e)
|
78
|
+
end
|
73
79
|
|
74
|
-
|
75
|
-
}
|
76
|
-
end
|
80
|
+
return if @do_exit
|
77
81
|
|
78
|
-
|
79
|
-
c = @ui.read_char_mapped
|
80
|
-
case c
|
81
|
-
when :up_arrow
|
82
|
-
@cur_line -= 1
|
83
|
-
@cur_node = nil
|
84
|
-
when :down_arrow
|
85
|
-
@cur_line += 1
|
86
|
-
@cur_node = nil
|
87
|
-
when :left_arrow
|
88
|
-
if @cur_node.open?
|
89
|
-
@cur_node.close
|
90
|
-
else
|
91
|
-
par = @cur_node.parent
|
92
|
-
if par
|
93
|
-
@cur_line = nil
|
94
|
-
@cur_node = par
|
95
|
-
end
|
82
|
+
clamp_cursor
|
96
83
|
end
|
97
|
-
|
98
|
-
|
84
|
+
end
|
85
|
+
|
86
|
+
def process_keypress
|
87
|
+
c = @ui.read_char_mapped
|
88
|
+
case c
|
89
|
+
when :up_arrow
|
90
|
+
@cur_line -= 1
|
91
|
+
@cur_node = nil
|
92
|
+
when :down_arrow
|
93
|
+
@cur_line += 1
|
94
|
+
@cur_node = nil
|
95
|
+
when :left_arrow
|
99
96
|
if @cur_node.open?
|
100
|
-
@
|
101
|
-
@cur_node = nil
|
97
|
+
@cur_node.close
|
102
98
|
else
|
103
|
-
@cur_node.
|
99
|
+
par = @cur_node.parent
|
100
|
+
if par
|
101
|
+
@cur_line = nil
|
102
|
+
@cur_node = par
|
103
|
+
end
|
104
104
|
end
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
105
|
+
when :right_arrow
|
106
|
+
if @cur_node.openable?
|
107
|
+
if @cur_node.open?
|
108
|
+
@cur_line += 1
|
109
|
+
@cur_node = nil
|
110
|
+
else
|
111
|
+
@cur_node.open
|
112
|
+
end
|
113
|
+
end
|
114
|
+
when :home
|
115
|
+
@cur_line = @cur_shift = 0
|
116
|
+
@cur_node = nil
|
117
|
+
when :end
|
118
|
+
@cur_line = @root.height - 1
|
119
|
+
@cur_node = nil
|
120
|
+
when :pg_up
|
121
|
+
@cur_line -= 20
|
122
|
+
@cur_node = nil
|
123
|
+
when :pg_dn
|
124
|
+
@cur_line += 20
|
125
|
+
@cur_node = nil
|
126
|
+
when :enter
|
127
|
+
if @cur_node.hex?
|
128
|
+
@ui.clear
|
129
|
+
hv = HexViewer.new(@ui, @cur_node.value)
|
130
|
+
hv.redraw
|
131
|
+
hv.run
|
132
|
+
@ui.clear
|
133
|
+
redraw
|
134
|
+
else
|
135
|
+
@cur_node.toggle
|
136
|
+
end
|
137
|
+
when :tab
|
138
|
+
@hv.run
|
139
|
+
when 'H'
|
140
|
+
@hv_hidden = !@hv_hidden
|
124
141
|
@ui.clear
|
125
142
|
redraw
|
126
|
-
|
127
|
-
@
|
143
|
+
when 'q'
|
144
|
+
@do_exit = true
|
128
145
|
end
|
129
|
-
when :tab
|
130
|
-
@hv.run
|
131
|
-
when 'H'
|
132
|
-
@hv_hidden = !@hv_hidden
|
133
|
-
@ui.clear
|
134
|
-
redraw
|
135
|
-
when 'q'
|
136
|
-
@do_exit = true
|
137
146
|
end
|
138
|
-
end
|
139
147
|
|
140
|
-
|
141
|
-
|
142
|
-
@cur_line = 0 if @cur_line < 0
|
148
|
+
def clamp_cursor
|
149
|
+
return unless @cur_line
|
143
150
|
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
if @cur_line - @cur_shift > @max_scr_ln
|
148
|
-
|
151
|
+
@cur_line = 0 if @cur_line.negative?
|
152
|
+
|
153
|
+
@cur_shift = @cur_line if (@cur_line - @cur_shift).negative?
|
154
|
+
@cur_shift = @cur_line - @max_scr_ln if (@cur_line - @cur_shift) > @max_scr_ln
|
155
|
+
end
|
156
|
+
|
157
|
+
def redraw
|
158
|
+
@draw_time = 0
|
159
|
+
Benchmark.realtime do
|
160
|
+
@ui.clear
|
161
|
+
@ln = 0
|
162
|
+
draw_rec(@root)
|
149
163
|
end
|
150
164
|
end
|
151
|
-
end
|
152
165
|
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
@ln
|
158
|
-
|
159
|
-
|
160
|
-
|
166
|
+
def draw_rec(n)
|
167
|
+
scr_ln = @ln - @cur_shift
|
168
|
+
return if @cur_node && (scr_ln > @max_scr_ln)
|
169
|
+
|
170
|
+
if @ln == @cur_line
|
171
|
+
# Seeking cur_node by cur_line
|
172
|
+
@cur_node = n
|
173
|
+
@ui.bg_color = :white
|
174
|
+
@ui.fg_color = :black
|
175
|
+
elsif @cur_node == n
|
176
|
+
# Seeking cur_line by cur_node
|
177
|
+
@cur_line = @ln
|
178
|
+
@ui.bg_color = :white
|
179
|
+
@ui.fg_color = :black
|
180
|
+
end
|
161
181
|
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
@
|
169
|
-
@ui.bg_color = :gray
|
170
|
-
@ui.fg_color = :black
|
171
|
-
elsif @cur_node == n
|
172
|
-
# Seeking cur_line by cur_node
|
173
|
-
@cur_line = @ln
|
174
|
-
@ui.bg_color = :gray
|
175
|
-
@ui.fg_color = :black
|
176
|
-
end
|
182
|
+
@draw_time += Benchmark.realtime do
|
183
|
+
# n.draw(@ui) if scr_ln >= 0
|
184
|
+
n.draw(@ui) if (scr_ln >= 0) && (scr_ln <= @max_scr_ln)
|
185
|
+
end
|
186
|
+
|
187
|
+
@ui.reset_colors if @ln == @cur_line
|
188
|
+
@ln += 1
|
177
189
|
|
178
|
-
|
179
|
-
# n.draw(@ui) if scr_ln >= 0
|
180
|
-
n.draw(@ui) if scr_ln >= 0 and scr_ln <= @max_scr_ln
|
181
|
-
}
|
190
|
+
return unless n.open?
|
182
191
|
|
183
|
-
|
184
|
-
@ln += 1
|
185
|
-
if n.open?
|
186
|
-
n.children.each { |ch|
|
192
|
+
n.children.each do |ch|
|
187
193
|
draw_rec(ch)
|
188
194
|
break if scr_ln > @max_scr_ln
|
189
|
-
|
195
|
+
end
|
190
196
|
end
|
191
|
-
end
|
192
197
|
|
193
|
-
|
194
|
-
|
195
|
-
|
198
|
+
def do_exit
|
199
|
+
@do_exit = true
|
200
|
+
end
|
201
|
+
|
202
|
+
def hv_update_io
|
203
|
+
io = @cur_node.io
|
204
|
+
return unless io != @cur_io
|
196
205
|
|
197
|
-
def hv_update_io
|
198
|
-
io = @cur_node.io
|
199
|
-
if io != @cur_io
|
200
206
|
@cur_io = io
|
201
207
|
io.seek(0)
|
202
208
|
buf = io.read_bytes_full
|
203
209
|
@hv.buf = buf
|
204
210
|
|
205
|
-
#
|
211
|
+
# @hv.redraw
|
206
212
|
end
|
207
|
-
end
|
208
213
|
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
r << [node.pos1, node.pos2]
|
215
|
-
node = node.parent
|
216
|
-
}
|
217
|
-
r
|
218
|
-
end
|
214
|
+
def highlight_regions(max_levels)
|
215
|
+
node = @cur_node
|
216
|
+
r = []
|
217
|
+
max_levels.times do |_i|
|
218
|
+
return r if node.nil?
|
219
219
|
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
@hv_shift_x
|
220
|
+
r << [node.pos1, node.pos2]
|
221
|
+
node = node.parent
|
222
|
+
end
|
223
|
+
r
|
225
224
|
end
|
226
|
-
end
|
227
225
|
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
obj.each_with_index { |el, i|
|
235
|
-
n = explore_object(el, level + 1)
|
236
|
-
n.id = i
|
237
|
-
root.add(n)
|
238
|
-
}
|
239
|
-
else
|
240
|
-
root = Node.new(obj, level)
|
241
|
-
obj.instance_variables.each { |k|
|
242
|
-
k = k.to_s
|
243
|
-
next if k =~ /^@_/
|
244
|
-
el = obj.instance_eval(k)
|
245
|
-
n = explore_object(el, level + 1)
|
246
|
-
n.id = k
|
247
|
-
root.add(n)
|
248
|
-
}
|
226
|
+
def tree_width
|
227
|
+
if @hv_hidden
|
228
|
+
@ui.cols
|
229
|
+
else
|
230
|
+
@hv.shift_x
|
231
|
+
end
|
249
232
|
end
|
250
|
-
root
|
251
233
|
end
|
252
234
|
end
|
253
|
-
end
|
@@ -1,7 +1,8 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'kaitai/struct/visualizer/version'
|
4
4
|
require 'kaitai/tui'
|
5
|
+
require 'kaitai/struct/visualizer/parser'
|
5
6
|
require 'kaitai/struct/visualizer/tree'
|
6
7
|
|
7
8
|
# TODO: should be inside compiled files
|
@@ -9,39 +10,17 @@ require 'zlib'
|
|
9
10
|
require 'stringio'
|
10
11
|
|
11
12
|
module Kaitai::Struct::Visualizer
|
12
|
-
class Visualizer
|
13
|
-
|
14
|
-
|
15
|
-
@formats_fn = formats_fn
|
16
|
-
@primary_format = @formats_fn.shift
|
13
|
+
class Visualizer < Parser
|
14
|
+
def run
|
15
|
+
load_exc = load
|
17
16
|
|
18
|
-
|
17
|
+
@ui = Kaitai::TUI.new
|
18
|
+
@tree = Tree.new(@ui, @data)
|
19
19
|
|
20
|
-
|
21
|
-
|
22
|
-
}
|
20
|
+
@tree.redraw
|
21
|
+
@ui.message_box_exception(load_exc) if load_exc
|
23
22
|
|
24
|
-
|
25
|
-
@data = main_class.from_file(@bin_fn)
|
26
|
-
|
27
|
-
load_exc = nil
|
28
|
-
begin
|
29
|
-
@data._read
|
30
|
-
rescue EOFError => e
|
31
|
-
load_exc = e
|
32
|
-
rescue Kaitai::Struct::Stream::UnexpectedDataError => e
|
33
|
-
load_exc = e
|
23
|
+
@tree.run
|
34
24
|
end
|
35
|
-
|
36
|
-
@ui = Kaitai::TUI.new
|
37
|
-
@tree = Tree.new(@ui, @data)
|
38
|
-
|
39
|
-
@tree.redraw
|
40
|
-
@ui.message_box_exception(load_exc) if load_exc
|
41
25
|
end
|
42
|
-
|
43
|
-
def run
|
44
|
-
@tree.run
|
45
|
-
end
|
46
|
-
end
|
47
26
|
end
|