143 lines
3.3 KiB
Ruby
143 lines
3.3 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require 'bin_utils'
|
|
|
|
module Mikunyan
|
|
# Class for manipulating binary string
|
|
# @attr [Symbol] endian endianness
|
|
class BinaryReader
|
|
attr_accessor :endian
|
|
|
|
# Constructor
|
|
# @param [IO,String] io binary String or IO
|
|
# @param [Symbol] endian endianness
|
|
def initialize(io, endian = :big)
|
|
@io = io.is_a?(String) ? StringIO.new(io, 'r') : io.dup
|
|
@io.binmode
|
|
@base_pos = @io.pos
|
|
@endian = endian
|
|
end
|
|
|
|
# Returns whether little endian or not
|
|
# @return [Boolean]
|
|
def little?
|
|
@endian == :little
|
|
end
|
|
|
|
# Tells current potision
|
|
# @return [Integer]
|
|
def pos
|
|
@io.pos - @base_pos
|
|
end
|
|
|
|
# Jumps to given position
|
|
# @param [Integer] jmp_pos position
|
|
def jmp(jmp_pos = 0)
|
|
@io.pos = jmp_pos + @base_pos
|
|
end
|
|
alias pos= jmp
|
|
|
|
# Advances position given size
|
|
# @param [Integer] size size
|
|
def adv(size = 0)
|
|
@io.seek(size, IO::SEEK_CUR)
|
|
end
|
|
|
|
# Rounds up position to multiple of given size
|
|
# @param [Integer] size size
|
|
def align(size)
|
|
rem = pos % size
|
|
adv(size - rem) if rem > 0
|
|
end
|
|
|
|
# Reads given size of binary string and seek
|
|
# @param [Integer] size size
|
|
# @return [String] data
|
|
def read(size)
|
|
ret = @io.read(size)
|
|
raise EOFError if ret.nil? || size && ret.bytesize < size
|
|
|
|
ret
|
|
end
|
|
|
|
# Reads given size of binary string from specified position. This method does not seek.
|
|
# @param [Integer] size size
|
|
# @param [Integer] jmp_pos position
|
|
# @return [String] data
|
|
def read_abs(size, jmp_pos)
|
|
orig_pos = pos
|
|
jmp(jmp_pos)
|
|
ret = read(size)
|
|
jmp(orig_pos)
|
|
ret
|
|
end
|
|
|
|
# Reads string until null character
|
|
# @return [String] string
|
|
def cstr
|
|
raise EOFError if @io.eof?
|
|
|
|
@io.each_byte.take_while(&:nonzero?).pack('C*')
|
|
end
|
|
|
|
# Reads an 8bit bool value
|
|
def bool
|
|
i8u != 0
|
|
end
|
|
|
|
# Reads an 8bit signed integer value
|
|
def i8s
|
|
BinUtils.get_sint8(read(1))
|
|
end
|
|
alias i8 i8s
|
|
|
|
# Reads an 8bit unsigned integer value
|
|
def i8u
|
|
@io.getbyte
|
|
end
|
|
|
|
# Reads a 16bit signed integer value
|
|
def i16s
|
|
little? ? BinUtils.get_sint16_le(read(2)) : BinUtils.get_sint16_be(read(2))
|
|
end
|
|
alias i16 i16s
|
|
|
|
# Reads a 16bit unsigned integer value
|
|
def i16u
|
|
little? ? BinUtils.get_int16_le(read(2)) : BinUtils.get_int16_be(read(2))
|
|
end
|
|
|
|
# Reads a 32bit signed integer value
|
|
def i32s
|
|
little? ? BinUtils.get_sint32_le(read(4)) : BinUtils.get_sint32_be(read(4))
|
|
end
|
|
alias i32 i32s
|
|
|
|
# Reads a 32bit unsigned integer value
|
|
def i32u
|
|
little? ? BinUtils.get_int32_le(read(4)) : BinUtils.get_int32_be(read(4))
|
|
end
|
|
|
|
# Reads a 64bit signed integer value
|
|
def i64s
|
|
little? ? BinUtils.get_sint64_le(read(8)) : BinUtils.get_sint64_be(read(8))
|
|
end
|
|
alias i64 i64s
|
|
|
|
# Reads a 64bit unsigned integer value
|
|
def i64u
|
|
little? ? BinUtils.get_int64_le(read(8)) : BinUtils.get_int64_be(read(8))
|
|
end
|
|
|
|
# Reads a 32bit floating point value
|
|
def float
|
|
little? ? read(4).unpack1('e') : read(4).unpack1('g')
|
|
end
|
|
|
|
# Reads a 64bit floating point value
|
|
def double
|
|
little? ? read(8).unpack1('E') : read(8).unpack1('G')
|
|
end
|
|
end
|
|
end
|