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