feat(cp02): add firmware processing tools

- Add extract_firmware.sh for extracting firmware components from merged bins
- Add merge_2323_firmware.sh for merging PA768 updater, firmware and IUM data
- Add split_and_merge.sh for splitting firmware and creating 40W firmware
- Add comprehensive README.md with usage documentation and examples
This commit is contained in:
Ching L 2025-12-12 18:03:39 +08:00
commit db8f25a518
4 changed files with 444 additions and 0 deletions

43
CP02/README.md Normal file
View File

@ -0,0 +1,43 @@
# CP02 固件处理工具
## 文件说明
### split_and_merge.sh
- **功能**: 切分固件文件并与 updater.bin 合并,用于制作 40W 固件
- **用法**: `./split_and_merge.sh <待切分文件> <updater.bin路径> <输出文件名>`
- **描述**:
- 将输入固件文件切分为前面部分和最后256字节
- 调用合并脚本生成最终固件
- 自动删除最后1字节并检查文件大小限制
### merge_2323_firmware.sh
- **功能**: 合并 PA768 updater、2323固件和IUM数据为单一固件文件
- **用法**: `./merge_2323_firmware.sh -u <updater> -f <firmware> -i <ium> -o <output>`
- **描述**:
- 创建基础0xFF填充文件
- 在指定偏移位置写入各组件
- 生成固件元数据
- 输出完整内存布局信息
### extract_firmware.sh
- **功能**: 从合并的 bin 文件中提取 firmware、IUM 和 metadata
- **用法**: `./extract_firmware.sh <merged_bin> [output_prefix]`
- **描述**:
- 从固定偏移位置提取 firmware (0x04000)
- 提取 IUM 数据 (0x1dc00, 256 bytes)
- 解析并生成 metadata.json (blocks 和 last_block_size)
- 如未指定前缀,自动使用原文件名
## 使用示例
```bash
# 切分并合并固件
./split_and_merge.sh /Users/ching/Library/Containers/com.tencent.xinWeChat/Data/Documents/xwechat_files/looching_5217/msg/attach/61f8147fd472041d44f609e3618e827c/2025-12/Rec/ff6a18f4ab8db2a9/F/3/SW2303P_B_V1.0_00_D1F6_UFCS.bin ~/Downloads/固件/PA503_Updater.bin 40_ufcs2.bin
# 直接合并固件组件
./merge_2323_firmware.sh -u PA768_Updater.bin -f 2323_firmware.bin -i 2323_ium.bin -o merged.bin
# 提取固件组件
./extract_firmware.sh 40_ufcs2.bin firmware # 指定输出前缀
./extract_firmware.sh merged.bin # 使用原文件名作为前缀
```

129
CP02/extract_firmware.sh Executable file
View File

