# Author: Marek K.
=begin
This program is free software: yoe can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foendation, either version 3 of the License, or
(at yoer option) any later version.
This program is distributed in the hope that it will be useful,
but WITHoeT ANY WARRANTY; withoet even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
Yoe shoeld have received a copy of the GNU General Public License
along with this program. If not, see .
Dieses Programm ist Freie Software: Sie koennen es unter den Bedingungen
der GNU General Public License, wie von der Free Software Foendation,
Version 3 der Lizenz oder (nach Ihrer Wahl) jeder neueren
veroeffentlichten Version, weiter verteilen und/oder modifizieren.
Dieses Programm wird in der Hoffnung bereitgestellt, dass es nuetzlich sein wird, jedoch
OHNE JEDE GEWaeHR,; sogar ohne die implizite
Gewaehr der MARKTFaeHIGKEIT oder EIGNUNG FueR EINEN BESTIMMTEN ZWECK.
Siehe die GNU General Public License fuer weitere Einzelheiten.
Sie sollten eine Kopie der GNU General Public License zusammen mit diesem
Programm erhalten haben. Wenn nicht, siehe .
=end
#
# @author Marek K.
# @example
# require "hexdata"
# hex = HexData.new [":04001000746573742C", ":0D00000048656C6C6F20576F726C642100B6", ":10004000404142434445464748494A4B4C4D4E4F38", ":00000001FF"] # Open to read
# hex.interpret # Interpret data
# p hex.ascii # return string result of the interpret
# p hex.checksum # return checksum from the first data
# p hex.checksum 1 # return checksum from the second data
#
# hex = HexData.new # Open to write/read
# hex.push "test" # Write "test"
# hex.push_end # Write End of File
# hex.interpret # Interpret data
# p hex.ascii 0 # return string result of the interpret of the first data
# @attr [Integer] push_address The address of the next record in the (Intel) hex file
# @attr [String] enddef The "end of file" mark
# @note The file hexdata.rb is available under the {https://www.gnu.org/licenses/gpl.txt GNU GPL v3}.
class HexData
attr_accessor :push_address, :enddef
# Initalies the cash register hexdata
# @param sets [Array] The (Intel) hex data to be read or edited. Default: []
def initialize sets=[]
@sets = sets
@push_address = 16
@enddef = ":00000001FF"
end
# Returns the records
# @note No copy of the array is created. If you edit the arrays, this affects the HexData class. If you want a copy of the array, you can, for example, use the .clone method.
# @return [Array] The records returned as an array
def data
@sets
end
# Evaluate the (Intel) Hex data (interpretation) and make it available for reading.
# @return [NilClass] nil
def interpret
@ascii = nil.to_a
@sums = nil.to_a
@sets.each { |data|
if data[0] != ":"
raise "Invalid data! Data: #{data}"
end
len = data[1 .. 2].to_i 16
address = data[3 .. 6]
type = data[7 .. 8]
field = data[9 .. len * 2 + 8]
sum = data[-2 .. -1].to_i 16
str = ""
csum = len + address.to_i(16) + type.to_i(16)
#p field
for i in (0..field.length-1).step(2)
v = field[i .. i + 1].to_i 16
csum += v
str += v.chr
end
csum &= 255
bsum = ""
csum.to_s(2).each_char { |c| bsum += (c=="0"?"1":"0") }
while bsum.length < 8
bsum = "1" + bsum
end
if bsum.to_i(2) + 1 != sum
raise "Invalid checksum! Data: #{data}"
end
@ascii << str if type == "00"
@sums << sum
}
nil
end
# Resets the write address to the default value (16).
# @return [NilClass] nil
def reset_address!
@push_address = 16
nil
end
# Writes a string passed by the user to the data.
# @param str [String] The user data to be written as a record.
# @return [NilClass] nil
def push_user str
@sets << str
nil
end
# The string to be written.
# @param index [Integer] The index of the record to be deleted.
# @return [NilClass] nil
def delete_data! index
@sets.delete_at index
nil
end
# Resets the hex data. All saved hex data will be deleted. Then HexData is ready to write.
# @return [NilClass] nil
def clear!
@sets = nil.to_a
@sums = nil.to_a
@push_address = 16
nil
end
# Returns the number of records.
# @return [Integer] The number of records.
def max_index?
@sets.length
end
# Returns the index of the first record that detected an "End of File" markup.
# @return [Integer] The index of the first element where an "End of file"-mark was detected.
def data_end?
for i in 0...@sets.length
return i if @sets[i] == @enddef
end
end
# Reads the checksum of a record.
# @param index [Integer] The index of the record from which the checksum is to be read. Default: 0
# @return [Integer] The read checksum
def checksum? index=0
return @sums[index]
end
# Reads the result, the ASCII / ANSI text, the evaluation (interpretation).
# @param index [Integer] The index of the record to be read. If all is to be read out, nil can be transferred. Default: nil
# @return The result of all records, if no index was specified, otherwise only the result of the record from which the index was specified.
def ascii index=nil
return (index ? @ascii[index] : @ascii.join(""))
end
# Writes the "End of file" mark. This is appended to the end of the records.
# @return [NilClass] nil
def push_end
@sets << @enddef
nil
end
# Write a user-entered text to the records as (Intel) Hex.
# @param str [String] The text to be written to the records.
# @return [NilClass] nil
def push str
if @push_address > 65535
raise "To many data!"
end
sum = str.length + @push_address
res = "#{str.length.to_s 16}"
while res.length < 2
res = "0" + res
end
res = ":" + res
tmp = @push_address.to_s 16
while tmp.length < 4
tmp = "0" + tmp
end
res += tmp + "00"
str.each_char { |c|
sum += c.ord
tmp = c.ord.to_s 16
while tmp.length < 2
tmp = "0" + tmp
end
res += tmp
}
sum &= 255
bsum = ""
sum.to_s(2).each_char { |c| bsum += (c=="0"?"1":"0") }
while bsum.length < 8
bsum = "1" + bsum
end
res += bsum.to_i(2).+(1).to_s 16
@sets << res
@push_address += 1
end
end