kaitai-struct-visualizer 0.7 → 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 -27
- data/bin/ksdump +39 -69
- data/bin/ksv +25 -12
- data/lib/kaitai/console_ansi.rb +101 -109
- data/lib/kaitai/console_windows.rb +174 -233
- data/lib/kaitai/struct/visualizer/hex_viewer.rb +250 -243
- data/lib/kaitai/struct/visualizer/ks_error_matcher.rb +44 -0
- data/lib/kaitai/struct/visualizer/ksy_compiler.rb +178 -51
- data/lib/kaitai/struct/visualizer/node.rb +242 -230
- data/lib/kaitai/struct/visualizer/obj_to_h.rb +40 -0
- data/lib/kaitai/struct/visualizer/parser.rb +24 -26
- data/lib/kaitai/struct/visualizer/tree.rb +174 -203
- data/lib/kaitai/struct/visualizer/version.rb +3 -1
- data/lib/kaitai/struct/visualizer/visualizer.rb +11 -9
- data/lib/kaitai/struct/visualizer.rb +2 -0
- data/lib/kaitai/tui.rb +85 -98
- metadata +54 -25
- data/kaitai-struct-visualizer.gemspec +0 -37
@@ -1,295 +1,302 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'kaitai/struct/visualizer/version'
|
2
4
|
|
3
5
|
module Kaitai::Struct::Visualizer
|
4
|
-
class HexViewer
|
5
|
-
|
6
|
-
|
7
|
-
def initialize(ui, buf, tree = nil)
|
8
|
-
@ui = ui
|
9
|
-
@buf = buf
|
10
|
-
@shift_x = 0
|
11
|
-
@tree = tree
|
12
|
-
|
13
|
-
@embedded = not(tree.nil?)
|
14
|
-
@max_scr_ln = @ui.rows - 3
|
15
|
-
|
16
|
-
@addr = 0
|
17
|
-
@scroll_y = 0
|
18
|
-
reset_cur
|
19
|
-
raise if @cur_x.nil?
|
20
|
-
end
|
6
|
+
class HexViewer
|
7
|
+
attr_accessor :shift_x
|
21
8
|
|
22
|
-
|
23
|
-
|
24
|
-
|
9
|
+
def initialize(ui, buf, tree = nil)
|
10
|
+
@ui = ui
|
11
|
+
@buf = buf
|
12
|
+
@shift_x = 0
|
13
|
+
@tree = tree
|
25
14
|
|
26
|
-
|
27
|
-
|
28
|
-
@addr = a
|
29
|
-
reset_cur
|
30
|
-
end
|
15
|
+
@embedded = !tree.nil?
|
16
|
+
@max_scr_ln = @ui.rows - 3
|
31
17
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
18
|
+
@addr = 0
|
19
|
+
@scroll_y = 0
|
20
|
+
reset_cur
|
21
|
+
raise if @cur_x.nil?
|
22
|
+
end
|
36
23
|
|
37
|
-
|
38
|
-
highlight_hide
|
39
|
-
@hl_regions = regions
|
40
|
-
highlight_show
|
41
|
-
end
|
24
|
+
attr_writer :buf
|
42
25
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
@
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
26
|
+
attr_reader :addr
|
27
|
+
|
28
|
+
def addr=(a)
|
29
|
+
@addr = a
|
30
|
+
reset_cur
|
31
|
+
end
|
32
|
+
|
33
|
+
def reset_cur
|
34
|
+
@cur_y = addr_to_row(@addr)
|
35
|
+
@cur_x = addr_to_col(@addr)
|
36
|
+
end
|
37
|
+
|
38
|
+
def highlight(regions)
|
39
|
+
highlight_hide
|
40
|
+
@hl_regions = regions
|
52
41
|
highlight_show
|
53
42
|
end
|
54
|
-
end
|
55
43
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
44
|
+
def ensure_visible
|
45
|
+
scr_y = row_to_scr(@cur_y)
|
46
|
+
if scr_y.negative?
|
47
|
+
@scroll_y = @cur_y
|
48
|
+
redraw
|
49
|
+
highlight_show
|
50
|
+
elsif scr_y > @max_scr_ln
|
51
|
+
@scroll_y = @cur_y - @max_scr_ln
|
52
|
+
redraw
|
53
|
+
highlight_show
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def run
|
58
|
+
c = nil
|
59
|
+
loop do
|
60
|
+
@ui.goto(0, @max_scr_ln + 1)
|
61
|
+
printf '%08x (%d, %d)', @addr, @cur_x, @cur_y
|
62
|
+
|
63
|
+
@ui.goto(col_to_col_char(@cur_x), row_to_scr(@cur_y))
|
64
|
+
c = @ui.read_char_mapped
|
65
|
+
case c
|
66
|
+
when :tab
|
67
|
+
return if @embedded
|
68
|
+
when :left_arrow
|
69
|
+
if @addr.positive?
|
70
|
+
@addr -= 1
|
71
|
+
@cur_x -= 1
|
72
|
+
if @cur_x.negative?
|
73
|
+
@cur_y -= 1
|
74
|
+
@cur_x = PER_LINE - 1
|
75
|
+
end
|
74
76
|
end
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
77
|
+
when :right_arrow
|
78
|
+
if @addr < @buf.size
|
79
|
+
@addr += 1
|
80
|
+
@cur_x += 1
|
81
|
+
if @cur_x >= PER_LINE
|
82
|
+
@cur_y += 1
|
83
|
+
@cur_x = 0
|
84
|
+
end
|
85
|
+
end
|
86
|
+
when :up_arrow
|
87
|
+
@addr -= PER_LINE
|
88
|
+
@cur_y -= 1
|
89
|
+
clamp_cursor
|
90
|
+
when :down_arrow
|
91
|
+
@addr += PER_LINE
|
92
|
+
@cur_y += 1
|
93
|
+
clamp_cursor
|
94
|
+
when :pg_dn
|
95
|
+
@addr += PER_LINE * PAGE_ROWS
|
96
|
+
@cur_y += PAGE_ROWS
|
97
|
+
clamp_cursor
|
98
|
+
when :pg_up
|
99
|
+
@addr -= PER_LINE * PAGE_ROWS
|
100
|
+
@cur_y -= PAGE_ROWS
|
101
|
+
clamp_cursor
|
102
|
+
when :home
|
103
|
+
if @cur_x.zero?
|
104
|
+
@addr = 0
|
105
|
+
@cur_y = 0
|
106
|
+
else
|
107
|
+
@addr -= @cur_x
|
82
108
|
@cur_x = 0
|
83
109
|
end
|
110
|
+
clamp_cursor
|
111
|
+
when :end
|
112
|
+
if @cur_x == PER_LINE - 1
|
113
|
+
@addr = @buf.size - 1
|
114
|
+
reset_cur
|
115
|
+
else
|
116
|
+
@addr = @addr - @cur_x + PER_LINE - 1
|
117
|
+
@cur_x = PER_LINE - 1
|
118
|
+
end
|
119
|
+
clamp_cursor
|
120
|
+
when 'w'
|
121
|
+
fn = @ui.input_str('Write buffer to file', 'Filename')
|
122
|
+
File.open(fn, 'w') do |out|
|
123
|
+
out.write(@buf)
|
124
|
+
end
|
125
|
+
@ui.clear
|
126
|
+
redraw
|
127
|
+
when 'q'
|
128
|
+
@tree&.do_exit
|
129
|
+
return
|
84
130
|
end
|
85
|
-
when :up_arrow
|
86
|
-
@addr -= PER_LINE
|
87
|
-
@cur_y -= 1
|
88
|
-
clamp_cursor
|
89
|
-
when :down_arrow
|
90
|
-
@addr += PER_LINE
|
91
|
-
@cur_y += 1
|
92
|
-
clamp_cursor
|
93
|
-
when :pg_dn
|
94
|
-
@addr += PER_LINE * PAGE_ROWS
|
95
|
-
@cur_y += PAGE_ROWS
|
96
|
-
clamp_cursor
|
97
|
-
when :pg_up
|
98
|
-
@addr -= PER_LINE * PAGE_ROWS
|
99
|
-
@cur_y -= PAGE_ROWS
|
100
|
-
clamp_cursor
|
101
|
-
when :home
|
102
|
-
if @cur_x == 0
|
103
|
-
@addr = 0
|
104
|
-
@cur_y = 0
|
105
|
-
else
|
106
|
-
@addr -= @cur_x
|
107
|
-
@cur_x = 0
|
108
|
-
end
|
109
|
-
clamp_cursor
|
110
|
-
when :end
|
111
|
-
if @cur_x == PER_LINE - 1
|
112
|
-
@addr = @buf.size - 1
|
113
|
-
reset_cur
|
114
|
-
else
|
115
|
-
@addr = @addr - @cur_x + PER_LINE - 1
|
116
|
-
@cur_x = PER_LINE - 1
|
117
|
-
end
|
118
|
-
clamp_cursor
|
119
|
-
when 'w'
|
120
|
-
fn = @ui.input_str('Write buffer to file', 'Filename')
|
121
|
-
File.open(fn, 'w') { |out|
|
122
|
-
out.write(@buf)
|
123
|
-
}
|
124
|
-
@ui.clear
|
125
|
-
redraw
|
126
|
-
when 'q'
|
127
|
-
@tree.do_exit if @tree
|
128
|
-
return
|
129
|
-
end
|
130
131
|
|
131
|
-
|
132
|
-
|
133
|
-
|
132
|
+
ensure_visible
|
133
|
+
end
|
134
|
+
end
|
134
135
|
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
136
|
+
def clamp_cursor
|
137
|
+
if @addr.negative?
|
138
|
+
@addr = 0
|
139
|
+
@cur_x = 0
|
140
|
+
@cur_y = 0
|
141
|
+
elsif @addr >= @buf.size
|
142
|
+
@addr = @buf.size - 1
|
143
|
+
reset_cur
|
144
|
+
end
|
143
145
|
end
|
144
|
-
end
|
145
146
|
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
147
|
+
PER_LINE = 16
|
148
|
+
PER_GROUP = 4
|
149
|
+
PAGE_ROWS = 20
|
150
|
+
FMT = "%08x: %-#{PER_LINE * 3}s| %-#{PER_LINE}s\n"
|
150
151
|
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
152
|
+
def self.line_width
|
153
|
+
# 8 + 2 + 3 * PER_LINE + 2 + PER_LINE
|
154
|
+
12 + 4 * PER_LINE
|
155
|
+
end
|
155
156
|
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
157
|
+
def col_to_col_hex(c)
|
158
|
+
# 8 + 2 + 3 * c
|
159
|
+
@shift_x + 10 + 3 * c
|
160
|
+
end
|
160
161
|
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
162
|
+
def col_to_col_char(c)
|
163
|
+
# 8 + 2 + 3 * PER_LINE + 2
|
164
|
+
@shift_x + 12 + 3 * PER_LINE + c
|
165
|
+
end
|
165
166
|
|
166
|
-
|
167
|
-
|
168
|
-
|
167
|
+
def row_to_scr(r)
|
168
|
+
r - @scroll_y
|
169
|
+
end
|
169
170
|
|
170
|
-
|
171
|
-
|
172
|
-
|
171
|
+
def redraw
|
172
|
+
i = row_col_to_addr(@scroll_y, 0)
|
173
|
+
row = 0
|
173
174
|
|
174
|
-
|
175
|
-
|
176
|
-
|
175
|
+
while row <= @max_scr_ln
|
176
|
+
line = @buf[i, PER_LINE]
|
177
|
+
return unless line
|
177
178
|
|
178
|
-
|
179
|
+
@ui.goto(@shift_x, row)
|
179
180
|
|
180
|
-
|
181
|
-
|
181
|
+
hex = line.bytes.map { |x| format('%02x', x) }.join(' ')
|
182
|
+
char = line.bytes.map { |x| byte_to_display_char(x) }.join
|
182
183
|
|
183
|
-
|
184
|
-
|
185
|
-
|
184
|
+
printf FMT, i, hex, char
|
185
|
+
i += PER_LINE
|
186
|
+
row += 1
|
187
|
+
end
|
186
188
|
end
|
187
|
-
end
|
188
189
|
|
189
|
-
|
190
|
-
|
191
|
-
n = @hl_regions.size
|
192
|
-
(n - 1).downto(0).each { |i|
|
193
|
-
p1 = @hl_regions[i][0]
|
194
|
-
p2 = @hl_regions[i][1]
|
195
|
-
yield i, p1, p2 unless p1.nil?
|
196
|
-
}
|
197
|
-
end
|
190
|
+
def each_highlight_region
|
191
|
+
return if @hl_regions.nil?
|
198
192
|
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
193
|
+
n = @hl_regions.size
|
194
|
+
(n - 1).downto(0).each do |i|
|
195
|
+
p1 = @hl_regions[i][0]
|
196
|
+
p2 = @hl_regions[i][1]
|
197
|
+
yield i, p1, p2 unless p1.nil?
|
198
|
+
end
|
199
|
+
end
|
204
200
|
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
}
|
211
|
-
@ui.reset_colors
|
212
|
-
end
|
201
|
+
def highlight_hide
|
202
|
+
each_highlight_region do |_i, p1, p2|
|
203
|
+
highlight_draw(p1, p2)
|
204
|
+
end
|
205
|
+
end
|
213
206
|
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
return if i >= p2
|
222
|
-
else
|
223
|
-
c = addr_to_col(p1)
|
224
|
-
i = p1
|
207
|
+
def highlight_show
|
208
|
+
each_highlight_region do |i, p1, p2|
|
209
|
+
@ui.bg_color = @ui.highlight_colors[i]
|
210
|
+
@ui.fg_color = :black
|
211
|
+
highlight_draw(p1, p2)
|
212
|
+
end
|
213
|
+
@ui.reset_colors
|
225
214
|
end
|
226
215
|
|
227
|
-
|
228
|
-
|
229
|
-
|
216
|
+
def highlight_draw(p1, p2)
|
217
|
+
r = row_to_scr(addr_to_row(p1))
|
218
|
+
return if r > @max_scr_ln
|
230
219
|
|
231
|
-
|
232
|
-
@ui.goto(col_to_col_hex(c), r)
|
233
|
-
while i < p2
|
234
|
-
v = byte_at(i)
|
235
|
-
return if v.nil?
|
236
|
-
printf('%02x ', v)
|
237
|
-
c += 1
|
238
|
-
if c >= PER_LINE
|
220
|
+
if r.negative?
|
239
221
|
c = 0
|
240
|
-
r
|
241
|
-
|
242
|
-
|
222
|
+
r = 0
|
223
|
+
i = row_col_to_addr(@scroll_y, 0)
|
224
|
+
return if i >= p2
|
225
|
+
else
|
226
|
+
c = addr_to_col(p1)
|
227
|
+
i = p1
|
243
228
|
end
|
244
|
-
|
229
|
+
|
230
|
+
highlight_draw_hex(r, c, i, p2)
|
231
|
+
highlight_draw_char(r, c, i, p2)
|
245
232
|
end
|
246
|
-
end
|
247
233
|
|
248
|
-
|
249
|
-
|
234
|
+
def highlight_draw_hex(r, c, i, p2)
|
235
|
+
@ui.goto(col_to_col_hex(c), r)
|
236
|
+
while i < p2
|
237
|
+
v = byte_at(i)
|
238
|
+
return if v.nil?
|
250
239
|
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
240
|
+
printf('%02x ', v)
|
241
|
+
c += 1
|
242
|
+
if c >= PER_LINE
|
243
|
+
c = 0
|
244
|
+
r += 1
|
245
|
+
return if r > @max_scr_ln
|
246
|
+
|
247
|
+
@ui.goto(col_to_col_hex(c), r)
|
248
|
+
end
|
249
|
+
i += 1
|
261
250
|
end
|
262
|
-
i += 1
|
263
251
|
end
|
264
|
-
end
|
265
252
|
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
253
|
+
def highlight_draw_char(r, c, i, p2)
|
254
|
+
@ui.goto(col_to_col_char(c), r)
|
255
|
+
|
256
|
+
while i < p2
|
257
|
+
v = byte_at(i)
|
258
|
+
return if v.nil?
|
259
|
+
|
260
|
+
print byte_to_display_char(v)
|
261
|
+
c += 1
|
262
|
+
if c >= PER_LINE
|
263
|
+
c = 0
|
264
|
+
r += 1
|
265
|
+
return if r > @max_scr_ln
|
266
|
+
|
267
|
+
@ui.goto(col_to_col_char(c), r)
|
268
|
+
end
|
269
|
+
i += 1
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
def byte_at(i)
|
274
|
+
v = @buf[i]
|
275
|
+
if v.nil?
|
276
|
+
nil
|
277
|
+
else
|
278
|
+
v.ord
|
279
|
+
end
|
272
280
|
end
|
273
|
-
end
|
274
281
|
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
282
|
+
def byte_to_display_char(x)
|
283
|
+
if (x < 0x20) || (x >= 0x7f)
|
284
|
+
'.'
|
285
|
+
else
|
286
|
+
x.chr
|
287
|
+
end
|
280
288
|
end
|
281
|
-
end
|
282
289
|
|
283
|
-
|
284
|
-
|
285
|
-
|
290
|
+
def addr_to_row(addr)
|
291
|
+
addr / PER_LINE
|
292
|
+
end
|
286
293
|
|
287
|
-
|
288
|
-
|
289
|
-
|
294
|
+
def addr_to_col(addr)
|
295
|
+
addr % PER_LINE
|
296
|
+
end
|
290
297
|
|
291
|
-
|
292
|
-
|
298
|
+
def row_col_to_addr(row, col)
|
299
|
+
row * PER_LINE + col
|
300
|
+
end
|
293
301
|
end
|
294
302
|
end
|
295
|
-
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'kaitai/struct/struct'
|
4
|
+
|
5
|
+
module Kaitai::Struct::Visualizer
|
6
|
+
class KSErrorMatcher
|
7
|
+
def self.===(exc)
|
8
|
+
return true if exc.is_a?(EOFError)
|
9
|
+
return true if exc.is_a?(ArgumentError)
|
10
|
+
return true if exc.is_a?(NoMethodError)
|
11
|
+
return true if exc.is_a?(TypeError)
|
12
|
+
|
13
|
+
# Raised by the runtime library's seek() implementation for negative offsets, see
|
14
|
+
# https://github.com/kaitai-io/kaitai_struct_ruby_runtime/blob/0fa62e64949f68cb001b58b7b45e15580d154ac9/lib/kaitai/struct/struct.rb#L637
|
15
|
+
return true if exc.is_a?(Errno::EINVAL)
|
16
|
+
|
17
|
+
# KaitaiStructError is a common ancestor of all Validation*Error and
|
18
|
+
# UndecidedEndiannessError classes since 0.9. However, it doesn't exist in
|
19
|
+
# the runtime library before 0.9, so we first make sure it's defined
|
20
|
+
# before we access it (accessing an undefined item would result in a
|
21
|
+
# NameError).
|
22
|
+
return true if
|
23
|
+
defined?(Kaitai::Struct::KaitaiStructError) &&
|
24
|
+
exc.is_a?(Kaitai::Struct::KaitaiStructError)
|
25
|
+
# Since 0.9, UndecidedEndiannessError is a subclass of KaitaiStructError
|
26
|
+
# (which was already handled above), but in 0.8 it was derived directly
|
27
|
+
# from Exception (in which case it hasn't been handled yet). Also,
|
28
|
+
# switchable default endianness is a new feature in 0.8, so the
|
29
|
+
# UndecidedEndiannessError class doesn't exist in older runtimes at all.
|
30
|
+
return true if
|
31
|
+
defined?(Kaitai::Struct::Stream::UndecidedEndiannessError) &&
|
32
|
+
exc.is_a?(Kaitai::Struct::Stream::UndecidedEndiannessError)
|
33
|
+
# UnexpectedDataError is no longer thrown by KSC-generated code since 0.9 -
|
34
|
+
# it has been superseded by ValidationNotEqualError. It still exists
|
35
|
+
# even in the 0.10 runtime library, but it will be removed one day, so
|
36
|
+
# we'll also check if it's defined.
|
37
|
+
return true if
|
38
|
+
defined?(Kaitai::Struct::Stream::UnexpectedDataError) &&
|
39
|
+
exc.is_a?(Kaitai::Struct::Stream::UnexpectedDataError)
|
40
|
+
|
41
|
+
false
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|