kaitai-struct-visualizer 0.4 → 0.5

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d14808a034e6e74e58395601dcfdeec2cf874d69
4
- data.tar.gz: 75e679c69ed1ee921fe0c9aa2448157bd19dc6e1
3
+ metadata.gz: '0294e63ea9c355016ef56c48aed683eec1204ee4'
4
+ data.tar.gz: 49acc6230547d1c10c1978d626cb498a29d2cd0f
5
5
  SHA512:
6
- metadata.gz: f49dccf89740812c59b12e8eb41067cbe5705ef7ec7e3a210d33087c4c8b2b0863c860db0a43ee38f9dd926ee67e3819ae401e2a4b14f17a8ed160b6699f1415
7
- data.tar.gz: 2224037570682309e1ce9db6b9245869f5382b71150705caf3bfd802c7a3727dff7aacfcb1cc196d225795bc60ad5214e07b06dc94169a3f41717c9c8c8e7414
6
+ metadata.gz: 7962d68bdafde103d6cf56408436ec79299e3fc25ed7b76dee888bde59d06e65bc71231732d4c9fe6ba5dfd3ebb5c727c2f394aa9c081af1123c69f39fb20380
7
+ data.tar.gz: 07ac40819e35d59ae48e3fcdba3f965df4288ec0c6fe45867f236ec3859f0f06ed62b5e1ea5e6e59fdae80c4a1a7d688fd2ff7658da81767e61fb1b389e41302
data/README.md CHANGED
@@ -18,9 +18,17 @@ for details on `.ksy` files and general usage patterns.
18
18
 
19
19
  ## Downloading and installing
20
20
 
21
- ### Generic
21
+ ### From Ruby Gems repository
22
22
 
