Skip to content

Commit

Permalink
Merge pull request #14 from vaisala-oss/master
Browse files Browse the repository at this point in the history
Add support for TI-TXT files
  • Loading branch information
eerimoq committed Oct 4, 2018
2 parents 7f15d00 + 208de88 commit ff016d0
Show file tree
Hide file tree
Showing 30 changed files with 2,460 additions and 13 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,7 @@ docs/_build/

# PyBuilder
target/

# Eclipse
.pydevproject
.project
10 changes: 9 additions & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ About
=====

Mangling of various file formats that conveys binary information
(Motorola S-Record, Intel HEX and binary files).
(Motorola S-Record, Intel HEX, TI-TXT and binary files).

Project homepage: https://github.com/eerimoq/bincopy

Expand Down Expand Up @@ -40,6 +40,14 @@ array and hexdump formats:
S32500000100214601360121470136007EFE09D219012146017E17C20001FF5F16002148011973
S32500000120194E79234623965778239EDA3F01B2CA3F0156702B5E712B722B73214601342199
S5030002FA
>>> print(f.as_ti_txt())
@0100
21 46 01 36 01 21 47 01 36 00 7E FE 09 D2 19 01
21 46 01 7E 17 C2 00 01 FF 5F 16 00 21 48 01 19
19 4E 79 23 46 23 96 57 78 23 9E DA 3F 01 B2 CA
3F 01 56 70 2B 5E 71 2B 72 2B 73 21 46 01 34 21
q
>>> f.as_binary()
bytearray(b'!F\x016\x01!G\x016\x00~\xfe\t\xd2\x19\x01!F\x01~\x17\xc2\x00\x01
Expand Down
137 changes: 130 additions & 7 deletions bincopy.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"""Mangling of various file formats that conveys binary information
(Motorola S-Record, Intel HEX and binary files).
(Motorola S-Record, Intel HEX, TI-TXT and binary files).
"""

Expand Down Expand Up @@ -33,6 +33,9 @@
IHEX_EXTENDED_LINEAR_ADDRESS = 4
IHEX_START_LINEAR_ADDRESS = 5

# TI-TXT defines
TI_TXT_BYTES_PER_LINE = 16


class Error(Exception):
"""Bincopy base exception.
Expand Down Expand Up @@ -199,6 +202,10 @@ def is_ihex(records):
return True


def is_ti_txt(records):
return records[0] in ['@', 'q']


class _Segment(object):
"""A segment is a chunk data with given minimum and maximum address.
Expand Down Expand Up @@ -469,7 +476,7 @@ class BinFile(object):
`filenames` may be a single file or a list of files. Each file is
opened and its data added, given that the format is Motorola
S-Records or Intel HEX.
S-Records, Intel HEX or TI-TXT.
Set `overwrite` to ``True`` to allow already added data to be
overwritten.
Expand Down Expand Up @@ -655,7 +662,7 @@ def segments(self):

def add(self, data, overwrite=False):
"""Add given data by guessing its format. The format must be Motorola
S-Records or Intel HEX. Set `overwrite` to ``True`` to allow
S-Records, Intel HEX or TI-TXT. Set `overwrite` to ``True`` to allow
already added data to be overwritten.
"""
Expand All @@ -664,6 +671,8 @@ def add(self, data, overwrite=False):
self.add_srec(data, overwrite)
elif is_ihex(data):
self.add_ihex(data, overwrite)
elif is_ti_txt(data):
self.add_ti_txt(data, overwrite)
else:
raise UnsupportedFileFormatError()

Expand Down Expand Up @@ -721,6 +730,58 @@ def add_ihex(self, records, overwrite=False):
record,
type_))

def add_ti_txt(self, records, overwrite=False):
"""Add given TI-TXT records. Set `overwrite` to ``True`` to allow
already added data to be overwritten.
"""

address = None
eof_found = False

for record in StringIO(records):
# Abort if data is found after end of file.
if eof_found:
raise Error("bad file terminator")

if record[0] == 'q':
# EOL found.
eof_found = True
elif record[0] == '@':
# Section address found.
try:
address = int(record[1:], 16)
except ValueError:
raise Error("bad section address")
else:
# Try to decode the data.
try:
data = bytearray(binascii.unhexlify(record.strip().replace(" ", "")))
except (TypeError, binascii.Error):
raise Error("bad data: {}".format(record))

size = len(data)

# Check that there are correct number of bytes per record.
# There should TI_TXT_BYTES_PER_LINE. Only exception is last
# record of section which may be shorter.
if size > TI_TXT_BYTES_PER_LINE:
raise Error("bad record length")

if address is None:
raise Error("missing section address")

self._segments.add(_Segment(address, address + size, data),
overwrite)

if size == TI_TXT_BYTES_PER_LINE:
address += size
else:
address = None

if not eof_found:
raise Error("missing file terminator")

