Optimizing Uniden bearmock srecutils

https://techcoderadio.blogspot.com/2025/04/bearmock-for-python-3.html

Some optimizations
 

# srecutils.py
#
# Copyright (C) 2011 Gabriel Tremblay - initnull hat gmail.com
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

"""
    Motorola S-Record utis
    - Kudos to Montreal CISSP Groupies
"""

from functools import lru_cache

# Address len in bytes for S* types
# http://www.amelek.gda.pl/avr/uisp/srecord.htm
__ADDR_LEN = {'S0' : 2,
              'S1' : 2,
              'S2' : 3,
              'S3' : 4,
              'S5' : 2,
              'S7' : 4,
              'S8' : 3,
              'S9' : 2}

@lru_cache(maxsize=384)

def int_to_padded_hex_byte(integer):
    """
        Convert an int to a 0-padded hex byte string
        example: 65 == 41, 10 == 0A
        Returns: The hex byte as string (ex: "0C")
    """
    return format(integer, '02X')


def compute_srec_checksum(srec):
    """
        Compute the checksum byte of a given S-Record
        Returns: The checksum as a string hex byte (ex: "0C")
    """
    # Get the summable data from srec (skip the 'S*' record type)
    data = srec[2:]

    # Sum all bytes (step every 2 characters to form a byte)
    total = sum(int(data[pos:pos+2], 16) for pos in range(0, len(data), 2))

    # Extract least significant byte and compute 8-bit one's complement
    least_significant_byte = total & 0xFF
    computed_checksum = (~least_significant_byte) & 0xFF

    return computed_checksum


def validate_srec_checksum(srec):
    """
        Validate if the checksum of the supplied s-record is valid
        Returns: True if valid, False if not
    """
    # Strip the original checksum and compare with the computed one
    return compute_srec_checksum(srec[:-2]) == int(srec[-2:], 16)


def get_readable_string(integer):
    r"""
        Convert an integer to a readable 2-character representation. This is useful for reversing
        examples: 41 == ".A", 13 == "\n", 20 (space) == "__"
        Returns a readable 2-char representation of an int.
    """
    special_chars = {9: "\\t", 10: "\\r", 13: "\\n", 32: '__'}
    
    if integer in special_chars:
        return special_chars[integer]
    elif 33 <= integer <= 126:  # Readable ascii
        return chr(integer) + '.'
    else:
        #return int_to_padded_hex_byte(integer)
        return format(integer, '02X')

def offset_byte_in_data(target_data, offset, target_byte_pos, readable=False, wraparound=False):
    """
        Offset a given byte in the provided data payload (kind of rot(x))
        readable will return a human-readable representation of the byte+offset
        wraparound will wrap around 255 to 0 (ex: 257 = 2)
        Returns: the offseted byte
    """
    byte_pos = target_byte_pos * 2
    prefix = target_data[:byte_pos]
    suffix = target_data[byte_pos+2:]
    int_value = int(target_data[byte_pos:byte_pos+2], 16) + offset

    # Wraparound
    if wraparound and int_value > 255:
        int_value -= 256

    # Get representation (readable or hex)
    if readable and 0 < int_value < 256:
        offset_byte = get_readable_string(int_value)
    else:
        offset_byte = format(int_value, '02X') #int_to_padded_hex_byte(int_value)

    #s = (prefix, offset_byte, suffix)
    return "".join((prefix, offset_byte, suffix))
    #return prefix + offset_byte + suffix


# offset can be from -255 to 255
def offset_data(data_section, offset, readable=False, wraparound=False):
    """
        Offset the whole data section.
        see offset_byte_in_data for more information
        Returns: the entire data section + offset on each byte
    """
    p = len(data_section) // 2
    for pos in range(p):
        data_section = offset_byte_in_data(data_section, offset, pos, readable, wraparound)
    return data_section


def parse_srec(srec):
    """
        Extract the data portion of a given S-Record (without checksum)
        Returns: the record type, the lenght of the data section, the write address, the data itself and the checksum
    """
    record_type = srec[:2]
    data_len = srec[2:4]
    addr_len = __ADDR_LEN.get(record_type) * 2
    addr_end = 4 + addr_len
    return record_type, data_len, srec[4:addr_end], srec[addr_end:-2], srec[-2:]
 

 

Comments

Popular posts from this blog

Telive-2 how-to

Modifying LILYGO T3 LoRa32 RTL_433 ESP OOK receiver example - log found devices to file

Modifying LILYGO T3 LoRa32 RTL_433 ESP OOK receiver example