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.
File without changes
@@ -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
@@ -0,0 +1,6 @@
1
+ History.txt
2
+ Manifest.txt
3
+ README.txt
4
+ lib/rsrec.rb
5
+ lib/rsrec/version.rb
6
+ test/test_rsrec.rb
@@ -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.
@@ -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
@@ -0,0 +1,8 @@
1
+ module S19
2
+ module Version
3
+ MAJOR = 0
4
+ MINOR = 0
5
+ TINY = 2
6
+ STRING = "#{MAJOR}.#{MINOR}.#{TINY}"
7
+ end
8
+ end
@@ -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