diff --git a/source/utl.py b/source/utl.py index a3c36e7..cd2386c 100644 --- a/source/utl.py +++ b/source/utl.py @@ -9,9 +9,22 @@ def trans_str_to_list(data: str) -> list: func_trans = lambda x: int(x, 16) return list(map(func_trans, data.strip().split(" "))) +def conv_int_to_array(num: int, big_end=False): + """ 数值转字节数组 """ + result = [0, 0, 0, 0] + if big_end: + indexlist = reversed(range(len(result))) + else: + indexlist = range(len(result)) + + for i in indexlist: + result[i] = int(num % 0x100) + num //= 0x100 + return result def display_hex(data, len) -> str: """ Hex字符表示 """ data %= 2 ** (4 * len) result = "0" * len + hex(data)[2:] - return "0x" + result[-len:].upper() \ No newline at end of file + return "0x" + result[-len:].upper() + diff --git a/source/utl_upgrade.py b/source/utl_upgrade.py new file mode 100644 index 0000000..32f3d1e --- /dev/null +++ b/source/utl_upgrade.py @@ -0,0 +1,222 @@ +# 升级包生成脚本 +import hashlib +from pathlib import Path + +from utl import conv_int_to_array + +header_tag = { + 'file_type': [0x00, 2], # 0 - 文件类型; 2byte + 'file_version': [0x01, 2], # 1 - 文件版本; 2byte + 'file_length': [0x02, 4], # 2 - 文件长度; 4byte + 'md5': [0x03, 16], # 3 - 文件MD5; 16byte + 'encrypt': [0x04, 1], # 4 - 加密算法; 1byte + 'update_type': [0x05, 1], # 5 - 升级文件类别; 1byte + 'update_spec': [0x06, 4], # 6 - 升级特征字; 4byte + 'update_verison': [0x07, 4], # 7 - 升级版本号; 4byte + 'update_date': [0x08, 3], # 8 - 升级版本日期; 3byte + 'area_code': [0x09, 4], # 9 - 省份特征; 4byte + 'uptate_str': [0x0A, -1, 64], # 10 - 升级段描述; less than 64byte + 'device_str': [0x0D, -1, 64], # 13 - 设备特征描述; less than 64byte + 'hex_name': [0xFF, -1, 80], # 255 - Hex文件名; less than 80byte +} + +def file_hex_to_bin(file_data, base_address, len_max=0xC000, conv_end=False): + """ + 将Intel Hex格式文件转换为Bin格式; + """ + result = bytearray(len_max).replace(b'\x00', b'\xff') + lines = file_data.split('\n') + offset = 0 + max_address = 0 + for line in lines: + if max_address >= len_max: + raise("Bin file Oversize.") + if line[0] == ':': + checksum = sum([int(x, 16) * (15 * (~i & 0x01) + 1) for i, x in enumerate(line[1:])]) + if (checksum & 0x00FF) != 0: + raise Exception('Hex file checksum error!') + + if line[7:9] == '00': + len = int(line[1:3], 16) + address = offset + int(line[3:7], 16) - base_address + address *= 2 + data = bytearray.fromhex(line[9:-2]) + if conv_end: + for i in range(0, len, 2): + t = data[i] + data[i] = data[i+1] + data[i+1] = t + result[address:address+len] = data + + if (address + len) > max_address: + max_address = address + len + elif line[7:9] == '01': + break + elif line[7:9] == '02': + offset = int(line[9:-2], 16) * 2**16 + elif line[7:9] == '03': + pass + elif line[7:9] == '04': # 拓展地址偏移 + offset = int(line[9:-2], 16) * 2**16 + elif line[7:9] == '05': + pass + return result[:max_address] + +def file_encryption(buffer): + """ 文件加密算法 """ + pwd_idx = 0 + pwd = b'moc.mmocspot.www' + pwd = list(map(lambda x: (x - 0x30 + 0x100) % 0x100, pwd)) + result = bytearray(len(buffer)) + for i in range(len(buffer)): + k = i + k |= i >> 8 + k |= i >> 16 + k |= i >> 24 + result[i] = buffer[i] ^ pwd[pwd_idx] ^ (k & 0xFF) + pwd_idx = (pwd_idx + 1) % len(pwd) + return result + + +def build_header(data_bin: bytearray, config: dict, len_max=512): + """ + 基于配置参数, 生成文件信息头; + """ + # 定义文件头 + m_file_header = bytearray(len_max) + m_file_header[0:8] = b"TOPSCOMM" + + header_len = 11 + tag_num = 0 + for tag, value in config.items(): + if tag in header_tag.keys(): + if header_tag[tag][1] == -1: + tag_len = min(len(value), header_tag[tag][2]) + else: + tag_len = header_tag[tag][1] + tag_date = [header_tag[tag][0], tag_len] + value[:tag_len] + m_file_header[header_len: header_len + tag_len + 2] = bytearray(tag_date) + tag_num += 1 + header_len += 2 + tag_len + m_file_header[8] = ((header_len - 10) % 0x100) + m_file_header[9] = ((header_len - 10) // 0x100) + m_file_header[10] = tag_num + m_file_header[header_len] = sum(m_file_header[:header_len]) % 0x100 + m_file_header[header_len+1] = sum(m_file_header[:header_len]) // 0x100 + + if header_len+2 > 64: + return None + else: + return m_file_header + +def build_header1(data_bin: bytearray, config: dict): + """ + 基于配置参数, 生成文件信息头; 基于新版本升级方案; + """ + # 定义文件头 + m_file_header = bytearray(64) + m_file_header[0:4] = bytearray(config["update_verison"]) + m_file_header[4:8] = bytearray(config["file_length"]) + m_file_header[8:24] = bytearray(config["md5"]) + + return m_file_header + + + + +def make_datafile(file_path: str, config: dict, header_len=512): + """ 升级文件生成函数 """ + file_path = Path(file_path) + file_data = file_path.read_text() + data_bin = file_hex_to_bin(file_data, config['flash_addr'], len_max=config['flash_size'] * 2) + md5_ctx = hashlib.md5() + md5_ctx.update(data_bin) + config['file_length'] = conv_int_to_array(len(data_bin)) + config["md5"] = list(md5_ctx.digest()) + if header_len > 256: + config['hex_name'] = list(file_path.name.encode())[:80] + + data_header = build_header(data_bin, config, header_len) + if data_header is None: + raise ("header tag oversize! ") + data_encrypt = file_encryption(data_bin) + return data_header, data_encrypt, data_bin + +def make_datafile1(file_path: str, config: dict, header_len=512): + """ 升级文件生成函数; 基于新版本方案 """ + file_path = Path(file_path) + file_data = file_path.read_text() + data_bin = file_hex_to_bin(file_data, config['flash_addr'], len_max=config['flash_size'] * 2) + md5_ctx = hashlib.md5() + md5_ctx.update(data_bin) + + config['file_length'] = conv_int_to_array(len(data_bin), True) + config["md5"] = list(md5_ctx.digest()) + if header_len > 256: + config['hex_name'] = list(file_path.name.encode())[:80] + + data_header = build_header1(data_bin, config) + data_encrypt = file_encryption(data_bin) + return data_header, data_encrypt, data_bin + +def test1(fp): + """ bin文件生成测试 """ + file1 = Path(fp) + path1 = file1.parent / (file1.stem + ".bin") + data1 = file1.read_text() + data2 = file_hex_to_bin(data1, 0x3F0100) + path1.write_bytes(data2) + pass + +def test2(): + """ md5校验, 加密测试 """ + data_bin = '123456 123456 '.encode() + md5_ctx = hashlib.md5() + md5_ctx.update(data_bin) + hash = md5_ctx.hexdigest() + buffer1 = data_bin[:] + buffer1_en = file_encryption(buffer1) + buffer2 = buffer1_en[:6] + buffer1[6:] + buffer2_en = file_encryption(buffer2) + pass + +def test3(): + """ 完整升级包生成测试 """ + config = { + # 'file_type': [0x43, 0x00], # 叠光DSP升级文件 + # 'file_version': [0x00, 0x00], # 文件版本-00 + # 'file_length': [], # 文件长度 + # 'md5': [], # 文件MD5 + 'encrypt': [0x01], # 默认加密算法 + 'update_type': [0x01], # APP升级 + # 'update_spec': [], # 升级特征字 + 'update_verison': [0x01, 0x00, 0x00, 0x00], # 升级版本号; 4byte + 'update_date': [0x23, 0x12, 0x16], # 升级版本日期; 3byte + # 'area_code': [], # 省份特征; 4byte + # 'uptate_str': [], # 升级段描述; less than 64byte + # 'device_str': [], # 设备特征描述; less than 64byte + # 'hex_name': [], # Hex文件名; less than 80byte + + # 文件结构信息 + 'flash_addr': 0x3E8020, # 程序文件起始地址 + 'flash_size': 0x005FC0, # 程序文件大小 + } + file1 = Path("D:\WorkingProject\LightStackController\lamina_pvdc\Source\PVDC20\Debug\LAMINA_PVDC.hex") + func_gen = [make_datafile, make_datafile1] + func_para = [{'header_len':64}, {}] + + count = 0 + for func, para in zip(func_gen, func_para): + header, data_e, data_b = func(file1, config, **para) + print(list(map(hex, header))) + print(list(map(hex, data_e))) + print(list(map(hex, data_b))) + file2 = file1.parent / (file1.stem + f'_v{count}.dat') + file2.write_bytes(header + data_b) + + +if __name__ == "__main__": + # test1() + # test2() + test3() + pass