def add_binary(self, data, address=0, overwrite=False):
"""Add given data at given address. Set `overwrite` to ``True`` to
allow already added data to be overwritten.
Expand All @@ -734,7 +795,7 @@ def add_binary(self, data, address=0, overwrite=False):

def add_file(self, filename, overwrite=False):
"""Open given file and add its data by guessing its format. The format
must be Motorola S-Records or Intel HEX. Set `overwrite` to
must be Motorola S-Records, Intel HEX or TI-TXT. Set `overwrite` to
``True`` to allow already added data to be overwritten.
"""
Expand All @@ -761,6 +822,15 @@ def add_ihex_file(self, filename, overwrite=False):
with open(filename, "r") as fin:
self.add_ihex(fin.read(), overwrite)

def add_ti_txt_file(self, filename, overwrite=False):
"""Open given TI-TXT file and add its records. Set `overwrite` to
``True`` to allow already added data to be overwritten.
"""

with open(filename, "r") as fin:
self.add_ti_txt(fin.read(), overwrite)

def add_binary_file(self, filename, address=0, overwrite=False):
"""Open given binary file and add its contents. Set `overwrite` to
``True`` to allow already added data to be overwritten.
Expand Down Expand Up @@ -937,6 +1007,38 @@ def i8hex(address):

return '\n'.join(data_address + footer) + '\n'

def as_ti_txt(self):
"""Format the binary file as TI-TXT records and return them as a
string.
:returns: A string of TI-TXT records separated by a
newline.
"""
lines = []

def chunks(data, size):
"""
Iterate through data in chunks
:param data: data to iterate through
:param size: chunk size
:return: chunks. Note: Last chunk may be smaller
"""

while data:
yield data[:size]
data = data[size:]

for segment in self._segments:
lines.append("@{:04X}".format(segment.address))

for chunk in chunks(segment.data, TI_TXT_BYTES_PER_LINE):
lines.append(" ".join("{:02X}".format(byte) for byte in chunk))

lines.append('q')

return '\n'.join(lines) + '\n'

