| #!/usr/bin/env python |
| |
| # pycrc -- parameterisable CRC calculation utility and C source code generator |
| # |
| # Copyright (c) 2006-2017 Thomas Pircher <tehpeh-web@tty1.net> |
| # |
| # Permission is hereby granted, free of charge, to any person obtaining a copy |
| # of this software and associated documentation files (the "Software"), to |
| # deal in the Software without restriction, including without limitation the |
| # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or |
| # sell copies of the Software, and to permit persons to whom the Software is |
| # furnished to do so, subject to the following conditions: |
| # |
| # The above copyright notice and this permission notice shall be included in |
| # all copies or substantial portions of the Software. |
| # |
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
| # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
| # IN THE SOFTWARE. |
| |
| |
| """ |
| pycrc is a fully parameterisable Cyclic Redundancy Check (CRC) calculation |
| utility and C source code generator written in Python. |
| |
| It can: |
| - generate the checksum of a string |
| - generate the checksum of a file |
| - generate the C header file and source of any of the algorithms below |
| |
| It supports the following CRC algorithms: |
| - bit-by-bit the basic algorithm which operates bit by bit on the |
| augmented message |
| - bit-by-bit-fast a variation of the simple bit-by-bit algorithm |
| - table-driven the standard table driven algorithm |
| """ |
| |
| from __future__ import print_function |
| from pycrc import progname, version, url |
| from pycrc.opt import Options |
| from pycrc.algorithms import Crc |
| import pycrc.codegen as cg |
| import binascii |
| import sys |
| |
| |
| |
| def print_parameters(opt): |
| """ |
| Generate a string with the options pretty-printed (used in the --verbose mode). |
| """ |
| return str(cg.ParamBlock(opt, '')) |
| |
| |
| def check_string(opt): |
| """ |
| Return the calculated CRC sum of a string. |
| """ |
| error = False |
| if opt.undefined_crc_parameters: |
| sys.stderr.write("{0:s}: error: undefined parameters\n".format(progname)) |
| sys.exit(1) |
| if opt.algorithm == 0: |
| opt.algorithm = opt.algo_bit_by_bit | opt.algo_bit_by_bit_fast | opt.algo_table_driven |
| |
| alg = Crc( |
| width=opt.width, poly=opt.poly, |
| reflect_in=opt.reflect_in, xor_in=opt.xor_in, |
| reflect_out=opt.reflect_out, xor_out=opt.xor_out, |
| table_idx_width=opt.tbl_idx_width) |
| |
| crc = None |
| if opt.algorithm & opt.algo_bit_by_bit: |
| bbb_crc = alg.bit_by_bit(opt.check_string) |
| if crc != None and bbb_crc != crc: |
| error = True |
| crc = bbb_crc |
| if opt.algorithm & opt.algo_bit_by_bit_fast: |
| bbf_crc = alg.bit_by_bit_fast(opt.check_string) |
| if crc != None and bbf_crc != crc: |
| error = True |
| crc = bbf_crc |
| if opt.algorithm & opt.algo_table_driven: |
| # no point making the python implementation slower by using less than 8 bits as index. |
| opt.tbl_idx_width = 8 |
| tbl_crc = alg.table_driven(opt.check_string) |
| if crc != None and tbl_crc != crc: |
| error = True |
| crc = tbl_crc |
| |
| if error: |
| sys.stderr.write("{0:s}: error: different checksums!\n".format(progname)) |
| if opt.algorithm & opt.algo_bit_by_bit: |
| sys.stderr.write(" bit-by-bit: {0:#x}\n".format(bbb_crc)) |
| if opt.algorithm & opt.algo_bit_by_bit_fast: |
| sys.stderr.write(" bit-by-bit-fast: {0:#x}\n".format(bbf_crc)) |
| if opt.algorithm & opt.algo_table_driven: |
| sys.stderr.write(" table_driven: {0:#x}\n".format(tbl_crc)) |
| sys.exit(1) |
| return crc |
| |
| |
| def check_hexstring(opt): |
| """ |
| Return the calculated CRC sum of a hex string. |
| """ |
| if opt.undefined_crc_parameters: |
| sys.stderr.write("{0:s}: error: undefined parameters\n".format(progname)) |
| sys.exit(1) |
| if len(opt.check_string) % 2 != 0: |
| opt.check_string = "0" + opt.check_string |
| if sys.version_info >= (3, 0): |
| opt.check_string = bytes(opt.check_string, 'utf_8') |
| try: |
| check_str = bytearray(binascii.unhexlify(opt.check_string)) |
| except TypeError: |
| sys.stderr.write( |
| "{0:s}: error: invalid hex string {1:s}\n".format(progname, opt.check_string)) |
| sys.exit(1) |
| |
| opt.check_string = check_str |
| return check_string(opt) |
| |
| |
| def crc_file_update(alg, register, check_bytes): |
| """ |
| Update the CRC using the bit-by-bit-fast CRC algorithm. |
| """ |
| # If the input data is a string, convert to bytes. |
| if isinstance(check_bytes, str): |
| check_bytes = bytearray(check_bytes, 'utf_8') |
| |
| for octet in check_bytes: |
| if alg.reflect_in: |
| octet = alg.reflect(octet, 8) |
| for j in range(8): |
| bit = register & alg.msb_mask |
| register <<= 1 |
| if octet & (0x80 >> j): |
| bit ^= alg.msb_mask |
| if bit: |
| register ^= alg.poly |
| register &= alg.mask |
| return register |
| |
| |
| def check_file(opt): |
| """ |
| Calculate the CRC of a file. |
| This algorithm uses the table_driven CRC algorithm. |
| """ |
| if opt.undefined_crc_parameters: |
| sys.stderr.write("{0:s}: error: undefined parameters\n".format(progname)) |
| sys.exit(1) |
| alg = Crc( |
| width=opt.width, poly=opt.poly, |
| reflect_in=opt.reflect_in, xor_in=opt.xor_in, |
| reflect_out=opt.reflect_out, xor_out=opt.xor_out, |
| table_idx_width=opt.tbl_idx_width) |
| |
| if not opt.reflect_in: |
| register = opt.xor_in |
| else: |
| register = alg.reflect(opt.xor_in, opt.width) |
| |
| try: |
| with open(opt.check_file, 'rb') as f: |
| check_bytes = bytearray(f.read(1024)) |
| while check_bytes != b"": |
| register = crc_file_update(alg, register, check_bytes) |
| check_bytes = bytearray(f.read(1024)) |
| except IOError: |
| sys.stderr.write( |
| "{0:s}: error: can't open file {1:s}\n".format(progname, opt.check_file)) |
| sys.exit(1) |
| |
| if opt.reflect_out: |
| register = alg.reflect(register, opt.width) |
| register = register ^ opt.xor_out |
| return register |
| |
| |
| def write_file(filename, out_str): |
| """ |
| Write the content of out_str to filename. |
| """ |
| try: |
| out_file = open(filename, "w") |
| out_file.write(out_str) |
| out_file.close() |
| except IOError: |
| sys.stderr.write("{0:s}: error: cannot write to file {1:s}\n".format(progname, filename)) |
| sys.exit(1) |
| |
| |
| def main(): |
| """ |
| Main function. |
| """ |
| opt = Options(progname, version, url) |
| opt.parse(sys.argv[1:]) |
| if opt.verbose: |
| print(print_parameters(opt)) |
| if opt.action == opt.action_check_str: |
| crc = check_string(opt) |
| print("{0:#x}".format(crc)) |
| if opt.action == opt.action_check_hex_str: |
| crc = check_hexstring(opt) |
| print("{0:#x}".format(crc)) |
| if opt.action == opt.action_check_file: |
| crc = check_file(opt) |
| print("{0:#x}".format(crc)) |
| if opt.action in set([ |
| opt.action_generate_h, opt.action_generate_c, opt.action_generate_c_main, |
| opt.action_generate_table]): |
| if opt.action == opt.action_generate_h: |
| in_str = "$h_template" |
| elif opt.action == opt.action_generate_c: |
| in_str = "$c_template" |
| elif opt.action == opt.action_generate_c_main: |
| in_str = "$c_template\n\n$main_template" |
| elif opt.action == opt.action_generate_table: |
| in_str = "$crc_table_init" |
| else: |
| sys.stderr.write( |
| "{0:s}: error: unknown action. Please file a bug report!\n".format(progname)) |
| sys.exit(1) |
| out = str(cg.File(opt, '')) |
| if opt.output_file == None: |
| print(out) |
| else: |
| write_file(opt.output_file, out) |
| return 0 |
| |
| |
| if __name__ == "__main__": |
| sys.exit(main()) |