package_tool.py 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. '''
  2. Author: your name
  3. Date: 2021-07-15 10:05:16
  4. LastEditTime: 2025-09-25 08:00:00
  5. LastEditors: wdfk_prog
  6. Description: Packages a binary patch file into an RBL package, using the new firmware file for header metadata.
  7. FilePath: /pkg/package_tool.py
  8. '''
  9. import os
  10. import struct
  11. import zlib
  12. import sys
  13. # --- C语言宏定义对应的Python常量 ---
  14. # 加密算法
  15. QBOOT_ALGO_CRYPT_NONE = 0
  16. # 压缩算法 (用于告知Bootloader包体类型是差分补丁)
  17. QBOOT_ALGO_CMPRS_HPATCHLITE = (4 << 8)
  18. # 校验算法
  19. QBOOT_ALGO2_VERIFY_CRC = 1
  20. def crc32(bytes_obj):
  21. """计算字节对象的CRC32校验和"""
  22. return zlib.crc32(bytes_obj) & 0xFFFFFFFF
  23. def create_firmware_header(new_fw_obj, patch_obj, algo, algo2, timestamp, part_name_str, fw_ver_str, prod_code_str):
  24. """
  25. 根据 fw_info_t 结构创建固件头部
  26. :param new_fw_obj: 新版本固件的完整数据 (用于 raw_size 和 raw_crc)
  27. :param patch_obj: 补丁文件的数据 (包的主体, 用于 pkg_size 和 pkg_crc)
  28. """
  29. # u8 type[4];
  30. type_name = b'RBL\x00'
  31. type_4 = struct.pack('4s', type_name)
  32. # u16 algo; u16 algo2;
  33. algo_pack = struct.pack('<H', algo)
  34. algo2_pack = struct.pack('<H', algo2)
  35. # u32 time_stamp;
  36. time_stamp_pack = struct.pack('<I', int(timestamp))
  37. # u8 part_name[16];
  38. part_name = struct.pack('16s', part_name_str.encode('utf-8'))
  39. # u8 fw_ver[24];
  40. fw_ver = struct.pack('24s', fw_ver_str.encode('utf-8'))
  41. # u8 prod_code[24];
  42. prod_code = struct.pack('24s', prod_code_str.encode('utf-8'))
  43. # u32 pkg_crc; -> 使用补丁文件计算
  44. pkg_crc_pack = struct.pack('<I', crc32(patch_obj))
  45. # u32 raw_crc; -> 使用新版本文件计算
  46. raw_crc_pack = struct.pack('<I', crc32(new_fw_obj))
  47. # u32 raw_size; -> 使用新版本文件计算
  48. raw_size_pack = struct.pack('<I', len(new_fw_obj))
  49. # u32 pkg_size; -> 使用补丁文件计算
  50. pkg_size_pack = struct.pack('<I', len(patch_obj))
  51. # 组装除头部CRC外的所有字段
  52. header_no_crc = (type_4 + algo_pack + algo2_pack + time_stamp_pack +
  53. part_name + fw_ver + prod_code +
  54. pkg_crc_pack + raw_crc_pack + raw_size_pack + pkg_size_pack)
  55. # u32 hdr_crc;
  56. hdr_crc_pack = struct.pack('<I', crc32(header_no_crc))
  57. return header_no_crc + hdr_crc_pack
  58. def package_patch(patch_file, new_file, output_file):
  59. """为一个二进制补丁文件添加RBL头部"""
  60. print(f"--- Packaging Patch File: '{patch_file}' ---")
  61. # 1. 读取新版本文件 (new_fw_obj) 以获取 raw_size 和 raw_crc
  62. with open(new_file, "rb") as f:
  63. new_fw_obj = f.read()
  64. print(f"Read new file '{new_file}' for header info, size: {len(new_fw_obj)}")
  65. # 2. 读取补丁文件 (patch_obj),它将作为包的主体
  66. with open(patch_file, "rb") as f:
  67. patch_obj = f.read()
  68. print(f"Read patch file (package body) '{patch_file}', size: {len(patch_obj)}")
  69. # 3. 创建头部
  70. algo = QBOOT_ALGO_CMPRS_HPATCHLITE
  71. algo2 = QBOOT_ALGO_CRYPT_NONE
  72. timestamp = os.path.getmtime(patch_file)
  73. my_head = create_firmware_header(
  74. new_fw_obj=new_fw_obj,
  75. patch_obj=patch_obj,
  76. algo=algo,
  77. algo2=algo2,
  78. timestamp=timestamp,
  79. part_name_str='app',
  80. fw_ver_str='v1.00',
  81. prod_code_str='00010203040506070809'
  82. )
  83. print(f"Generated header size: {len(my_head)}")
  84. # 4. 写入最终文件 (头部 + 原始补丁数据)
  85. with open(output_file, "wb") as f:
  86. f.write(my_head)
  87. f.write(patch_obj)
  88. print(f"Successfully created RBL patch package: '{output_file}'")
  89. if __name__ == "__main__":
  90. # 检查命令行参数数量
  91. if len(sys.argv) < 3 or len(sys.argv) > 4:
  92. print(f"\nUsage: python {os.path.basename(sys.argv[0])} <patch_file> <new_file> [output_file]")
  93. print("\n <patch_file>: The binary patch data (e.g., from hdiffi).")
  94. print(" <new_file>: The new version file, used for header metadata (raw_size, raw_crc).")
  95. sys.exit(1)
  96. # 获取输入文件
  97. patch_file = sys.argv[1]
  98. new_file = sys.argv[2]
  99. if not os.path.exists(patch_file):
  100. print(f"Error: Patch file not found at '{patch_file}'")
  101. sys.exit(1)
  102. if not os.path.exists(new_file):
  103. print(f"Error: New file not found at '{new_file}'")
  104. sys.exit(1)
  105. # 决定输出文件
  106. if len(sys.argv) == 4:
  107. output_file = sys.argv[3]
  108. else:
  109. # 默认输出文件名 (e.g., light_patch.bin -> light_patch.rbl)
  110. base_name = os.path.splitext(patch_file)[0]
  111. output_file = f"{base_name}.rbl"
  112. # 执行打包
  113. package_patch(patch_file, new_file, output_file)