kaitai-struct-visualizer 0.4 → 0.5

Sign up to get free protection for your applications and to get access to all the features.
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