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,293 +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
|
-
@ui = ui
|
7
|
-
@buf = buf
|
8
|
-
@shift_x = shift_x
|
9
|
-
@tree = tree
|
10
|
-
|
11
|
-
@embedded = not(tree.nil?)
|
12
|
-
@max_scr_ln = @ui.rows - 3
|
13
|
-
|
14
|
-
@addr = 0
|
15
|
-
@scroll_y = 0
|
16
|
-
reset_cur
|
17
|
-
raise if @cur_x.nil?
|
18
|
-
end
|
6
|
+
class HexViewer
|
7
|
+
attr_accessor :shift_x
|
19
8
|
|
20
|
-
|
21
|
-
|
22
|
-
|
9
|
+
def initialize(ui, buf, tree = nil)
|
10
|
+
@ui = ui
|
11
|
+
@buf = buf
|
12
|
+
@shift_x = 0
|
13
|
+
@tree = tree
|
23
14
|
|
24
|
-
|
25
|
-
|
26
|
-
@addr = a
|
27
|
-
reset_cur
|
28
|
-
end
|
15
|
+
@embedded = !tree.nil?
|
16
|
+
@max_scr_ln = @ui.rows - 3
|
29
17
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
18
|
+
@addr = 0
|
19
|
+
@scroll_y = 0
|
20
|
+
reset_cur
|
21
|
+
raise if @cur_x.nil?
|
22
|
+
end
|
34
23
|
|
35
|
-
|
36
|
-
highlight_hide
|
37
|
-
@hl_regions = regions
|
38
|
-
highlight_show
|
39
|
-
end
|
24
|
+
attr_writer :buf
|
40
25
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
@
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
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
|
50
41
|
highlight_show
|
51
42
|
end
|
52
|
-
end
|
53
43
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
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
|
72
76
|
end
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
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
|
80
108
|
@cur_x = 0
|
81
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
|
82
130
|
end
|
83
|
-
when :up_arrow
|
84
|
-
@addr -= PER_LINE
|
85
|
-
@cur_y -= 1
|
86
|
-
clamp_cursor
|
87
|
-
when :down_arrow
|
88
|
-
@addr += PER_LINE
|
89
|
-
@cur_y += 1
|
90
|
-
clamp_cursor
|
91
|
-
when :pg_dn
|
92
|
-
@addr += PER_LINE * PAGE_ROWS
|
93
|
-
@cur_y += PAGE_ROWS
|
94
|
-
clamp_cursor
|
95
|
-
when :pg_up
|
96
|
-
@addr -= PER_LINE * PAGE_ROWS
|
97
|
-
@cur_y -= PAGE_ROWS
|
98
|
-
clamp_cursor
|
99
|
-
when :home
|
100
|
-
if @cur_x == 0
|
101
|
-
@addr = 0
|
102
|
-
@cur_y = 0
|
103
|
-
else
|
104
|
-
@addr -= @cur_x
|
105
|
-
@cur_x = 0
|
106
|
-
end
|
107
|
-
clamp_cursor
|
108
|
-
when :end
|
109
|
-
if @cur_x == PER_LINE - 1
|
110
|
-
@addr = @buf.size - 1
|
111
|
-
reset_cur
|
112
|
-
else
|
113
|
-
@addr = @addr - @cur_x + PER_LINE - 1
|
114
|
-
@cur_x = PER_LINE - 1
|
115
|
-
end
|
116
|
-
clamp_cursor
|
117
|
-
when 'w'
|
118
|
-
fn = @ui.input_str('Write buffer to file', 'Filename')
|
119
|
-
File.open(fn, 'w') { |out|
|
120
|
-
out.write(@buf)
|
121
|
-
}
|
122
|
-
@ui.clear
|
123
|
-
redraw
|
124
|
-
when 'q'
|
125
|
-
@tree.do_exit if @tree
|
126
|
-
return
|
127
|
-
end
|
128
131
|
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
+
ensure_visible
|
133
|
+
end
|
134
|
+
end
|
132
135
|
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
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
|
141
145
|
end
|
142
|
-
end
|
143
146
|
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
147
|
+
PER_LINE = 16
|
148
|
+
PER_GROUP = 4
|
149
|
+
PAGE_ROWS = 20
|
150
|
+
FMT = "%08x: %-#{PER_LINE * 3}s| %-#{PER_LINE}s\n"
|
148
151
|
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
152
|
+
def self.line_width
|
153
|
+
# 8 + 2 + 3 * PER_LINE + 2 + PER_LINE
|
154
|
+
12 + 4 * PER_LINE
|
155
|
+
end
|
153
156
|
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
157
|
+
def col_to_col_hex(c)
|
158
|
+
# 8 + 2 + 3 * c
|
159
|
+
@shift_x + 10 + 3 * c
|
160
|
+
end
|
158
161
|
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
162
|
+
def col_to_col_char(c)
|
163
|
+
# 8 + 2 + 3 * PER_LINE + 2
|
164
|
+
@shift_x + 12 + 3 * PER_LINE + c
|
165
|
+
end
|
163
166
|
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
+
def row_to_scr(r)
|
168
|
+
r - @scroll_y
|
169
|
+
end
|
167
170
|
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
+
def redraw
|
172
|
+
i = row_col_to_addr(@scroll_y, 0)
|
173
|
+
row = 0
|
171
174
|
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
+
while row <= @max_scr_ln
|
176
|
+
line = @buf[i, PER_LINE]
|
177
|
+
return unless line
|
175
178
|
|
176
|
-
|
179
|
+
@ui.goto(@shift_x, row)
|
177
180
|
|
178
|
-
|
179
|
-
|
181
|
+
hex = line.bytes.map { |x| format('%02x', x) }.join(' ')
|
182
|
+
char = line.bytes.map { |x| byte_to_display_char(x) }.join
|
180
183
|
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
+
printf FMT, i, hex, char
|
185
|
+
i += PER_LINE
|
186
|
+
row += 1
|
187
|
+
end
|
184
188
|
end
|
185
|
-
end
|
186
189
|
|
187
|
-
|
188
|
-
|
189
|
-
n = @hl_regions.size
|
190
|
-
(n - 1).downto(0).each { |i|
|
191
|
-
p1 = @hl_regions[i][0]
|
192
|
-
p2 = @hl_regions[i][1]
|
193
|
-
yield i, p1, p2 unless p1.nil?
|
194
|
-
}
|
195
|
-
end
|
190
|
+
def each_highlight_region
|
191
|
+
return if @hl_regions.nil?
|
196
192
|
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
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
|
202
200
|
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
}
|
209
|
-
@ui.reset_colors
|
210
|
-
end
|
201
|
+
def highlight_hide
|
202
|
+
each_highlight_region do |_i, p1, p2|
|
203
|
+
highlight_draw(p1, p2)
|
204
|
+
end
|
205
|
+
end
|
211
206
|
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
return if i >= p2
|
220
|
-
else
|
221
|
-
c = addr_to_col(p1)
|
222
|
-
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
|
223
214
|
end
|
224
215
|
|
225
|
-
|
226
|
-
|
227
|
-
|
216
|
+
def highlight_draw(p1, p2)
|
217
|
+
r = row_to_scr(addr_to_row(p1))
|
218
|
+
return if r > @max_scr_ln
|
228
219
|
|
229
|
-
|
230
|
-
@ui.goto(col_to_col_hex(c), r)
|
231
|
-
while i < p2
|
232
|
-
v = byte_at(i)
|
233
|
-
return if v.nil?
|
234
|
-
printf('%02x ', v)
|
235
|
-
c += 1
|
236
|
-
if c >= PER_LINE
|
220
|
+
if r.negative?
|
237
221
|
c = 0
|
238
|
-
r
|
239
|
-
|
240
|
-
|
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
|
241
228
|
end
|
242
|
-
|
229
|
+
|
230
|
+
highlight_draw_hex(r, c, i, p2)
|
231
|
+
highlight_draw_char(r, c, i, p2)
|
243
232
|
end
|
244
|
-
end
|
245
233
|
|
246
|
-
|
247
|
-
|
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?
|
248
239
|
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
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
|
259
250
|
end
|
260
|
-
i += 1
|
261
251
|
end
|
262
|
-
end
|
263
252
|
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
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
|
270
280
|
end
|
271
|
-
end
|
272
281
|
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
282
|
+
def byte_to_display_char(x)
|
283
|
+
if (x < 0x20) || (x >= 0x7f)
|
284
|
+
'.'
|
285
|
+
else
|
286
|
+
x.chr
|
287
|
+
end
|
278
288
|
end
|
279
|
-
end
|
280
289
|
|
281
|
-
|
282
|
-
|
283
|
-
|
290
|
+
def addr_to_row(addr)
|
291
|
+
addr / PER_LINE
|
292
|
+
end
|
284
293
|
|
285
|
-
|
286
|
-
|
287
|
-
|
294
|
+
def addr_to_col(addr)
|
295
|
+
addr % PER_LINE
|
296
|
+
end
|
288
297
|
|
289
|
-
|
290
|
-
|
298
|
+
def row_col_to_addr(row, col)
|
299
|
+
row * PER_LINE + col
|
300
|
+
end
|
291
301
|
end
|
292
302
|
end
|
293
|
-
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
|