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.
@@ -1,253 +1,198 @@
1
- # coding: utf-8
2
- require 'Win32API'
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
- class ConsoleWindows
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
- def puts(s)
54
- Kernel::puts s
55
- # num_written = 'XXXX'
56
- # reserved = 'XXXX'
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
- # Put the cursor up to screen position (x, y). First line is 0,
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
- COLORS = {
89
- :black => 0,
90
- :blue => 1,
91
- :green => 2,
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
- def bg_color=(col)
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
- def reset_colors
122
- @fg_color = 7
123
- @bg_color = 0
124
- update_colors
125
- 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
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
- ZERO_ESCAPE = 0.chr
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
- def read_char_mapped
140
- c = read_char
141
- c2 = KEY_MAP[c]
142
- c2 ? c2 : c
143
- end
46
+ def load_term_size
47
+ csbi = 'X' * 22
144
48
 
145
- KEY_MAP = {
146
- "\b" => :backspace,
147
- "\t" => :tab,
148
- "\r" => :enter,
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
- SINGLE_CHARSET = '┌┐└┘─│'
176
- HEAVY_CHARSET = '┏┓┗┛━┃'
177
- DOUBLE_CHARSET = '╔╗╚╝═║'
178
-
179
- CHAR_TL = 0
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
- def input_str(header, msg)
201
- top_y = @rows / 2 - 5
202
- draw_rectangle(10, top_y, @cols - 20, 10)
203
- goto(@cols / 2 - (header.length / 2) - 1, top_y)
204
- print ' ', header, ' '
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
- goto(11, top_y + 1)
207
- Readline.readline('', false)
208
- end
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
- def draw_rectangle(x, y, w, h, charset = DOUBLE_CHARSET)
211
- goto(x, y)
212
- print charset[CHAR_TL]
213
- print charset[CHAR_H] * (w - 2)
214
- print charset[CHAR_TR]
215
-
216
- ((y + 1)..(y + h - 1)).each { |i|
217
- goto(x, i)
218
- print charset[CHAR_V]
219
- print ' ' * (w - 2)
220
- print charset[CHAR_V]
221
- }
222
-
223
- goto(x, y + h)
224
- print charset[CHAR_BL]
225
- print charset[CHAR_H] * (w - 2)
226
- print charset[CHAR_BR]
227
- end
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
- def draw_button(x, y, w, caption)
230
- goto(x, y)
231
- puts "[ #{caption} ]"
232
- end
116
+ def bg_color=(col)
117
+ code = COLORS[col]
118
+ raise "Invalid color: #{col}" unless code
233
119
 
234
- # Regexp borrowed from
235
- # http://stackoverflow.com/questions/170956/how-can-i-find-which-operating-system-my-ruby-program-is-running-on
236
- @@is_windows = (RUBY_PLATFORM =~ /cygwin|mswin|mingw|bccwin|wince|emx/) ? true : false
120
+ @bg_color = code
121
+ update_colors
122
+ end
237
123
 
238
- # Detects if current platform is Windows-based.
239
- def self.is_windows?
240
- @@is_windows
241
- end
124
+ def reset_colors
125
+ @fg_color = 7
126
+ @bg_color = 0
127
+ update_colors
128
+ end
242
129
 
243
- private
244
- def current_color_code
245
- (@bg_color << 4) | @fg_color
246
- end
247
-
248
- def update_colors
249
- SET_CONSOLE_TEXT_ATTRIBUTE.call(@stdout_handle, current_color_code)
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