23
- KS visualizer is written in Ruby and is available as .gem package.
23
+ KS visualizer is written in [Ruby](https://www.ruby-lang.org/) and is
24
+ available as
25
+ [.gem package](https://rubygems.org/gems/kaitai-struct-visualizer). Thus,
26
+ you'll need Ruby (RubyGems package manager comes bundled with Ruby
27
+ since v1.9) installed on your box, and then you can just run:
28
+
29
+ ```shell
30
+ gem install kaitai-struct-visualizer
31
+ ```
24
32
 
25
33
  ### Source code
26
34
 
@@ -0,0 +1,119 @@
1
+ # coding: utf-8
2
+ require 'io/console'
3
+ require 'readline'
4
+
5
+ module Kaitai
6
+
7
+ class ConsoleANSI
8
+ attr_reader :cols
9
+ attr_reader :rows
10
+
11
+ def initialize
12
+ # Normal POSIX way to determine console parameters
13
+ @cols = `tput cols`.to_i
14
+ @rows = `tput lines`.to_i
15
+
16
+ @seq_clear = `tput clear`
17
+ @seq_sgr0 = `tput sgr0`
18
+
19
+ @seq_fgcolor = []
20
+ @seq_bgcolor = []
21
+ end
22
+
23
+ def clear
24
+ print @seq_clear
25
+ end
26
+
27
+ ##
28
+ # Put the cursor up to screen position (x, y). First line is 0,
29
+ # first column is 0.
30
+ def goto(x, y)
31
+ #print `tput cup #{y} #{x}`
32
+ printf "\e[%d;%dH", y + 1, x + 1
33
+ end
34
+
35
+ COLORS = {
36
+ :black => 0,
37
+ :gray => 7,
38
+ :gray0 => 232,
39
+ :gray1 => 233,
40
+ :gray2 => 234,
41
+ :gray3 => 235,
42
+ :gray4 => 236,
43
+ :gray5 => 237,
44
+ :gray6 => 238,
45
+ :gray7 => 239,
46
+ :gray8 => 240,
47
+ :gray9 => 241,
48
+ :gray10 => 242,
49
+ :gray11 => 243,
50
+ :gray12 => 244,
51
+ :gray13 => 245,
52
+ :gray14 => 246,
53
+ :gray15 => 247,
54
+ :gray16 => 248,
55
+ :gray17 => 249,
56
+ :gray18 => 250,
57
+ :gray19 => 251,
58
+ :gray20 => 252,
59
+ :gray21 => 253,
60
+ :gray22 => 254,
61
+ :gray23 => 255,
62
+ }
63
+
64
+ def fg_color=(col)
65
+ #print @seq_fgcolor[col] ||= `tput setaf #{col}`
66
+ code = COLORS[col]
67
+ raise "Invalid color: #{col}" unless code
68
+ print "\e[38;5;#{code}m"
69
+ end
70
+
71
+ def bg_color=(col)
72
+ #print @seq_bgcolor[col] ||= `tput setab #{col}`
73
+ code = COLORS[col]
74
+ raise "Invalid color: #{col}" unless code
75
+ print "\e[48;5;#{code}m"
76
+ end
77
+
78
+ def reset_colors
79
+ print @seq_sgr0
80
+ end
81
+
82
+ # Reads keypresses from the user including 2 and 3 escape character sequences.
83
+ def read_char
84
+ $stdin.echo = false
85
+ $stdin.raw!
86
+
87
+ input = $stdin.getc.chr
88
+ if input == "\e" then
89
+ input << $stdin.read_nonblock(3) rescue nil
90
+ input << $stdin.read_nonblock(2) rescue nil
91
+ end
92
+ ensure
93
+ $stdin.echo = true
94
+ $stdin.cooked!
95
+
96
+ return input
97
+ end
98
+
99
+ def read_char_mapped
100
+ c = read_char
101
+ c2 = KEY_MAP[c]
102
+ c2 ? c2 : c
103
+ end
104
+
105
+ KEY_MAP = {
106
+ "\t" => :tab,
107
+ "\r" => :enter,
108
+ "\e[A" => :up_arrow,
109
+ "\e[B" => :down_arrow,
110
+ "\e[C" => :right_arrow,
111
+ "\e[D" => :left_arrow,
112
+ "\e[5~" => :pg_up,
113
+ "\e[6~" => :pg_dn,
114
+ "\e[H" => :home,
115
+ "\e[F" => :end,
116
+ }
117
+ end
118
+
119
+ end
@@ -0,0 +1,253 @@
1
+ # coding: utf-8
2
+ require 'Win32API'
3
+ require 'readline'
4
+
5
+ module Kaitai
6
+
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
52
+
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
79
+
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
87
+
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
113
+
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
120
+
121
+ def reset_colors
122
+ @fg_color = 7
123
+ @bg_color = 0
124
+ update_colors
125
+ end
126
+
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
135
+ end
136
+ return input
137
+ end
138
+
139
+ def read_char_mapped
140
+ c = read_char
141
+ c2 = KEY_MAP[c]
142
+ c2 ? c2 : c
143
+ end
144
+
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
174
+
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
199
+
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, ' '
205
+
206
+ goto(11, top_y + 1)
207
+ Readline.readline('', false)
208
+ end
209
+
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
228
+
229
+ def draw_button(x, y, w, caption)
230
+ goto(x, y)
231
+ puts "[ #{caption} ]"
232
+ end
233
+
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
237
+
238
+ # Detects if current platform is Windows-based.
239
+ def self.is_windows?
240
+ @@is_windows
241
+ end
242
+
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
252
+
253
+ end
@@ -200,17 +200,9 @@ class HexViewer
200
200
  }
201
201
  end
202
202
 
203
- HIGHLIGHT_COLOR = [
204
- :gray14,
205
- :gray11,
206
- :gray8,
207
- :gray5,
208
- :gray2,
209
- ]
210
-
211
203
  def highlight_show
212
204
  each_highlight_region { |i, p1, p2|
213
- @ui.bg_color = HIGHLIGHT_COLOR[i]
205
+ @ui.bg_color = @ui.highlight_colors[i]
214
206
  @ui.fg_color = :black
215
207
  highlight_draw(p1, p2)
216
208
  }
@@ -12,6 +12,7 @@ class Node
12
12
  attr_reader :pos2
13
13
  attr_reader :children
14
14
  attr_accessor :parent
15
+ attr_accessor :type
15
16
 
16
17
  def initialize(tree, value, level, value_method = nil, pos1 = nil, pos2 = nil)
