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,257 +1,198 @@
|
|
1
|
-
#
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'fiddle'
|
3
4
|
require 'readline'
|
4
5
|
|
5
6
|
module Kaitai
|
7
|
+
class ConsoleWindows
|
8
|
+
attr_reader :cols, :rows
|
6
9
|
|
7
|
-
|
8
|
-
attr_reader :cols
|
9
|
-
attr_reader :rows
|
10
|
-
|
11
|
-
GET_STD_HANDLE = Win32API.new('kernel32', 'GetStdHandle', 'L', 'L')
|
12
|
-
GET_CONSOLE_SCREEN_BUFFER_INFO = Win32API.new('kernel32', 'GetConsoleScreenBufferInfo', 'LP', 'L')
|
13
|
-
|
14
|
-
FILL_CONSOLE_OUTPUT_ATTRIBUTE = Win32API.new('kernel32', 'FillConsoleOutputAttribute', 'LILLP', 'I')
|
15
|
-
FILL_CONSOLE_OUTPUT_CHARACTER = Win32API.new('kernel32', 'FillConsoleOutputCharacter', 'LILLP', 'I')
|
16
|
-
SET_CONSOLE_CURSOR_POSITION = Win32API.new('kernel32', 'SetConsoleCursorPosition', 'LI', 'I')
|
17
|
-
SET_CONSOLE_TEXT_ATTRIBUTE = Win32API.new('kernel32', 'SetConsoleTextAttribute', 'LL', 'I')
|
18
|
-
|
19
|
-
WRITE_CONSOLE = Win32API.new("kernel32", "WriteConsole", ['l', 'p', 'l', 'p', 'p'], 'l')
|
20
|
-
|
21
|
-
GETCH = Win32API.new("msvcrt", "_getch", [], 'I')
|
22
|
-
|
23
|
-
def initialize
|
24
|
-
@stdin_handle = GET_STD_HANDLE.call(-10)
|
25
|
-
@stdout_handle = GET_STD_HANDLE.call(-11)
|
26
|
-
@fg_color = 7
|
27
|
-
@bg_color = 0
|
28
|
-
|
29
|
-
# typedef struct _CONSOLE_SCREEN_BUFFER_INFO {
|
30
|
-
# COORD dwSize;
|
31
|
-
# COORD dwCursorPosition;
|
32
|
-
# WORD wAttributes;
|
33
|
-
# SMALL_RECT srWindow;
|
34
|
-
# COORD dwMaximumWindowSize;
|
35
|
-
# } CONSOLE_SCREEN_BUFFER_INFO;
|
36
|
-
|
37
|
-
# 4 + 4 + 2 + 4 * 2 + 4 = 22
|
38
|
-
|
39
|
-
csbi = 'X' * 22
|
40
|
-
|
41
|
-
GET_CONSOLE_SCREEN_BUFFER_INFO.call(@stdout_handle, csbi)
|
42
|
-
@buf_cols, @buf_rows,
|
43
|
-
cur_x, cur_y, cur_attr,
|
44
|
-
win_left, win_top, win_right, win_bottom,
|
45
|
-
max_win_x, max_win_y = csbi.unpack('vvvvvvvvvvv')
|
46
|
-
|
47
|
-
# Getting size of actual visible portion of Windows console
|
48
|
-
# http://stackoverflow.com/a/12642749/487064
|
49
|
-
@cols = win_right - win_left + 1
|
50
|
-
@rows = win_bottom - win_top + 1
|
51
|
-
end
|
10
|
+
kernel32 = Fiddle.dlopen('kernel32')
|
52
11
|
|
53
|
-
|
54
|
-
|
55
|
-
|
12
|
+
dword = Fiddle::TYPE_LONG
|
13
|
+
word = Fiddle::TYPE_SHORT
|
14
|
+
ptr = Fiddle::TYPE_VOIDP
|
15
|
+
handle = Fiddle::TYPE_VOIDP
|
56
16
|
|
57
|
-
|
58
|
-
|
59
|
-
# num_written = 'XXXX'
|
60
|
-
# reserved = 'XXXX'
|
61
|
-
# WRITE_CONSOLE.call(@stdout_handle, s, s.length, num_written, reserved)
|
62
|
-
end
|
63
|
-
|
64
|
-
def clear
|
65
|
-
con_size = @buf_cols * @buf_rows
|
66
|
-
num_written = 'XXXX'
|
67
|
-
FILL_CONSOLE_OUTPUT_CHARACTER.call(
|
68
|
-
@stdout_handle,
|
69
|
-
0x20, # ' '
|
70
|
-
con_size,
|
71
|
-
0, # [0, 0] coords
|
72
|
-
num_written
|
73
|
-
)
|
74
|
-
FILL_CONSOLE_OUTPUT_ATTRIBUTE.call(
|
75
|
-
@stdout_handle,
|
76
|
-
current_color_code,
|
77
|
-
con_size,
|
78
|
-
0, # [0, 0] coords
|
79
|
-
num_written
|
80
|
-
)
|
81
|
-
goto(0, 0)
|
82
|
-
end
|
17
|
+
GET_STD_HANDLE = Fiddle::Function.new(kernel32['GetStdHandle'], [dword], handle)
|
18
|
+
GET_CONSOLE_SCREEN_BUFFER_INFO = Fiddle::Function.new(kernel32['GetConsoleScreenBufferInfo'], [handle, ptr], dword)
|
83
19
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
coord = [x, y].pack('vv').unpack('V')[0]
|
89
|
-
SET_CONSOLE_CURSOR_POSITION.call(@stdout_handle, coord)
|
90
|
-
end
|
20
|
+
FILL_CONSOLE_OUTPUT_ATTRIBUTE = Fiddle::Function.new(kernel32['FillConsoleOutputAttribute'], [handle, word, dword, dword, ptr], dword)
|
21
|
+
FILL_CONSOLE_OUTPUT_CHARACTER = Fiddle::Function.new(kernel32['FillConsoleOutputCharacter'], [handle, word, dword, dword, ptr], dword)
|
22
|
+
SET_CONSOLE_CURSOR_POSITION = Fiddle::Function.new(kernel32['SetConsoleCursorPosition'], [handle, dword], dword)
|
23
|
+
SET_CONSOLE_TEXT_ATTRIBUTE = Fiddle::Function.new(kernel32['SetConsoleTextAttribute'], [handle, dword], dword)
|
91
24
|
|
92
|
-
|
93
|
-
:black => 0,
|
94
|
-
:blue => 1,
|
95
|
-
:green => 2,
|
96
|
-
:aqua => 3,
|
97
|
-
:red => 4,
|
98
|
-
:purple => 5,
|
99
|
-
:yellow => 6,
|
100
|
-
:white => 7,
|
101
|
-
:gray => 8,
|
102
|
-
:light_blue => 9,
|
103
|
-
:light_green => 0xa,
|
104
|
-
:light_aqua => 0xb,
|
105
|
-
:light_red => 0xc,
|
106
|
-
:light_purple => 0xd,
|
107
|
-
:light_yellow => 0xe,
|
108
|
-
:bright_white => 0xf,
|
109
|
-
}
|
110
|
-
|
111
|
-
def fg_color=(col)
|
112
|
-
code = COLORS[col]
|
113
|
-
raise "Invalid color: #{col}" unless code
|
114
|
-
@fg_color = code
|
115
|
-
update_colors
|
116
|
-
end
|
25
|
+
GETCH = Fiddle::Function.new(Fiddle.dlopen('msvcrt')['_getch'], [], word)
|
117
26
|
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
end
|
27
|
+
def initialize
|
28
|
+
@stdin_handle = GET_STD_HANDLE.call(-10)
|
29
|
+
@stdout_handle = GET_STD_HANDLE.call(-11)
|
30
|
+
@fg_color = 7
|
31
|
+
@bg_color = 0
|
124
32
|
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
33
|
+
# typedef struct _CONSOLE_SCREEN_BUFFER_INFO {
|
34
|
+
# COORD dwSize;
|
35
|
+
# COORD dwCursorPosition;
|
36
|
+
# WORD wAttributes;
|
37
|
+
# SMALL_RECT srWindow;
|
38
|
+
# COORD dwMaximumWindowSize;
|
39
|
+
# } CONSOLE_SCREEN_BUFFER_INFO;
|
40
|
+
|
41
|
+
# 4 + 4 + 2 + 4 * 2 + 4 = 22
|
130
42
|
|
131
|
-
|
132
|
-
E0_ESCAPE = 0xe0.chr
|
133
|
-
|
134
|
-
# Reads keypresses from the user including 2 and 3 escape character sequences.
|
135
|
-
def read_char
|
136
|
-
input = GETCH.call.chr
|
137
|
-
if input == E0_ESCAPE || input == ZERO_ESCAPE
|
138
|
-
input << GETCH.call.chr
|
43
|
+
load_term_size
|
139
44
|
end
|
140
|
-
return input
|
141
|
-
end
|
142
45
|
|
143
|
-
|
144
|
-
|
145
|
-
c2 = KEY_MAP[c]
|
146
|
-
c2 ? c2 : c
|
147
|
-
end
|
46
|
+
def load_term_size
|
47
|
+
csbi = 'X' * 22
|
148
48
|
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
# Regular AT keyboard arrows
|
155
|
-
E0_ESCAPE + "H" => :up_arrow,
|
156
|
-
E0_ESCAPE + "P" => :down_arrow,
|
157
|
-
E0_ESCAPE + "K" => :left_arrow,
|
158
|
-
E0_ESCAPE + "M" => :right_arrow,
|
159
|
-
E0_ESCAPE + "I" => :pg_up,
|
160
|
-
E0_ESCAPE + "Q" => :pg_dn,
|
161
|
-
E0_ESCAPE + "G" => :home,
|
162
|
-
E0_ESCAPE + "O" => :end,
|
163
|
-
|
164
|
-
# Keypad
|
165
|
-
ZERO_ESCAPE + "H" => :up_arrow,
|
166
|
-
ZERO_ESCAPE + "P" => :down_arrow,
|
167
|
-
ZERO_ESCAPE + "K" => :left_arrow,
|
168
|
-
ZERO_ESCAPE + "M" => :right_arrow,
|
169
|
-
ZERO_ESCAPE + "I" => :pg_up,
|
170
|
-
ZERO_ESCAPE + "Q" => :pg_dn,
|
171
|
-
ZERO_ESCAPE + "G" => :home,
|
172
|
-
ZERO_ESCAPE + "O" => :end,
|
173
|
-
}
|
174
|
-
|
175
|
-
def message_box_exception(e)
|
176
|
-
message_box("Error while parsing", e.message)
|
177
|
-
end
|
49
|
+
GET_CONSOLE_SCREEN_BUFFER_INFO.call(@stdout_handle, csbi)
|
50
|
+
@buf_cols, @buf_rows,
|
51
|
+
_cur_x, _cur_y, _cur_attr,
|
52
|
+
win_left, win_top, win_right, win_bottom,
|
53
|
+
_max_win_x, _max_win_y = csbi.unpack('vvvvvvvvvvv')
|
178
54
|
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
CHAR_TR = 1
|
185
|
-
CHAR_BL = 2
|
186
|
-
CHAR_BR = 3
|
187
|
-
CHAR_H = 4
|
188
|
-
CHAR_V = 5
|
189
|
-
|
190
|
-
def message_box(header, msg)
|
191
|
-
top_y = @rows / 2 - 5
|
192
|
-
draw_rectangle(10, top_y, @cols - 20, 10)
|
193
|
-
goto(@cols / 2 - (header.length / 2) - 1, top_y)
|
194
|
-
print ' ', header, ' '
|
195
|
-
goto(11, top_y + 1)
|
196
|
-
puts msg
|
197
|
-
draw_button(@cols / 2 - 10, top_y + 8, 10, 'OK')
|
198
|
-
loop {
|
199
|
-
c = read_char_mapped
|
200
|
-
return if c == :enter
|
201
|
-
}
|
202
|
-
end
|
55
|
+
# Getting size of actual visible portion of Windows console
|
56
|
+
# http://stackoverflow.com/a/12642749/487064
|
57
|
+
@cols = win_right - win_left + 1
|
58
|
+
@rows = win_bottom - win_top + 1
|
59
|
+
end
|
203
60
|
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
61
|
+
attr_writer :on_resize
|
62
|
+
|
63
|
+
def clear
|
64
|
+
con_size = @buf_cols * @buf_rows
|
65
|
+
num_written = +'XXXX'
|
66
|
+
FILL_CONSOLE_OUTPUT_CHARACTER.call(
|
67
|
+
@stdout_handle,
|
68
|
+
0x20, # ' '
|
69
|
+
con_size,
|
70
|
+
0, # [0, 0] coords
|
71
|
+
num_written
|
72
|
+
)
|
73
|
+
FILL_CONSOLE_OUTPUT_ATTRIBUTE.call(
|
74
|
+
@stdout_handle,
|
75
|
+
current_color_code,
|
76
|
+
con_size,
|
77
|
+
0, # [0, 0] coords
|
78
|
+
num_written
|
79
|
+
)
|
80
|
+
goto(0, 0)
|
81
|
+
end
|
209
82
|
|
210
|
-
|
211
|
-
|
212
|
-
|
83
|
+
# Put the cursor up to screen position (x, y). First line is 0, first column is 0.
|
84
|
+
def goto(x, y)
|
85
|
+
coord = [x, y].pack('vv').unpack1('V')
|
86
|
+
SET_CONSOLE_CURSOR_POSITION.call(@stdout_handle, coord)
|
87
|
+
end
|
213
88
|
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
89
|
+
COLORS = {
|
90
|
+
black: 0,
|
91
|
+
blue: 1,
|
92
|
+
green: 2,
|
93
|
+
cyan: 3,
|
94
|
+
red: 4,
|
95
|
+
magenta: 5,
|
96
|
+
yellow: 6,
|
97
|
+
white: 7,
|
98
|
+
gray: 8,
|
99
|
+
bright_blue: 9,
|
100
|
+
bright_green: 10,
|
101
|
+
bright_cyan: 11,
|
102
|
+
bright_red: 12,
|
103
|
+
bright_magenta: 13,
|
104
|
+
bright_yellow: 14,
|
105
|
+
bright_white: 15
|
106
|
+
}.freeze
|
107
|
+
|
108
|
+
def fg_color=(col)
|
109
|
+
code = COLORS[col]
|
110
|
+
raise "Invalid color: #{col}" unless code
|
111
|
+
|
112
|
+
@fg_color = code
|
113
|
+
update_colors
|
114
|
+
end
|
232
115
|
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
end
|
116
|
+
def bg_color=(col)
|
117
|
+
code = COLORS[col]
|
118
|
+
raise "Invalid color: #{col}" unless code
|
237
119
|
|
238
|
-
|
239
|
-
|
240
|
-
|
120
|
+
@bg_color = code
|
121
|
+
update_colors
|
122
|
+
end
|
241
123
|
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
124
|
+
def reset_colors
|
125
|
+
@fg_color = 7
|
126
|
+
@bg_color = 0
|
127
|
+
update_colors
|
128
|
+
end
|
246
129
|
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
end
|
255
|
-
end
|
130
|
+
ZERO_ESCAPE = 0.chr
|
131
|
+
E0_ESCAPE = 0xe0.chr
|
132
|
+
|
133
|
+
# Reads keypresses from the user including 2 and 3 escape character sequences.
|
134
|
+
def read_char
|
135
|
+
input = GETCH.call.chr
|
136
|
+
input << GETCH.call.chr if input == E0_ESCAPE || input == ZERO_ESCAPE
|
256
137
|
|
138
|
+
# https://github.com/kaitai-io/kaitai_struct_visualizer/issues/14
|
139
|
+
load_term_size
|
140
|
+
@on_resize&.call(false)
|
141
|
+
|
142
|
+
input
|
143
|
+
end
|
144
|
+
|
145
|
+
def read_char_mapped
|
146
|
+
c = read_char
|
147
|
+
c2 = KEY_MAP[c]
|
148
|
+
c2 || c
|
149
|
+
end
|
150
|
+
|
151
|
+
KEY_MAP = {
|
152
|
+
"\b" => :backspace,
|
153
|
+
"\t" => :tab,
|
154
|
+
"\r" => :enter,
|
155
|
+
|
156
|
+
# Regular AT keyboard arrows
|
157
|
+
"#{E0_ESCAPE}H" => :up_arrow,
|
158
|
+
"#{E0_ESCAPE}P" => :down_arrow,
|
159
|
+
"#{E0_ESCAPE}K" => :left_arrow,
|
160
|
+
"#{E0_ESCAPE}M" => :right_arrow,
|
161
|
+
"#{E0_ESCAPE}I" => :pg_up,
|
162
|
+
"#{E0_ESCAPE}Q" => :pg_dn,
|
163
|
+
"#{E0_ESCAPE}G" => :home,
|
164
|
+
"#{E0_ESCAPE}O" => :end,
|
165
|
+
|
166
|
+
# Keypad
|
167
|
+
"#{ZERO_ESCAPE}H" => :up_arrow,
|
168
|
+
"#{ZERO_ESCAPE}P" => :down_arrow,
|
169
|
+
"#{ZERO_ESCAPE}K" => :left_arrow,
|
170
|
+
"#{ZERO_ESCAPE}M" => :right_arrow,
|
171
|
+
"#{ZERO_ESCAPE}I" => :pg_up,
|
172
|
+
"#{ZERO_ESCAPE}Q" => :pg_dn,
|
173
|
+
"#{ZERO_ESCAPE}G" => :home,
|
174
|
+
"#{ZERO_ESCAPE}O" => :end
|
175
|
+
}.freeze
|
176
|
+
|
177
|
+
SINGLE_CHARSET = '┌┐└┘─│'
|
178
|
+
HEAVY_CHARSET = '┏┓┗┛━┃'
|
179
|
+
DOUBLE_CHARSET = '╔╗╚╝═║'
|
180
|
+
|
181
|
+
CHAR_TL = 0
|
182
|
+
CHAR_TR = 1
|
183
|
+
CHAR_BL = 2
|
184
|
+
CHAR_BR = 3
|
185
|
+
CHAR_H = 4
|
186
|
+
CHAR_V = 5
|
187
|
+
|
188
|
+
private
|
189
|
+
|
190
|
+
def current_color_code
|
191
|
+
(@bg_color << 4) | @fg_color
|
192
|
+
end
|
193
|
+
|
194
|
+
def update_colors
|
195
|
+
SET_CONSOLE_TEXT_ATTRIBUTE.call(@stdout_handle, current_color_code)
|
196
|
+
end
|
197
|
+
end
|
257
198
|
end
|