rsrec 0.0.2
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.
- data/.gemtest +0 -0
- data/History.txt +10 -0
- data/Manifest.txt +6 -0
- data/README.txt +48 -0
- data/lib/rsrec.rb +154 -0
- data/lib/rsrec/version.rb +8 -0
- data/test/test_rsrec.rb +57 -0
- metadata +105 -0
data/.gemtest
ADDED
File without changes
|
data/History.txt
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
=== 0.0.2 / 2013-05-18
|
2
|
+
* Minor cleanup
|
3
|
+
* Updated gem dependencies
|
4
|
+
* Added coverage analysis in the tests
|
5
|
+
* Increased test coverage
|
6
|
+
* split the version in it's own file
|
7
|
+
|
8
|
+
=== 0.0.1 / 2012-06-19
|
9
|
+
* Initial release
|
10
|
+
* Parses SREC files and does all the checks
|
data/Manifest.txt
ADDED
data/README.txt
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
= rsrec
|
2
|
+
|
3
|
+
https://github.com/damphyr/rsrec
|
4
|
+
|
5
|
+
== DESCRIPTION:
|
6
|
+
|
7
|
+
Parser for the Motorola S-Record format
|
8
|
+
|
9
|
+
== REQUIREMENTS:
|
10
|
+
|
11
|
+
None, this is pure Ruby man!
|
12
|
+
|
13
|
+
== INSTALL:
|
14
|
+
|
15
|
+
* sudo gem install rsrec
|
16
|
+
|
17
|
+
== What the hell is SREC?
|
18
|
+
It's a hex text encoding for binary data. Really something you would use in embedded systems.
|
19
|
+
|
20
|
+
For more on the format check http://en.wikipedia.org/wiki/SREC_(file_format)
|
21
|
+
|
22
|
+
== Why?
|
23
|
+
Cause we need to parse these things and peek and poke at specific addresses before flashing. And it's a text format, which makes Ruby ideal.
|
24
|
+
|
25
|
+
== LICENSE:
|
26
|
+
|
27
|
+
(The MIT License)
|
28
|
+
|
29
|
+
Copyright (c) 2012 Vassilis Rizopoulos
|
30
|
+
|
31
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
32
|
+
a copy of this software and associated documentation files (the
|
33
|
+
'Software'), to deal in the Software without restriction, including
|
34
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
35
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
36
|
+
permit persons to whom the Software is furnished to do so, subject to
|
37
|
+
the following conditions:
|
38
|
+
|
39
|
+
The above copyright notice and this permission notice shall be
|
40
|
+
included in all copies or substantial portions of the Software.
|
41
|
+
|
42
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
43
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
44
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
45
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
46
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
47
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
48
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/lib/rsrec.rb
ADDED
@@ -0,0 +1,154 @@
|
|
1
|
+
require 'rsrec/version'
|
2
|
+
module S19
|
3
|
+
class SRecordError < RuntimeError
|
4
|
+
end
|
5
|
+
|
6
|
+
#Represents a line in the S-Record format
|
7
|
+
#
|
8
|
+
#http://en.wikipedia.org/wiki/SREC_%28file_format%29
|
9
|
+
class SRecord
|
10
|
+
attr_reader :binary,:data,:record_type,:byte_count,:address
|
11
|
+
def initialize rtype,addr,data
|
12
|
+
self.record_type= rtype
|
13
|
+
@address = addr
|
14
|
+
self.data=data
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_s
|
18
|
+
"S#{@record_type}#{"%02x"% @byte_count}#{format_address}#{@data}#{crc}".upcase
|
19
|
+
end
|
20
|
+
#Returns the S-Record CRC
|
21
|
+
def crc
|
22
|
+
SRecord.calculate_crc("#{"%02x"% @byte_count}#{format_address}#{@data}")
|
23
|
+
end
|
24
|
+
#Parses a single line in SREC format and returns an SRecord instance
|
25
|
+
def self.parse text_data
|
26
|
+
text_data.chomp!
|
27
|
+
#duplicate because we're slicing and dicing and throwing stuff away
|
28
|
+
line=text_data.dup
|
29
|
+
#the (0..0) is for 1.8.7 compatibility, in 1.9 it gives the sliced char back
|
30
|
+
if line.slice!(0..0)=='S'
|
31
|
+
record_type = line.slice!(0..0).to_i
|
32
|
+
#convert everything to hex
|
33
|
+
#take out the byte count
|
34
|
+
line.slice!(0..1).to_i(16)
|
35
|
+
address = calculate_address(record_type,line)
|
36
|
+
#take out the crc
|
37
|
+
line.slice!(-2..-1)
|
38
|
+
data = line
|
39
|
+
check_crc(text_data)
|
40
|
+
return SRecord.new(record_type,address,data)
|
41
|
+
else
|
42
|
+
raise SRecordError,"Line without leading S"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
#True if the record is of type 1,2 or 3
|
46
|
+
def data_record?
|
47
|
+
[1,2,3].include?(@record_type)
|
48
|
+
end
|
49
|
+
private
|
50
|
+
#Set the data.
|
51
|
+
#
|
52
|
+
#pld is just the payload in hex text format (2nhex digits pro byte)
|
53
|
+
def data= pld
|
54
|
+
@data=pld
|
55
|
+
@binary=SRecord.extract_data(pld)
|
56
|
+
#number of bytes in address+data+checksum (checksum is 1 byte)
|
57
|
+
@byte_count=format_address.size/2+@binary.size+1
|
58
|
+
end
|
59
|
+
#Raises SRecordError if supplied with an invalid record type
|
60
|
+
def record_type= rtype
|
61
|
+
case rtype
|
62
|
+
when 0,1,2,3,4,5,6,7,8,9
|
63
|
+
@record_type=rtype
|
64
|
+
else
|
65
|
+
raise SRecordError,"Invalid record type: '#{rtype}'. Should be a number"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
#String representation of the address left padded with 0s
|
69
|
+
def format_address
|
70
|
+
case @record_type
|
71
|
+
when 0,1,5,9
|
72
|
+
return "%04x"% @address
|
73
|
+
when 2,8
|
74
|
+
return "%06x"% @address
|
75
|
+
when 3,7
|
76
|
+
return "%08x"% @address
|
77
|
+
end
|
78
|
+
end
|
79
|
+
#Gets 2n chars and converts them to hex
|
80
|
+
#
|
81
|
+
#n depends on the record type
|
82
|
+
def self.calculate_address record_type,line
|
83
|
+
case record_type
|
84
|
+
when 0,1,5,9
|
85
|
+
#2 character pairs
|
86
|
+
address=line.slice!(0..3).to_i(16)
|
87
|
+
when 2,8
|
88
|
+
#3 character pairs
|
89
|
+
address=line.slice!(0..5).to_i(16)
|
90
|
+
when 3,7
|
91
|
+
#4 character pairs
|
92
|
+
address=line.slice!(0..7).to_i(16)
|
93
|
+
else
|
94
|
+
raise SRecordError,"Cannot calculate address. Unknown record type #{@record_type}"
|
95
|
+
end
|
96
|
+
return address
|
97
|
+
end
|
98
|
+
#From an SREC text line calculate the CRC and check it against the embedded CRC
|
99
|
+
def self.check_crc raw_data
|
100
|
+
line = raw_data.dup
|
101
|
+
#chop of the S, the record type and the crc
|
102
|
+
line.slice!(0..1)
|
103
|
+
crc=line.slice!(-2..-1)
|
104
|
+
calculated_crc=calculate_crc(line)
|
105
|
+
if calculated_crc.upcase == crc.upcase
|
106
|
+
return crc
|
107
|
+
else
|
108
|
+
raise SRecordError, "CRC failure: #{calculated_crc} instead of #{crc}"
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def self.calculate_crc line_data
|
113
|
+
data=extract_data(line_data)
|
114
|
+
data_sum=data.inject(0){|sum,item| sum + item}
|
115
|
+
#least significant byte is
|
116
|
+
lsb=data_sum & 0xFF
|
117
|
+
#1's complement of lsb and in hex string format
|
118
|
+
("%02x"% (lsb^0xFF)).upcase
|
119
|
+
end
|
120
|
+
#Converts 2n hex characters in n bytes
|
121
|
+
def self.extract_data data_string
|
122
|
+
line=data_string.dup
|
123
|
+
binary=[]
|
124
|
+
while !line.empty?
|
125
|
+
data_chars = line.slice!(0..1)
|
126
|
+
binary<<data_chars.to_i(16)
|
127
|
+
end
|
128
|
+
return binary
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
class MotFile
|
133
|
+
attr_reader :records
|
134
|
+
def initialize records=[]
|
135
|
+
@records=records
|
136
|
+
end
|
137
|
+
def to_s
|
138
|
+
@records.each_with_object(""){|record,msg| msg<<"#{record}\n"}
|
139
|
+
end
|
140
|
+
#Just the data records
|
141
|
+
def data_records
|
142
|
+
@records.select{|rec| rec.data_record?}.compact
|
143
|
+
end
|
144
|
+
#The total size of the image in bytes
|
145
|
+
def image_size
|
146
|
+
data_records.last.address+data_records.last.binary.size-data_records.first.address
|
147
|
+
end
|
148
|
+
#Parses a .mot file in memory, returns MotFile
|
149
|
+
def self.from_file filename
|
150
|
+
records=File.readlines(filename).map{|line| SRecord.parse(line)}
|
151
|
+
MotFile.new(records)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
data/test/test_rsrec.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
$:.unshift File.join(File.dirname(__FILE__),"..","lib")
|
2
|
+
require 'simplecov'
|
3
|
+
SimpleCov.start
|
4
|
+
require "test/unit"
|
5
|
+
require "rsrec"
|
6
|
+
|
7
|
+
class TestRsrec < Test::Unit::TestCase
|
8
|
+
def test_version
|
9
|
+
assert_equal(S19::Version::STRING, "#{S19::Version::MAJOR}.#{S19::Version::MINOR}.#{S19::Version::TINY}")
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_srecord
|
13
|
+
test_record="S00E00005065726675736F726D6F744B"
|
14
|
+
srec=nil
|
15
|
+
assert_nothing_raised(S19::SRecordError) { srec=S19::SRecord.parse(test_record) }
|
16
|
+
assert_not_nil(srec)
|
17
|
+
assert_equal(test_record, srec.to_s)
|
18
|
+
assert_equal(0, srec.record_type)
|
19
|
+
assert_equal(14, srec.byte_count)
|
20
|
+
assert_equal("4B", srec.crc)
|
21
|
+
assert(!srec.data_record?, "Not a data record")
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_srecord_crc
|
25
|
+
test_record="S00E00005065726675736F726D6F744A"
|
26
|
+
srec=nil
|
27
|
+
assert_raise(S19::SRecordError) { srec=S19::SRecord.parse(test_record) }
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_trimming
|
31
|
+
test_record="S00E00005065726675736F726D6F744B\n"
|
32
|
+
assert_nothing_raised(S19::SRecordError) { S19::SRecord.parse(test_record) }
|
33
|
+
test_record="S00E00005065726675736F726D6F744B\r"
|
34
|
+
assert_nothing_raised(S19::SRecordError) { S19::SRecord.parse(test_record) }
|
35
|
+
test_record="S00E00005065726675736F726D6F744B\r\n"
|
36
|
+
assert_nothing_raised(S19::SRecordError) { S19::SRecord.parse(test_record) }
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_format_checks
|
40
|
+
test_record="BF00726D6F744A"
|
41
|
+
assert_raise(S19::SRecordError) { S19::SRecord.parse(test_record) }
|
42
|
+
assert_raise(S19::SRecordError) { S19::SRecord.new(15,0x0405,"FOO") }
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_data_records
|
46
|
+
test_record="S11F001C4BFFFFE5398000007D83637880010014382100107C0803A64E800020E9"
|
47
|
+
srec=nil
|
48
|
+
assert_nothing_raised(S19::SRecordError) { srec=S19::SRecord.parse(test_record) }
|
49
|
+
assert(srec.data_record?, "A data record")
|
50
|
+
test_record="S5030003F9"
|
51
|
+
assert_nothing_raised(S19::SRecordError) { srec=S19::SRecord.parse(test_record) }
|
52
|
+
assert(!srec.data_record?, "Not a data record")
|
53
|
+
test_record="S9030000FC"
|
54
|
+
assert_nothing_raised(S19::SRecordError) { srec=S19::SRecord.parse(test_record) }
|
55
|
+
assert(!srec.data_record?, "Not a data record")
|
56
|
+
end
|
57
|
+
end
|
metadata
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rsrec
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Vassilis Rizopoulos
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-05-18 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rdoc
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '4.0'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '4.0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: hoe
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ~>
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '3.6'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '3.6'
|
46
|
+
description: ! 'https://github.com/damphyr/rsrec
|
47
|
+
|
48
|
+
|
49
|
+
== DESCRIPTION:
|
50
|
+
|
51
|
+
|
52
|
+
Parser for the Motorola S-Record format
|
53
|
+
|
54
|
+
|
55
|
+
== REQUIREMENTS:
|
56
|
+
|
57
|
+
|
58
|
+
None, this is pure Ruby man!'
|
59
|
+
email:
|
60
|
+
- vassilisrizopoulos@gmail.com
|
61
|
+
executables: []
|
62
|
+
extensions: []
|
63
|
+
extra_rdoc_files:
|
64
|
+
- History.txt
|
65
|
+
- Manifest.txt
|
66
|
+
- README.txt
|
67
|
+
files:
|
68
|
+
- History.txt
|
69
|
+
- Manifest.txt
|
70
|
+
- README.txt
|
71
|
+
- lib/rsrec.rb
|
72
|
+
- lib/rsrec/version.rb
|
73
|
+
- test/test_rsrec.rb
|
74
|
+
- .gemtest
|
75
|
+
homepage: http://github.com/damphyr/rsrec
|
76
|
+
licenses: []
|
77
|
+
post_install_message:
|
78
|
+
rdoc_options:
|
79
|
+
- --main
|
80
|
+
- README.txt
|
81
|
+
require_paths:
|
82
|
+
- lib
|
83
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
84
|
+
none: false
|
85
|
+
requirements:
|
86
|
+
- - ! '>='
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '0'
|
89
|
+
segments:
|
90
|
+
- 0
|
91
|
+
hash: -639295409136969064
|
92
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
93
|
+
none: false
|
94
|
+
requirements:
|
95
|
+
- - ! '>='
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
version: '0'
|
98
|
+
requirements: []
|
99
|
+
rubyforge_project: rsrec
|
100
|
+
rubygems_version: 1.8.25
|
101
|
+
signing_key:
|
102
|
+
specification_version: 3
|
103
|
+
summary: rsrec provides classes for parsing Motorola's S-Record format
|
104
|
+
test_files:
|
105
|
+
- test/test_rsrec.rb
|