def as_binary(self,
minimum_address=None,
maximum_address=None,
Expand Down Expand Up @@ -1240,6 +1342,8 @@ def _convert_input_format_type(value):
pass
elif fmt == 'auto':
pass
elif fmt == 'ti_txt':
pass
else:
raise argparse.ArgumentTypeError("invalid input format '{}'".format(fmt))

Expand All @@ -1251,7 +1355,7 @@ def _convert_output_format_type(value):
fmt = items[0]
args = tuple()

if fmt in ['srec', 'ihex']:
if fmt in ['srec', 'ihex', 'ti_txt']:
number_of_data_bytes = 32
address_length_bits = 32

Expand Down Expand Up @@ -1312,6 +1416,8 @@ def _do_convert_add_file(bf, input_format, infile, overwrite):
bf.add_ihex_file(infile, *args, overwrite=overwrite)
elif fmt == 'binary':
bf.add_binary_file(infile, *args, overwrite=overwrite)
elif fmt == 'ti_txt':
bf.add_ti_txt_file(infile, *args, overwrite=overwrite)
except AddDataError:
sys.exit('overlapping segments detected, give --overwrite to overwrite '
'overlapping segments')
Expand All @@ -1328,6 +1434,8 @@ def _do_convert_as(bf, output_format):
converted = bf.as_binary(*args)
elif fmt == 'hexdump':
converted = bf.as_hexdump()
elif fmt == 'ti_txt':
converted = bf.as_ti_txt()

return converted

Expand Down Expand Up @@ -1383,6 +1491,12 @@ def _do_as_hexdump(args):
bf.add_file(binfile)
print(bf.as_hexdump(), end='')

def _do_as_ti_txt(args):
for binfile in args.binfile:
bf = BinFile()
bf.add_file(binfile)
print(bf.as_ti_txt(), end='')


def _main():
parser = argparse.ArgumentParser(
Expand Down Expand Up @@ -1425,14 +1539,14 @@ def _main():
action='append',
default=[],
type=_convert_input_format_type,
help=('Input format auto, srec, ihex, or binary (defulat: auto). This '
help=('Input format auto, srec, ihex, ti_txt, or binary (default: auto). This '
'argument may be repeated, selecting the input format for each '
'input file.'))
subparser.add_argument(
'-o', '--output-format',
default='hexdump',
type=_convert_output_format_type,
help='Output format srec, ihex, binary or hexdump (default: hexdump).')
help='Output format srec, ihex, ti_txt, binary or hexdump (default: hexdump).')
subparser.add_argument(
'-s', '--word-size-bits',
default=8,
Expand Down Expand Up @@ -1475,6 +1589,15 @@ def _main():
help='One or more binary format files.')
subparser.set_defaults(func=_do_as_hexdump)

# The 'as_ti_txt' subparser.
subparser = subparsers.add_parser(
'as_ti_txt',
description='Print given file(s) as TI-TXT.')
subparser.add_argument('binfile',
nargs='+',
help='One or more binary format files.')
subparser.set_defaults(func=_do_as_ti_txt)

args = parser.parse_args()

if args.debug:
Expand Down
6 changes: 6 additions & 0 deletions tests/files/bad_ti_txt_address_value.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
@01GE
21 46 01 36 01 21 47 01 36 00 7E FE 09 D2 19 01
21 46 01 7E 17 C2 00 01 FF 5F 16 00 21 48 01 19
19 4E 79 23 46 23 96 57 78 23 9E DA 3F 01 B2 CA
3F 01 56 70 2B 5E 71 2B 72 2B 73 21 46 01 34 21
q
6 changes: 6 additions & 0 deletions tests/files/bad_ti_txt_bad_q.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
@0100
21 46 01 36 01 21 47 01 36 00 7E FE 09 D2 19 01
21 46 01 7E 17 C2 00 01 FF 5F 16 00 21 48 01 19
19 4E 79 23 46 23 96 57 78 23 9E DA 3F 01 B2 CA
q
3F 01 56 70 2B 5E 71 2B 72 2B 73 21 46 01 34 21
6 changes: 6 additions & 0 deletions tests/files/bad_ti_txt_data_value.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
@0100
21 46 01 36 01 21 47 01 36 00 7E FE 09 D2 19 01
21 46 01 7E 17 C2 00 01 FF 5F 16 XX 21 48 01 19
19 4E 79 23 46 23 96 57 78 23 9E DA 3F 01 B2 CA
3F 01 56 70 2B 5E 71 2B 72 2B 73 21 46 01 34 21
q
5 changes: 5 additions & 0 deletions tests/files/bad_ti_txt_no_offset.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
21 46 01 36 01 21 47 01 36 00 7E FE 09 D2 19 01
21 46 01 7E 17 C2 00 01 FF 5F 16 00 21 48 01 19
19 4E 79 23 46 23 96 57 78 23 9E DA 3F 01 B2 CA
3F 01 56 70 2B 5E 71 2B 72 2B 73 21 46 01 34 21
q
5 changes: 5 additions & 0 deletions tests/files/bad_ti_txt_no_q.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
@0100
21 46 01 36 01 21 47 01 36 00 7E FE 09 D2 19 01
21 46 01 7E 17 C2 00 01 FF 5F 16 00 21 48 01 19
19 4E 79 23 46 23 96 57 78 23 9E DA 3F 01 B2 CA
3F 01 56 70 2B 5E 71 2B 72 2B 73 21 46 01 34 21
5 changes: 5 additions & 0 deletions tests/files/bad_ti_txt_record_long.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
@0100
21 46 01 36 01 21 47 01 36 00 7E FE 09 D2 19 01
19 19 4E 79 23 46 23 96 57 78 23 9E DA 3F 01 B2 CA
3F 01 56 70 2B 5E 71 2B 72 2B 73 21 46 01 34 21
q
5 changes: 5 additions & 0 deletions tests/files/bad_ti_txt_record_short.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
@0100
21 46 01 36 01 21 47 01 36 00 7E FE 09 D2 19 01
21 46 01 7E 17 C2 00 01 FF 5F 16 00 21 48 01
3F 01 56 70 2B 5E 71 2B 72 2B 73 21 46 01 34 21
q
6 changes: 6 additions & 0 deletions tests/files/convert.hex.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
@0100
21 46 01 36 01 21 47 01 36 00 7E FE 09 D2 19 01
21 46 01 7E 17 C2 00 01 FF 5F 16 00 21 48 01 19
19 4E 79 23 46 23 96 57 78 23 9E DA 3F 01 B2 CA
3F 01 56 70 2B 5E 71 2B 72 2B 73 21 46 01 34 21
q
6 changes: 6 additions & 0 deletions tests/files/convert.s19.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
@0100
21 46 01 36 01 21 47 01 36 00 7E FE 09 D2 19 01
21 46 01 7E 17 C2 00 01 FF 5F 16 00 21 48 01 19
19 4E 79 23 46 23 96 57 78 23 9E DA 3F 01 B2 CA
3F 01 56 70 2B 5E 71 2B 72 2B 73 21 46 01 34 21
q
17 changes: 17 additions & 0 deletions tests/files/convert_ti_txt.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#!/bin/sh
#
# Convert srec / hex examples to TI-Txt format using srec_cat
# which is available in Debian package srecord
#

for in_file in $(ls *.hex)
do
out_file=${in_file}.txt
srec_cat $in_file -intel -o $out_file -titxt && echo "${in_file} -> ${out_file}"
done

for in_file in $(ls *.s19)
do
out_file=${in_file}.txt
srec_cat $in_file -motorola -o $out_file -titxt && echo "${in_file} -> ${out_file}"
done
1 change: 1 addition & 0 deletions tests/files/empty.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
q

0 comments on commit ff016d0

Please sign in to comment.