17
18
  @tree = tree
@@ -41,6 +42,7 @@ class Node
41
42
  not (
42
43
  @value.is_a?(Fixnum) or
43
44
  @value.is_a?(Bignum) or
45
+ @value.is_a?(Float) or
44
46
  @value.is_a?(String) or
45
47
  @value.is_a?(Symbol) or
46
48
  @value === true or
@@ -59,7 +61,7 @@ class Node
59
61
  open
60
62
  end
61
63
  end
62
-
64
+
63
65
  def open
64
66
  return unless openable?
65
67
  explore
@@ -85,36 +87,40 @@ class Node
85
87
 
86
88
  pos = 2 * level + 4 + @id.length
87
89
 
88
- if @value.is_a?(Fixnum) or @value.is_a?(Bignum)
89
- print " = #{@value}"
90
- elsif @value.is_a?(Symbol)
91
- print " = #{@value}"
92
- elsif @value.is_a?(String)
93
- print ' = '
94
- pos += 3
95
- @str_mode = detect_str_mode unless @str_mode
96
- max_len = @tree.tree_width - pos
97
- case @str_mode
98
- when :str
99
- v = @value.encode('UTF-8')
100
- s = v[0, max_len]
101
- when :str_esc
102
- v = @value.encode('UTF-8')
103
- s = v.inspect[0, max_len]
104
- when :hex
105
- s = first_n_bytes_dump(@value, max_len / 3 + 1)
106
- else
107
- raise "Invalid str_mode: #{@str_mode.inspect}"
108
- end
109
- if s.length > max_len
110
- s = s[0, max_len - 1]
111
- s += '…'
90
+ if open? or not openable?
91
+ if @value.is_a?(Fixnum) or @value.is_a?(Bignum) or @value.is_a?(Float)
92
+ print " = #{@value}"
93
+ elsif @value.is_a?(Symbol)
94
+ print " = #{@value}"
95
+ elsif @value.is_a?(String)
96
+ print ' = '
97
+ pos += 3
98
+ @str_mode = detect_str_mode unless @str_mode
99
+ max_len = @tree.tree_width - pos
100
+ case @str_mode
101
+ when :str
102
+ v = @value.encode('UTF-8')
103
+ s = v[0, max_len]
104
+ when :str_esc
105
+ v = @value.encode('UTF-8')
106
+ s = v.inspect[0, max_len]
107
+ when :hex
108
+ s = first_n_bytes_dump(@value, max_len / 3 + 1)
109
+ else
110
+ raise "Invalid str_mode: #{@str_mode.inspect}"
111
+ end
112
+ if s.length > max_len
113
+ s = s[0, max_len - 1]
114
+ s += '…'
115
+ end
116
+ print s
117
+ elsif @value === true or @value === false
118
+ print " = #{@value}"
119
+ elsif @value.nil?
120
+ print " = null"
121
+ elsif @value.is_a?(Array)
122
+ printf ' (%d = 0x%x entries)', @value.size, @value.size
112
123
  end
113
- print s
114
- elsif @value === true or @value === false
115
- print " = #{@value}"
116
- elsif @value.is_a?(Array)
117
- printf ' (%d = 0x%x entries)', @value.size, @value.size
118
124
  end
119
125
 
120
126
  puts
@@ -162,8 +168,23 @@ class Node
162
168
  @value = @parent.value.send(@value_method)
163
169
  end
164
170
 
165
- if @value.is_a?(Fixnum) or @value.is_a?(Bignum) or @value.is_a?(String) or @value.is_a?(Symbol)
166
- # do nothing else
171
+ @explored = true
172
+
173
+ if @value.is_a?(Fixnum) or
174
+ @value.is_a?(Bignum) or
175
+ @value.is_a?(Float) or
176
+ @value.is_a?(String) or
177
+ @value == true or
178
+ @value == false or
179
+ @value.nil? or
180
+ @value.is_a?(Symbol)
181
+ clean_id = @id[0] == '@' ? @id[1..-1] : @id
182
+ debug_el = @parent.value._debug[clean_id]
183
+ #raise "Unable to get debugging aid for: #{@parent.value._debug.inspect} using ID '#{clean_id}'" unless debug_el
184
+ if debug_el
185
+ @pos1 = debug_el[:start]
186
+ @pos2 = debug_el[:end]
187
+ end
167
188
  elsif @value.is_a?(Array)
168
189
  clean_id = @id[0] == '@' ? @id[1..-1] : @id
169
190
  debug_el = @parent.value._debug[clean_id]
@@ -175,18 +196,16 @@ class Node
175
196
  fmt = "%#{max_val_digits}d"
176
197
 
177
198
  @value.each_with_index { |el, i|
178
- n = Node.new(@tree, el, level + 1, nil, aid[i][:start], aid[i][:end])
199
+ aid_el = aid[i] || {}
200
+ n = Node.new(@tree, el, level + 1, nil, aid_el[:start], aid_el[:end])
179
201
  n.id = sprintf(fmt, i)
180
202
  add(n)
181
203
  }
182
204
  else
183
205
  # Gather seq attributes
184
- attrs = Set.new
185
- @value.instance_variables.each { |k|
186
- k = k.to_s
187
- next if k =~ /^@_/
188
- el = @value.instance_eval(k)
189
- aid = @value._debug[k[1..-1]]
206
+ @value.class::SEQ_FIELDS.each { |k|
207
+ el = @value.instance_eval("@#{k}")
208
+ aid = @value._debug[k]
190
209
  if aid
191
210
  aid_s = aid[:start]
192
211
  aid_e = aid[:end]
@@ -195,12 +214,16 @@ class Node
195
214
  aid_s = nil
196
215
  aid_e = nil
197
216
  end
198
- n = Node.new(@tree, el, level + 1, nil, aid_s, aid_e)
199
- n.id = k
200
- add(n)
201
- attrs << k.gsub(/^@/, '')
217
+ unless el.nil?
218
+ n = Node.new(@tree, el, level + 1, nil, aid_s, aid_e)
219
+ n.id = k
220
+ n.type = :seq
221
+ add(n)
222
+ end
202
223
  }
203
224
 
225
+ attrs = Set.new(@value.class::SEQ_FIELDS)
226
+
204
227
  # Gather instances
205
228
  common_meths = Set.new
206
229
  @value.class.ancestors.each { |cl|
@@ -213,10 +236,10 @@ class Node
213
236
  next if k =~ /^_/ or attrs.include?(k)
214
237
  n = Node.new(@tree, nil, level + 1, meth)
215
238
  n.id = k
239
+ n.type = :instance
216
240
  add(n)
217
241
  }
218
242
  end
219
- @explored = true
220
243
  end
221
244
 
222
245
  ##
@@ -1,7 +1,7 @@
1
1
  module Kaitai
2
2
  module Struct
3
3
  module Visualizer
4
- VERSION = '0.4'
4
+ VERSION = '0.5'
5
5
  end
6
6
  end
7
7
  end
@@ -1,5 +1,6 @@
1
1
  require 'kaitai/struct/visualizer/version'
2
2
  require 'kaitai/struct/visualizer/visualizer'
3
+ require 'kaitai/tui'
3
4
 
4
5
  module Kaitai::Struct::Visualizer
5
6
 
@@ -7,13 +8,30 @@ class ExternalCompilerVisualizer < Visualizer
7
8
  def compile_format(fn)
8
9
  main_class_name = nil
9
10
  Dir.mktmpdir { |code_dir|
10
- system("ksc -- --debug -t ruby '#{fn}' -d '#{code_dir}'")
11
- exit $?.exitstatus if $?.exitstatus != 0
11
+ args = ['--debug', '-t', 'ruby', fn, '-d', code_dir]
12
+
13
+ # UNIX-based systems run ksc via a shell wrapper that requires
14
+ # extra '--' in invocation to disambiguate our '-d' from java runner
15
+ # '-d' (which allows to pass defines to JVM). Windows-based systems
16
+ # do not need and do not support this extra '--', so we don't add it
17
+ # on Windows.
18
+ args.unshift('--') unless Kaitai::TUI::is_windows?
19
+
20
+ system('kaitai-struct-compiler', *args)
21
+ if $?.exitstatus != 0
22
+ st = $?.exitstatus
23
+ $stderr.puts("ksv: unable to find and execute kaitai-struct-compiler in your PATH") if st == 127
24
+ exit st
25
+ end
26
+
27
+ puts "Compilation OK"
12
28
 
13
29
  compiled_path = Dir.glob("#{code_dir}/*.rb")[0]
14
30
 
15
31
  require compiled_path
16
32
 
33
+ puts "Class loaded OK"
34
+
17
35
  main_class_name = File.readlines(compiled_path).grep(/^class /)[0].strip.gsub(/^class /, '').gsub(/ <.*$/, '')
18
36
  }
19
37
 
@@ -1,118 +1,38 @@
1
1
  # coding: utf-8
2
- require 'io/console'
3
- require 'readline'
2
+ require 'forwardable'
4
3
 
5
4
  module Kaitai
6
5
 
7
6
  class TUI
8
- attr_reader :cols
9
- attr_reader :rows
7
+ extend Forwardable
8
+ def_delegators :@console, :rows, :cols, :goto, :clear, :fg_color=, :bg_color=, :reset_colors, :read_char_mapped
10
9
 
11
- def initialize
12
- @cols = `tput cols`.to_i
13
- @rows = `tput lines`.to_i
14
-
15
- @seq_clear = `tput clear`
16
- @seq_sgr0 = `tput sgr0`
17
- @seq_fgcolor = []
18
- @seq_bgcolor = []
19
- end
20
-
21
- def clear
22
- print @seq_clear
23
- end
24
-
25
- ##
26
- # Put the cursor up to screen position (x, y). First line is 0,
27
- # first column is 0.
28
- def goto(x, y)
29
- #print `tput cup #{y} #{x}`
30
- printf "\e[%d;%dH", y + 1, x + 1
31
- end
32
-
33
- COLORS = {
34
- :black => 0,
35
- :gray => 7,
36
- :gray0 => 232,
37
- :gray1 => 233,
38
- :gray2 => 234,
39
- :gray3 => 235,
40
- :gray4 => 236,
41
- :gray5 => 237,
42
- :gray6 => 238,
43
- :gray7 => 239,
44
- :gray8 => 240,
45
- :gray9 => 241,
46
- :gray10 => 242,
47
- :gray11 => 243,
48
- :gray12 => 244,
49
- :gray13 => 245,
50
- :gray14 => 246,
51
- :gray15 => 247,
52
- :gray16 => 248,
53
- :gray17 => 249,
54
- :gray18 => 250,
55
- :gray19 => 251,
56
- :gray20 => 252,
57
- :gray21 => 253,
58
- :gray22 => 254,
59
- :gray23 => 255,
60
- }
61
-
62
- def fg_color=(col)
63
- #print @seq_fgcolor[col] ||= `tput setaf #{col}`
64
- code = COLORS[col]
65
- raise "Invalid color: #{col}" unless code
66
- print "\e[38;5;#{code}m"
67
- end
10
+ attr_reader :highlight_colors
68
11
 
69
- def bg_color=(col)
70
- #print @seq_bgcolor[col] ||= `tput setab #{col}`
71
- code = COLORS[col]
72
- raise "Invalid color: #{col}" unless code
73
- print "\e[48;5;#{code}m"
74
- end
75
-
76
- def reset_colors
77
- print @seq_sgr0
78
- end
79
-
80
- # Reads keypresses from the user including 2 and 3 escape character sequences.
81
- def read_char
82
- $stdin.echo = false
83
- $stdin.raw!
84
-
85
- input = $stdin.getc.chr
86
- if input == "\e" then
87
- input << $stdin.read_nonblock(3) rescue nil
88
- input << $stdin.read_nonblock(2) rescue nil
12
+ def initialize
13
+ if TUI::is_windows?
14
+ require 'kaitai/console_windows'
15
+ @console = ConsoleWindows.new
16
+ @highlight_colors = [
17
+ :white,
18
+ :aqua,
19
+ :blue,
20
+ :green,
21
+ :white,
22
+ ]
23
+ else
24
+ require 'kaitai/console_ansi'
25
+ @console = ConsoleANSI.new
26
+ @highlight_colors = [
27
+ :gray14,
28
+ :gray11,
29
+ :gray8,
30
+ :gray5,
31
+ :gray2,
32
+ ]
89
33
  end
90
- ensure
91
- $stdin.echo = true
92
- $stdin.cooked!
93
-
94
- return input
95
- end
96
-
97
- def read_char_mapped
98
- c = read_char
99
- c2 = KEY_MAP[c]
100
- c2 ? c2 : c
101
34
  end
102
35
 
103
- KEY_MAP = {
104
- "\t" => :tab,
105
- "\r" => :enter,
106
- "\e[A" => :up_arrow,
107
- "\e[B" => :down_arrow,
108
- "\e[C" => :right_arrow,
109
- "\e[D" => :left_arrow,
110
- "\e[5~" => :pg_up,
111
- "\e[6~" => :pg_dn,
112
- "\e[H" => :home,
113
- "\e[F" => :end,
114
- }
115
-
116
36
  def message_box_exception(e)
117
37
  message_box("Error while parsing", e.message)
118
38
  end
@@ -129,23 +49,23 @@ class TUI
129
49
  CHAR_V = 5
130
50
 
131
51
  def message_box(header, msg)
132
- top_y = @rows / 2 - 5
133
- draw_rectangle(10, top_y, @cols - 20, 10)
134
- goto(@cols / 2 - (header.length / 2) - 1, top_y)
52
+ top_y = @console.rows / 2 - 5
53
+ draw_rectangle(10, top_y, @console.cols - 20, 10)
54
+ @console.goto(@console.cols / 2 - (header.length / 2) - 1, top_y)
135
55
  print ' ', header, ' '
136
- goto(11, top_y + 1)
56
+ @console.goto(11, top_y + 1)
137
57
  puts msg
138
- draw_button(@cols / 2 - 10, top_y + 8, 10, 'OK')
58
+ draw_button(@console.cols / 2 - 10, top_y + 8, 10, 'OK')
139
59
  loop {
140
- c = read_char_mapped
60
+ c = @console.read_char_mapped
141
61
  return if c == :enter
142
62
  }
143
63
  end
144
64
 
145
65
  def input_str(header, msg)
146
- top_y = @rows / 2 - 5
147
- draw_rectangle(10, top_y, @cols - 20, 10)
148
- goto(@cols / 2 - (header.length / 2) - 1, top_y)
66
+ top_y = @console.rows / 2 - 5
67
+ draw_rectangle(10, top_y, @console.cols - 20, 10)
68
+ goto(@console.cols / 2 - (header.length / 2) - 1, top_y)
149
69
  print ' ', header, ' '
150
70
 
151
71
  goto(11, top_y + 1)
@@ -175,6 +95,15 @@ class TUI
175
95
  goto(x, y)
176
96
  puts "[ #{caption} ]"
177
97
  end
98
+
99
+ # Regexp borrowed from
100
+ # http://stackoverflow.com/questions/170956/how-can-i-find-which-operating-system-my-ruby-program-is-running-on
101
+ @@is_windows = (RUBY_PLATFORM =~ /cygwin|mswin|mingw|bccwin|wince|emx/) ? true : false
102
+
103
+ # Detects if current platform is Windows-based.
104
+ def self.is_windows?
105
+ @@is_windows
106
+ end
178
107
  end
179
108
 
180
109
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kaitai-struct-visualizer
3
3
  version: !ruby/object:Gem::Version
4
- version: '0.4'
4
+ version: '0.5'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mikhail Yakshin
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-08-09 00:00:00.000000000 Z
11
+ date: 2016-11-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -68,6 +68,8 @@ files:
68
68
  - README.md
69
69
  - bin/ksv
70
70
  - kaitai-struct-visualizer.gemspec
71
+ - lib/kaitai/console_ansi.rb
72
+ - lib/kaitai/console_windows.rb
71
73
  - lib/kaitai/struct/visualizer.rb
72
74
  - lib/kaitai/struct/visualizer/hex_viewer.rb
73
75
  - lib/kaitai/struct/visualizer/node.rb