@ -0,0 +1,129 @@
#!/bin/bash
# 用法: ./extract_firmware.sh <merged_bin> [output_prefix]
# 从合并的 bin 文件中提取 head file (firmware), tail file (IUM), 以及生成 metadata.json
#
# 文件布局:
# 0x04000: firmware (head file)
# 0x1dc00: IUM (tail file, 256 bytes)
# 0x1dd00: metadata (blocks: uint16, last_block_size: uint8)
INPUT="$1"
# 如果没有传 PREFIX使用原文件名去掉扩展名
if [ -n "$2" ]; then
PREFIX="$2"
else
# 获取文件名(不含路径),然后去掉扩展名
BASENAME=$(basename "$INPUT")
PREFIX="${BASENAME%.*}"
fi
if [ -z "$INPUT" ]; then
echo "用法: $0 <merged_bin> [output_prefix]"
echo "示例: $0 40_ufcs2.bin firmware"
echo " $0 40_ufcs2.bin # 使用原文件名作为前缀"
echo ""
echo "输出文件:"
echo " {prefix}.firmware - firmware 部分"
echo " {prefix}.ium - IUM 部分 (256 bytes)"
echo " {prefix}_metadata.json - 元数据"
echo ""
echo "注: 如果不指定 output_prefix将使用原文件名去掉扩展名"
exit 1
fi
if [ ! -f "$INPUT" ]; then
echo "错误: 文件不存在: $INPUT"
exit 1
fi
# 常量定义
FIRMWARE_OFFSET=$((0x4000)) # 16384
IUM_OFFSET=$((0x1dc00)) # 121856
METADATA_OFFSET=$((0x1dd00)) # 122112
IUM_SIZE=256
# 获取文件大小
if stat --version >/dev/null 2>&1; then
FILE_SIZE=$(stat -c%s "$INPUT") # Linux
else
FILE_SIZE=$(stat -f%z "$INPUT") # macOS
fi
echo "输入文件: $INPUT"
echo "文件大小: $FILE_SIZE bytes (0x$(printf '%x' $FILE_SIZE))"
echo ""
# 读取 metadata (位于 0x1dd00)
# 结构: uint16_t blocks (little-endian), uint8_t last_block_size
echo "读取 metadata (offset 0x1dd00)..."
METADATA=$(dd if="$INPUT" bs=1 skip=$METADATA_OFFSET count=3 2>/dev/null | xxd -p)
if [ ${#METADATA} -lt 6 ]; then
echo "错误: 无法读取 metadata"
exit 1
fi
# 解析 little-endian uint16 (blocks) 和 uint8 (last_block_size)
BLOCKS_LOW=$((16#${METADATA:0:2}))
BLOCKS_HIGH=$((16#${METADATA:2:2}))
BLOCKS=$(( BLOCKS_LOW + BLOCKS_HIGH * 256 ))
LAST_BLOCK_SIZE=$((16#${METADATA:4:2}))
echo " blocks: $BLOCKS (0x$(printf '%04x' $BLOCKS))"
echo " last_block_size: $LAST_BLOCK_SIZE (0x$(printf '%02x' $LAST_BLOCK_SIZE))"
# 计算 firmware 大小
FIRMWARE_SIZE=$(( BLOCKS * 256 + LAST_BLOCK_SIZE ))
echo " 计算出 firmware 大小: $FIRMWARE_SIZE bytes"
echo ""
# 提取 head file (firmware: 从 0x4000 开始)
HEAD_FILE="${PREFIX}.firmware"
echo "提取 head file (firmware)..."
dd if="$INPUT" of="$HEAD_FILE" bs=1 skip=$FIRMWARE_OFFSET count=$FIRMWARE_SIZE status=none
if [ $? -eq 0 ]; then
ACTUAL_HEAD_SIZE=$(stat -f%z "$HEAD_FILE" 2>/dev/null || stat -c%s "$HEAD_FILE" 2>/dev/null)
echo " ✓ 已保存: $HEAD_FILE ($ACTUAL_HEAD_SIZE bytes)"
else
echo " ✗ 提取失败"
exit 1
fi
# 提取 tail file (IUM: 从 0x1dc00 开始, 256 bytes)
TAIL_FILE="${PREFIX}.ium"
echo "提取 tail file (IUM)..."
dd if="$INPUT" of="$TAIL_FILE" bs=1 skip=$IUM_OFFSET count=$IUM_SIZE status=none
if [ $? -eq 0 ]; then
ACTUAL_TAIL_SIZE=$(stat -f%z "$TAIL_FILE" 2>/dev/null || stat -c%s "$TAIL_FILE" 2>/dev/null)
echo " ✓ 已保存: $TAIL_FILE ($ACTUAL_TAIL_SIZE bytes)"
else
echo " ✗ 提取失败"
exit 1
fi
# 生成 metadata.json
METADATA_FILE="${PREFIX}_metadata.json"
echo "生成 metadata.json..."
cat > "$METADATA_FILE" << EOF
{"blocks": "0x$(printf '%04x' $BLOCKS)", "lastBlockSize": "0x$(printf '%02x' $LAST_BLOCK_SIZE)"}
EOF
echo " ✓ 已保存: $METADATA_FILE"
echo ""
# 显示结果
echo "========================================="
echo "提取完成!"
echo ""
echo "输出文件:"
echo " Head (firmware): $HEAD_FILE ($ACTUAL_HEAD_SIZE bytes)"
echo " Tail (IUM): $TAIL_FILE ($ACTUAL_TAIL_SIZE bytes)"
echo " Metadata: $METADATA_FILE"
echo ""
echo "Metadata 内容:"
cat "$METADATA_FILE"
echo ""

188
CP02/merge_2323_firmware.sh Executable file
View File

@ -0,0 +1,188 @@
#!/bin/bash
# Function to show usage
show_usage() {
echo "Usage: $0 [OPTIONS]"
echo ""
echo "Options:"
echo " -o, --output FILE Output file (default: 40w.bin)"
echo " -u, --updater FILE PA768 Updater binary file"
echo " -f, --firmware FILE 2323 firmware binary file"
echo " -i, --ium FILE 2323 IUM binary file"
echo " -h, --help Show this help message"
echo ""
echo "Example:"
echo " $0 -u PA768_Updater.bin -f 2323_firmware.bin -i 2323_ium.bin -o output.bin"
echo ""
exit 1
}
# Default values
OUTPUT_FILE="40w.bin"
PA768_FILE=""
FIRMWARE_FILE=""
IUM_FILE=""
# Parse command line arguments
while [[ $# -gt 0 ]]; do
case $1 in
-o | --output)
OUTPUT_FILE="$2"
shift 2
;;
-u | --updater)
PA768_FILE="$2"
shift 2
;;
-f | --firmware)
FIRMWARE_FILE="$2"
shift 2
;;
-i | --ium)
IUM_FILE="$2"
shift 2
;;
-h | --help)
show_usage
;;
*)
echo "Unknown option: $1"
show_usage
;;
esac
done
# Configuration
MAX_LENGTH=0x1df00 # 122,624 bytes
# Convert hex to decimal for dd
MAX_LENGTH_DEC=$((MAX_LENGTH)) # 122,624 bytes
IUM_OFFSET=$((0x1dc00)) # 121,856 bytes
METADATA_OFFSET=$((0x1dd00)) # 121,088 bytes
echo "Creating merged binary file: $OUTPUT_FILE"
echo "Maximum file size: $MAX_LENGTH ($MAX_LENGTH_DEC bytes)"
echo ""
echo "Input files:"
echo " PA768 Updater: ${PA768_FILE:-"(not specified)"}"
echo " Firmware: ${FIRMWARE_FILE:-"(not specified)"}"
echo " IUM: ${IUM_FILE:-"(not specified)"}"
echo ""
# Step 1: Create initial file filled with 0xFF bytes
echo "Step 1: Creating base file filled with 0xFF..."
dd if=/dev/zero bs=1 count=$MAX_LENGTH_DEC 2>/dev/null | LC_ALL=C tr "\000" "\377" >"$OUTPUT_FILE"
if [ $? -ne 0 ]; then
echo "Error: Failed to create base file"
exit 1
fi
# Step 2: Write PA768_Updater.bin at offset 0 (entire file)
echo "Step 2: Writing PA768_Updater.bin at offset 0..."
if [ -n "$PA768_FILE" ] && [ -f "$PA768_FILE" ]; then
UPDATER_SIZE=$(stat -f%z "$PA768_FILE" 2>/dev/null || stat -c%s "$PA768_FILE" 2>/dev/null)
echo " Updater size: $UPDATER_SIZE bytes"
dd if="$PA768_FILE" of="$OUTPUT_FILE" bs=1 conv=notrunc 2>/dev/null
echo " ✓ PA768_Updater.bin written successfully"
elif [ -n "$PA768_FILE" ]; then
echo " ✗ Error: $PA768_FILE not found"
exit 1
else
echo " - PA768_Updater.bin skipped (not specified)"
fi
# Step 3: Write firmware at offset 0x4000 (16,384) - entire file
echo "Step 3: Writing firmware at offset 0x4000..."
if [ -n "$FIRMWARE_FILE" ] && [ -f "$FIRMWARE_FILE" ]; then
# Get firmware file size first
FIRMWARE_SIZE=$(stat -f%z "$FIRMWARE_FILE" 2>/dev/null || stat -c%s "$FIRMWARE_FILE" 2>/dev/null)
# Calculate blocks for 2323_firmware.bin
BLOCK_SIZE=256
FIRMWARE_BLOCKS=$((FIRMWARE_SIZE / BLOCK_SIZE))
LAST_BLOCK_SIZE=$((FIRMWARE_SIZE % BLOCK_SIZE))
echo " Firmware size: $FIRMWARE_SIZE bytes"
echo " Complete blocks: $FIRMWARE_BLOCKS"
echo " Last block size: $LAST_BLOCK_SIZE bytes"
dd if="$FIRMWARE_FILE" of="$OUTPUT_FILE" bs=1 seek=16384 conv=notrunc 2>/dev/null
echo " ✓ 2323_firmware.bin written successfully"
# Write metadata at 0x1dd00
echo "Step 3a: Writing firmware metadata at offset 0x1dd00..."
# Create metadata structure using Python for reliable binary output
python3 -c "
import struct
blocks = $FIRMWARE_BLOCKS
last_size = $LAST_BLOCK_SIZE
# struct UserFirmwareMetadata: uint16_t blocks, uint8_t last_block_size, uint8_t reserved[13]
metadata = struct.pack('<HB13x', blocks, last_size) # little-endian uint16 + uint8 + 13 zero bytes
with open('temp_metadata.bin', 'wb') as f:
f.write(metadata)
print(f' Metadata: blocks={blocks} (0x{blocks:04x}), last_block_size={last_size} (0x{last_size:02x})')
"
# Write metadata to file
dd if=temp_metadata.bin of="$OUTPUT_FILE" bs=1 seek=$METADATA_OFFSET conv=notrunc 2>/dev/null
rm temp_metadata.bin
echo " ✓ Firmware metadata written successfully"
elif [ -n "$FIRMWARE_FILE" ]; then
echo " ✗ Error: $FIRMWARE_FILE not found"
exit 1
else
echo " - Firmware skipped (not specified)"
FIRMWARE_SIZE=0
FIRMWARE_BLOCKS=0
LAST_BLOCK_SIZE=0
fi
# Step 4: Write IUM data at offset 0x1dc00 (121,856)
echo "Step 4: Writing IUM data at offset 0x1dc00..."
if [ -n "$IUM_FILE" ] && [ -f "$IUM_FILE" ]; then
IUM_SIZE=$(stat -f%z "$IUM_FILE" 2>/dev/null || stat -c%s "$IUM_FILE" 2>/dev/null)
echo " IUM size: $IUM_SIZE bytes"
dd if="$IUM_FILE" of="$OUTPUT_FILE" bs=1 count=256 seek=$IUM_OFFSET conv=notrunc 2>/dev/null
echo " ✓ 2323_ium.bin written successfully"
elif [ -n "$IUM_FILE" ]; then
echo " ✗ Error: $IUM_FILE not found"
exit 1
else
echo " - IUM data skipped (not specified)"
fi
# Verify final file size
ACTUAL_SIZE=$(stat -f%z "$OUTPUT_FILE" 2>/dev/null || stat -c%s "$OUTPUT_FILE" 2>/dev/null)
echo ""
echo "Merge completed successfully!"
echo "Output file: $OUTPUT_FILE"
echo "Expected size: $MAX_LENGTH_DEC bytes"
echo "Actual size: $ACTUAL_SIZE bytes"
if [ "$ACTUAL_SIZE" -eq "$MAX_LENGTH_DEC" ]; then
echo "✓ File size matches expected length"
else
echo "⚠ Warning: File size mismatch"
fi
# Show memory layout with firmware blocks
echo ""
echo "Memory Layout:"
if [ -n "$PA768_FILE" ]; then
echo " 0x00000 - varies: PA768_Updater.bin (entire file)"
fi
if [ -n "$FIRMWARE_FILE" ] && [ $FIRMWARE_SIZE -gt 0 ]; then
FIRMWARE_END=$((16384 + FIRMWARE_SIZE))
echo " 0x04000 - 0x$(printf '%x' $((FIRMWARE_END - 1))): 2323_firmware.bin ($FIRMWARE_SIZE bytes, $FIRMWARE_BLOCKS blocks + $LAST_BLOCK_SIZE bytes)"
echo " 0x1dd00 - 0x1dd0f: Firmware metadata (blocks=$FIRMWARE_BLOCKS, last_block_size=$LAST_BLOCK_SIZE)"
fi
if [ -n "$IUM_FILE" ]; then
echo " 0x1dc00 - 0x1dcff: 2323_ium.bin (256 bytes)"
fi
echo " Total size: $ACTUAL_SIZE bytes (0x$(printf '%x' $ACTUAL_SIZE))"
echo ""
echo "Use 'hexdump -C $OUTPUT_FILE | grep 1dd00' to verify metadata"

84
CP02/split_and_merge.sh Executable file
View File

@ -0,0 +1,84 @@
#!/usr/bin/env bash
# 用法: ./split256_and_merge.sh <待切分文件> <updater.bin路径> <输出文件名>
# 示例: ./split256_and_merge.sh /Users/ching/Downloads/4_SW2303P_B_V1.0_00_E5C3.bin ~/Downloads/PA503_Updater.bin 40_4.bin
input="$1"
updater="$2"
output="$3"
if [ -z "$input" ] || [ -z "$updater" ] || [ -z "$output" ]; then
echo "用法: $0 <待切分文件> <updater.bin路径> <输出文件名>"
exit 1
fi
# 检查文件是否存在
if [ ! -f "$input" ]; then
echo "文件不存在: $input"
exit 1
fi
if [ ! -f "$updater" ]; then
echo "文件不存在: $updater"
exit 1
fi
# 获取文件大小(兼容 Linux / macOS
if stat --version >/dev/null 2>&1; then
size=$(stat -c%s "$input") # Linux
else
size=$(stat -f%z "$input") # macOS
fi
if [ "$size" -lt 256 ]; then
echo "文件太小 (<256 字节),无法切分"
exit 1
fi
head_size=$((size - 256))
# 输出文件名
head_file="${input}.head"
tail_file="${input}.tail"
# 切分
dd if="$input" of="$head_file" bs=1 count=$head_size status=none
dd if="$input" of="$tail_file" bs=1 skip=$head_size status=none
echo "切分完成:"
echo " 前面部分: $head_file ($head_size 字节)"
echo " 最后部分: $tail_file (256 字节)"
# 调用 merge 脚本
if ./merge_2323_firmware.sh -u "$updater" -f "$head_file" -i "$tail_file" -o "$output"; then
echo "✅ 合并成功,输出文件: $output"
# 获取输出文件大小
if stat --version >/dev/null 2>&1; then
out_size=$(stat -c%s "$output") # Linux
else
out_size=$(stat -f%z "$output") # macOS
fi
# 删除最后 1 字节
if [ "$out_size" -gt 0 ]; then
if command -v truncate >/dev/null 2>&1 && truncate -s $((out_size - 1)) "$output" 2>/dev/null; then
echo "已删除最后 1 字节(使用 truncate"
else
# 使用 dd 备用方案
dd if="$output" of="${output}.tmp" bs=1 count=$((out_size - 1)) status=none && mv "${output}.tmp" "$output"
echo "已删除最后 1 字节(使用 dd"
fi
echo "最终文件大小: $(stat -c%s "$output" 2>/dev/null || stat -f%z "$output")"
fi
# 检查是否 < 0x1df00
final_size=$(stat -c%s "$output" 2>/dev/null || stat -f%z "$output")
if [ "$final_size" -gt 122624 ]; then
echo "⚠️ 警告: 文件大小仍然 >= 0x1df00 (当前 $final_size 字节)"
fi
# 清理临时文件
rm -f "$head_file" "$tail_file"
echo "⚠️⚠️⚠️ Remember signing output file! ⚠️⚠️⚠️"
else
echo "合并失败,保留临时文件以便排查"
fi