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,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
|
-
|
56
|
-
|
57
|
-
# WRITE_CONSOLE.call(@stdout_handle, s, s.length, num_written, reserved)
|
58
|
-
end
|
59
|
-
|
60
|
-
def clear
|
61
|
-
con_size = @buf_cols * @buf_rows
|
62
|
-
num_written = 'XXXX'
|
63
|
-
FILL_CONSOLE_OUTPUT_CHARACTER.call(
|
64
|
-
@stdout_handle,
|
65
|
-
0x20, # ' '
|
66
|
-
con_size,
|
67
|
-
0, # [0, 0] coords
|
68
|
-
num_written
|
69
|
-
)
|
70
|
-
FILL_CONSOLE_OUTPUT_ATTRIBUTE.call(
|
71
|
-
@stdout_handle,
|
72
|
-
current_color_code,
|
73
|
-
con_size,
|
74
|
-
0, # [0, 0] coords
|
75
|
-
num_written
|
76
|
-
)
|
77
|
-
goto(0, 0)
|
78
|
-
end
|
12
|
+
dword = Fiddle::TYPE_LONG
|
13
|
+
word = Fiddle::TYPE_SHORT
|
14
|
+
ptr = Fiddle::TYPE_VOIDP
|
15
|
+
handle = Fiddle::TYPE_VOIDP
|
79
16
|
|
80
|
-
|
81
|
-
|
82
|
-
# first column is 0.
|
83
|
-
def goto(x, y)
|
84
|
-
coord = [x, y].pack('vv').unpack('V')[0]
|
85
|
-
SET_CONSOLE_CURSOR_POSITION.call(@stdout_handle, coord)
|
86
|
-
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)
|
87
19
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
:aqua => 3,
|
93
|
-
:red => 4,
|
94
|
-
:purple => 5,
|
95
|
-
:yellow => 6,
|
96
|
-
:white => 7,
|
97
|
-
:gray => 8,
|
98
|
-
:light_blue => 9,
|
99
|
-
:light_green => 0xa,
|
100
|
-
:light_aqua => 0xb,
|
101
|
-
:light_red => 0xc,
|
102
|
-
:light_purple => 0xd,
|
103
|
-
:light_yellow => 0xe,
|
104
|
-
:bright_white => 0xf,
|
105
|
-
}
|
106
|
-
|
107
|
-
def fg_color=(col)
|
108
|
-
code = COLORS[col]
|
109
|
-
raise "Invalid color: #{col}" unless code
|
110
|
-
@fg_color = code
|
111
|
-
update_colors
|
112
|
-
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)
|
113
24
|
|
114
|
-
|
115
|
-
code = COLORS[col]
|
116
|
-
raise "Invalid color: #{col}" unless code
|
117
|
-
@bg_color = code
|
118
|
-
update_colors
|
119
|
-
end
|
25
|
+
GETCH = Fiddle::Function.new(Fiddle.dlopen('msvcrt')['_getch'], [], word)
|
120
26
|
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
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
|
32
|
+
|
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
|
126
42
|
|
127
|
-
|
128
|
-
E0_ESCAPE = 0xe0.chr
|
129
|
-
|
130
|
-
# Reads keypresses from the user including 2 and 3 escape character sequences.
|
131
|
-
def read_char
|
132
|
-
input = GETCH.call.chr
|
133
|
-
if input == E0_ESCAPE || input == ZERO_ESCAPE
|
134
|
-
input << GETCH.call.chr
|
43
|
+
load_term_size
|
135
44
|
end
|
136
|
-
return input
|
137
|
-
end
|
138
45
|
|
139
|
-
|
140
|
-
|
141
|
-
c2 = KEY_MAP[c]
|
142
|
-
c2 ? c2 : c
|
143
|
-
end
|
46
|
+
def load_term_size
|
47
|
+
csbi = 'X' * 22
|
144
48
|
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
# Regular AT keyboard arrows
|
151
|
-
E0_ESCAPE + "H" => :up_arrow,
|
152
|
-
E0_ESCAPE + "P" => :down_arrow,
|
153
|
-
E0_ESCAPE + "K" => :left_arrow,
|
154
|
-
E0_ESCAPE + "M" => :right_arrow,
|
155
|
-
E0_ESCAPE + "I" => :pg_up,
|
156
|
-
E0_ESCAPE + "Q" => :pg_dn,
|
157
|
-
E0_ESCAPE + "G" => :home,
|
158
|
-
E0_ESCAPE + "O" => :end,
|
159
|
-
|
160
|
-
# Keypad
|
161
|
-
ZERO_ESCAPE + "H" => :up_arrow,
|
162
|
-
ZERO_ESCAPE + "P" => :down_arrow,
|
163
|
-
ZERO_ESCAPE + "K" => :left_arrow,
|
164
|
-
ZERO_ESCAPE + "M" => :right_arrow,
|
165
|
-
ZERO_ESCAPE + "I" => :pg_up,
|
166
|
-
ZERO_ESCAPE + "Q" => :pg_dn,
|
167
|
-
ZERO_ESCAPE + "G" => :home,
|
168
|
-
ZERO_ESCAPE + "O" => :end,
|
169
|
-
}
|
170
|
-
|
171
|
-
def message_box_exception(e)
|
172
|
-
message_box("Error while parsing", e.message)
|
173
|
-
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')
|
174
54
|
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
CHAR_TR = 1
|
181
|
-
CHAR_BL = 2
|
182
|
-
CHAR_BR = 3
|
183
|
-
CHAR_H = 4
|
184
|
-
CHAR_V = 5
|
185
|
-
|
186
|
-
def message_box(header, msg)
|
187
|
-
top_y = @rows / 2 - 5
|
188
|
-
draw_rectangle(10, top_y, @cols - 20, 10)
|
189
|
-
goto(@cols / 2 - (header.length / 2) - 1, top_y)
|
190
|
-
print ' ', header, ' '
|
191
|
-
goto(11, top_y + 1)
|
192
|
-
puts msg
|
193
|
-
draw_button(@cols / 2 - 10, top_y + 8, 10, 'OK')
|
194
|
-
loop {
|
195
|
-
c = read_char_mapped
|
196
|
-
return if c == :enter
|
197
|
-
}
|
198
|
-
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
|
199
60
|
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
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
|
205
82
|
|
206
|
-
|
207
|
-
|
208
|
-
|
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
|
209
88
|
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
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
|
228
115
|
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
end
|
116
|
+
def bg_color=(col)
|
117
|
+
code = COLORS[col]
|
118
|
+
raise "Invalid color: #{col}" unless code
|
233
119
|
|
234
|
-
|
235
|
-
|
236
|
-
|
120
|
+
@bg_color = code
|
121
|
+
update_colors
|
122
|
+
end
|
237
123
|
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
124
|
+
def reset_colors
|
125
|
+
@fg_color = 7
|
126
|
+
@bg_color = 0
|
127
|
+
update_colors
|
128
|
+
end
|
242
129
|
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
end
|
251
|
-
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
|
252
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
|
253
198
|
end
|