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 +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
|