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 +4 -4
- data/README.md +10 -2
- data/lib/kaitai/console_ansi.rb +119 -0
- data/lib/kaitai/console_windows.rb +253 -0
- data/lib/kaitai/struct/visualizer/hex_viewer.rb +1 -9
- data/lib/kaitai/struct/visualizer/node.rb +67 -44
- data/lib/kaitai/struct/visualizer/version.rb +1 -1
- data/lib/kaitai/struct/visualizer/visualizer_main.rb +20 -2
- data/lib/kaitai/tui.rb +43 -114
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '0294e63ea9c355016ef56c48aed683eec1204ee4'
|
4
|
+
data.tar.gz: 49acc6230547d1c10c1978d626cb498a29d2cd0f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
###
|
21
|
+
### From Ruby Gems repository
|
22
22
|
|
23
|
-
KS visualizer is written in Ruby and is
|
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 =
|
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
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
s
|
111
|
-
|
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
|
-
|
166
|
-
|
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
|
-
|
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
|
-
|
185
|
-
|
186
|
-
|
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
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
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,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
|
-
|
11
|
-
|
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
|
|
data/lib/kaitai/tui.rb
CHANGED
@@ -1,118 +1,38 @@
|
|
1
1
|
# coding: utf-8
|
2
|
-
require '
|
3
|
-
require 'readline'
|
2
|
+
require 'forwardable'
|
4
3
|
|
5
4
|
module Kaitai
|
6
5
|
|
7
6
|
class TUI
|
8
|
-
|
9
|
-
|
7
|
+
extend Forwardable
|
8
|
+
def_delegators :@console, :rows, :cols, :goto, :clear, :fg_color=, :bg_color=, :reset_colors, :read_char_mapped
|
10
9
|
|
11
|
-
|
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
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
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
|
+
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-
|
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
|