Go crazy with colored hexdump
This commit is contained in:
parent
2d4dc6dc65
commit
c119d393fc
30
lib/bytes.rb
30
lib/bytes.rb
|
@ -16,7 +16,15 @@ def data_to_ascii(data)
|
|||
ascii
|
||||
end
|
||||
|
||||
def hexdump_lines(data, width = 2, notes = [], opts = {})
|
||||
COL_LEN = 9
|
||||
|
||||
# TODO: make this a gem?!
|
||||
#
|
||||
# opts
|
||||
# legend: :long
|
||||
# legend: :short
|
||||
# legend: :inline
|
||||
def hexdump_lines(data, width = 2, notes = [], opts = { legend: :long })
|
||||
byte_groups = data.unpack1('H*').scan(/../).groups_of(4)
|
||||
lines = []
|
||||
hex = ''
|
||||
|
@ -31,11 +39,12 @@ def hexdump_lines(data, width = 2, notes = [], opts = {})
|
|||
legend.push([color, info.last.send(color)])
|
||||
end
|
||||
unless legend.empty?
|
||||
if opts[:long_legend]
|
||||
case opts[:legend]
|
||||
when :long
|
||||
legend.each do |leg|
|
||||
lines.push("#{leg.first}: #{leg.last}".send(leg.first))
|
||||
end
|
||||
else
|
||||
when :short
|
||||
lines.push(legend.map(&:last).join(' '))
|
||||
end
|
||||
end
|
||||
|
@ -43,6 +52,8 @@ def hexdump_lines(data, width = 2, notes = [], opts = {})
|
|||
hex += ' ' unless hex.empty?
|
||||
ascii += data_to_ascii(str_bytes(byte_group.join).pack('C*'))
|
||||
w += 1
|
||||
note = ''
|
||||
colors = 0
|
||||
notes.each do |info|
|
||||
color = info.first
|
||||
# p color
|
||||
|
@ -59,6 +70,8 @@ def hexdump_lines(data, width = 2, notes = [], opts = {})
|
|||
next
|
||||
end
|
||||
|
||||
note += " #{info[3]}".send(color) if opts[:legend] == :inline
|
||||
|
||||
from -= byte
|
||||
to -= byte
|
||||
from = 0 if from.negative?
|
||||
|
@ -69,6 +82,7 @@ def hexdump_lines(data, width = 2, notes = [], opts = {})
|
|||
next if byte_group[i].nil?
|
||||
|
||||
byte_group[i] = byte_group[i].send(color)
|
||||
colors += 1
|
||||
end
|
||||
end
|
||||
byte += 4
|
||||
|
@ -76,11 +90,13 @@ def hexdump_lines(data, width = 2, notes = [], opts = {})
|
|||
next unless w >= width
|
||||
|
||||
w = 0
|
||||
lines.push("#{hex} #{ascii}")
|
||||
hex_pad = hex.ljust((width * 4 * 3) + (colors * COL_LEN), ' ')
|
||||
ascii_pad = ascii.ljust(width * 4)
|
||||
lines.push("#{hex_pad} #{ascii_pad}#{note}")
|
||||
hex = ''
|
||||
ascii = ''
|
||||
end
|
||||
lines.push("#{hex} #{ascii}") unless hex.empty?
|
||||
lines.push("#{hex_pad} #{ascii_pad}#{note}") unless hex.empty?
|
||||
lines
|
||||
end
|
||||
|
||||
|
@ -112,12 +128,12 @@ def todo_make_this_a_unit_test
|
|||
puts l
|
||||
end
|
||||
|
||||
hexdump_lines("\x01\x41\x02\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\xef", 40, notes, long_legend: true).each do |l|
|
||||
hexdump_lines("\x01\x41\x02\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\xef", 40, notes, legend: long).each do |l|
|
||||
puts l
|
||||
end
|
||||
|
||||
# should not crash when annotating bytes out of range
|
||||
hexdump_lines("\x01\x41", 40, notes, long_legend: false).each do |l|
|
||||
hexdump_lines("\x01\x41", 40, notes, legend: :long).each do |l|
|
||||
puts l
|
||||
end
|
||||
end
|
||||
|
|
|
@ -178,13 +178,27 @@ class GameClient
|
|||
part_size = u.get_int
|
||||
end
|
||||
|
||||
data = u.get_raw
|
||||
puts "snap id=#{msg_id} game_tick=#{game_tick} delta_tick=#{delta_tick}"
|
||||
puts " num_parts=#{num_parts} part=#{part} crc=#{crc} part_size=#{part_size}"
|
||||
|
||||
header = []
|
||||
notes = []
|
||||
u.parsed.each_with_index do |parsed, index|
|
||||
header += parsed[:raw]
|
||||
color = (index % 2).zero? ? :green : :pink
|
||||
notes.push([color, parsed[:pos], parsed[:len], "#{parsed[:type]} #{parsed[:value]}"])
|
||||
parsed[:value]
|
||||
end
|
||||
|
||||
hexdump_lines(header.pack('C*'), 1, notes, legend: :inline).each do |hex|
|
||||
puts hex
|
||||
end
|
||||
|
||||
data = u.get_raw
|
||||
notes = [
|
||||
[:green, 0, 4, 'who dis?']
|
||||
]
|
||||
hexdump_lines(data.pack('C*'), 1, notes, long_legend: true).each do |hex|
|
||||
hexdump_lines(data.pack('C*'), 1, notes, legend: :long).each do |hex|
|
||||
puts hex
|
||||
end
|
||||
|
||||
|
|
|
@ -72,8 +72,14 @@ class Packer
|
|||
end
|
||||
|
||||
class Unpacker
|
||||
attr_reader :prev, :data, :parsed
|
||||
|
||||
def initialize(data)
|
||||
@data = data
|
||||
@prev = [] # all the data already unpacked
|
||||
@parsed = []
|
||||
@red_bytes = 0
|
||||
# { type: 'int', value: 1, raw: "\x01", len: 1, pos: 0 }
|
||||
if data.instance_of?(String)
|
||||
@data = data.unpack('C*')
|
||||
elsif data.instance_of?(Array)
|
||||
|
@ -103,11 +109,19 @@ class Unpacker
|
|||
return nil if @data.nil?
|
||||
|
||||
str = ''
|
||||
raw = []
|
||||
len = 0
|
||||
pos = @red_bytes
|
||||
@data.each_with_index do |byte, index|
|
||||
@prev.push(byte)
|
||||
raw.push(raw)
|
||||
@red_bytes += 1
|
||||
len += 1
|
||||
if byte.zero?
|
||||
@data = index == @data.length - 1 ? nil : @data[(index + 1)..]
|
||||
str = str_sanitize(str) unless (sanitize & SANITIZE).zero?
|
||||
str = str_sanitize_cc(str) unless (sanitize & SANITIZE_CC).zero?
|
||||
@parsed.push({ type: 'string', value: str, raw:, len:, pos: })
|
||||
return str
|
||||
end
|
||||
str += byte.chr
|
||||
|
@ -130,6 +144,7 @@ class Unpacker
|
|||
|
||||
sign = first[1]
|
||||
bits = []
|
||||
parsed = { type: 'int' }
|
||||
|
||||
# extended
|
||||
if first[0] == '1'
|
||||
|
@ -143,17 +158,34 @@ class Unpacker
|
|||
break if eigth_bits[0] == '0'
|
||||
end
|
||||
bits = bits.reverse
|
||||
@prev += @data[0...consumed]
|
||||
parsed[:raw] = @data[0...consumed]
|
||||
parsed[:len] = consumed
|
||||
parsed[:pos] = @red_bytes
|
||||
@red_bytes += consumed
|
||||
@data = @data[consumed..]
|
||||
else # single byte
|
||||
bits = [first[2..]]
|
||||
@prev.push(@data[0])
|
||||
parsed[:raw] = [@data[0]]
|
||||
parsed[:len] = 1
|
||||
parsed[:pos] = @red_bytes
|
||||
@red_bytes += 1
|
||||
@data = @data[1..]
|
||||
end
|
||||
num = bits.join.to_i(2)
|
||||
sign == '1' ? -(num + 1) : num
|
||||
parsed[:value] = sign == '1' ? -(num + 1) : num
|
||||
@parsed.push(parsed)
|
||||
parsed[:value]
|
||||
end
|
||||
|
||||
def get_raw(size = -1)
|
||||
len = size == -1 ? @data.size : size
|
||||
@prev += @data[...len]
|
||||
pos = @red_bytes
|
||||
@red_bytes += len
|
||||
@parsed.push({ type: 'raw', value: @data[...len], raw: @data[...len], len:, pos: })
|
||||
# TODO: error if size exceeds @data.size
|
||||
@data.shift(size == -1 ? @data.size : size)
|
||||
@data.shift(len)
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue