KORG PA600中文化完整技术手册 - 从逆向到实现的全流程指南

  1. KORG PA600 中文化完整技术手册
    1. 目录
    2. 项目背景
      1. 项目目标
      2. 技术挑战
    3. 核心技术发现
      1. 发现1: FONTS.PEG字体标记机制
      2. 发现2: 中文文件名转换路径
      3. 发现3: PKG文件ZLIB压缩特殊性
    4. PKG固件完整封包解包流程
      1. 一、PKG文件格式详解
      2. 1.1 整体结构
      3. 1.2 Chunk通用结构
      4. 1.3 Chunk类型表
      5. 1.4 FILE Chunk详细格式(核心)
      6. 1.5 ZLIB多块压缩格式
      7. 二、解包操作流程(普通人步骤)
      8. 步骤1: 准备环境
      9. 步骤2: 列出PKG内所有文件
      10. 步骤3: 提取目标文件
      11. 步骤4: 验证提取结果
      12. 三、修改操作流程
      13. 3.1 修改FONTS.PEG(启用外部BDF)
      14. 3.2 修改BDF字体(注入汉字)
      15. 四、封包操作流程(普通人步骤)
      16. 步骤1: 理解封包流程
      17. 步骤2: 使用Python工具封包
      18. 步骤3: 验证封包结果
      19. 五、ZLIB压缩/解压代码详解
      20. 解压函数(完整注释版)
      21. 压缩函数(完整注释版)
      22. 六、KorgPackage Java工具说明
      23. 6.1 GUI工具的问题
      24. 6.2 我们的解决方案: KorgPkgCLI
      25. 6.3 Java vs Python工具对比
    5. 三大需求技术方案
      1. 需求一: 固件菜单汉化
      2. 目标文件
      3. 字符串格式
      4. 修改约束
      5. 实施代码
      6. 需求二: U盘文件名中文显示
      7. 问题分析
      8. 解决方案: 字体劫持法
      9. 需求三: Style名称中文化
      10. Style文件格式
      11. 名称存储位置
      12. 修改方法
    6. Style文件修改流程
      1. STY文件结构详解
      2. STY修改操作步骤
      3. 步骤1: 分析现有Style
      4. 步骤2: 定位名称字段
      5. 步骤3: 修改并保存
    7. 工具链完整说明
      1. 核心工具清单(可复用)
      2. Java CLI工具(复用KorgPackage库)
      3. pkg_tool.py 详细用法
      4. pkg_modifier.py 详细用法
      5. pkg_font_patch.py 详细用法
      6. pkg_verify.py 详细用法
    8. 测试验证记录
      1. 测试固件列表
      2. 关键测试结论
    9. 关键警告与注意事项
      1. ⚠️ 绝对禁止
      2. ⚠️ 必须遵守
      3. ⚠️ 强烈建议
    10. 附录
      1. A. PKG Chunk完整类型表
      2. B. FONTS.PEG字体条目
      3. C. 高频汉字映射表
      4. D. 参考资源

KORG PA600 中文化完整技术手册

目标读者: 本文档站在”普通人看了能还原全部操作”的角度编写,包含完整的思路、原理和操作步骤。


目录

  1. 项目背景
  2. 核心技术发现
  3. PKG固件完整封包解包流程
  4. 三大需求技术方案
  5. Style文件修改流程
  6. 工具链完整说明
  7. 测试验证记录
  8. 关键警告与注意事项
  9. 附录

项目背景

项目目标

KORG PA600电子琴固件v2.10中文化,包含三个核心需求:

  1. 固件菜单汉化 - UI界面中文显示
  2. U盘文件名中文显示 - MEDIA界面支持中文文件夹/文件名
  3. Style名称中文化 - 风格文件名称中文显示

技术挑战

挑战 描述 解决状态
PKG格式未知 KORG专有封装格式 ✅ 已逆向
ZLIB压缩特殊 大端序解压大小 ✅ 已解决
字体系统封闭 内部/外部字体切换 ✅ 已突破
Unicode转换 中文被转成问号 ✅ 字体劫持绕过
OPOS加密 主程序不可修改 ⚠️ 无法触碰

核心技术发现

发现1: FONTS.PEG字体标记机制

位置: 每个字体文件名前3字节

04 b1 80 → 内部编译字体(默认,从ROM读取)
ff ff 80 → 外部BDF文件(从文件系统读取)

验证实验:

  1. 修改verdana11.BDF中字符”C”为方块
  2. 将FONTS.PEG中verdana11的标记改为ff ff 80
  3. 刷机后界面所有”C”变方块

结论: 系统支持运行时从外部BDF文件加载字体!

发现2: 中文文件名转换路径

FAT32 (UTF-16 LFN)
    ↓
OPOS编码转换层
    ↓
未知字符 → 0x3F ('?')
    ↓
BDF字体渲染 → 显示问号

劫持点: 使用高位ASCII (0x80-0xFF)绕过转换

发现3: PKG文件ZLIB压缩特殊性

关键: 解压大小字段使用大端序(Big-Endian),与其他字段不同!

comp_size = struct.unpack('<I', f.read(4))[0] - 4   # 小端序
uncomp_size = struct.unpack('>I', f.read(4))[0]     # 大端序!

PKG固件完整封包解包流程

本节是普通人复现操作的核心,详细说明每一步的原理和操作。

一、PKG文件格式详解

1.1 整体结构

┌─────────────────────────────────────────┐
│ [16 bytes] PKG MD5 Hash                 │ ← 文件16字节后所有内容的MD5
├─────────────────────────────────────────┤
│ [Chunk 1] Header                        │
├─────────────────────────────────────────┤
│ [Chunk 2] SystemFile (kernel)           │
├─────────────────────────────────────────┤
│ [Chunk 3] SystemFile (ramdisk)          │
├─────────────────────────────────────────┤
│ ...                                     │
├─────────────────────────────────────────┤
│ [Chunk N] FILE/DIRECTORY/LINK           │
└─────────────────────────────────────────┘

1.2 Chunk通用结构

┌────────────┬────────────┬─────────────────┬────────────┐
│ Chunk ID   │ Chunk Size │ Chunk Data      │ Padding    │
│ 4 bytes LE │ 4 bytes LE │ N bytes         │ 0-3 bytes  │
└────────────┴────────────┴─────────────────┴────────────┘
                                              ↑
                                         4字节对齐填充

1.3 Chunk类型表

ID 名称 说明
1 HEADER 固件版本信息
2 UPDATE_KERNEL 更新内核镜像
3 UPDATE_RAMDISK 更新内存盘
15 INSTALLER_SCRIPT 安装脚本
16 DIRECTORY 目录
17 FILE 文件(重点修改目标)
18 LINK 符号链接
19 ROOT_FS 根文件系统

1.4 FILE Chunk详细格式(核心)

┌─────────────────────────────────────────────────────────────────┐
│ FILE Chunk Body                                                 │
├─────────────────────────────────────────────────────────────────┤
│ [16 bytes] MD5 Hash     ← 解压后数据的MD5                        │
│ [2 bytes]  Owner        ← 文件属主 (little-endian)               │
│ [2 bytes]  Group        ← 文件属组 (little-endian)               │
│ [2 bytes]  Attributes   ← 文件权限 (little-endian)               │
│ [2 bytes]  Condition    ← 条件标志 (little-endian)               │
│ [4 bytes]  Data Size    ← 解压后大小 (little-endian)             │
│ [1 byte]   Compression  ← 压缩类型: 0=RAW, 1=ZLIB, 16=ENCRYPTED  │
│ [N bytes]  Name         ← 文件路径 (null-terminated)             │
│ [N bytes]  Date         ← 日期字符串 (null-terminated)           │
│ [N bytes]  Time         ← 时间字符串 (null-terminated)           │
│ [N bytes]  Data         ← 压缩/原始数据                          │
└─────────────────────────────────────────────────────────────────┘

1.5 ZLIB多块压缩格式

当Compression Type = 1时,数据按以下格式分块压缩:

┌────────────────────────────────────────────────────────────────┐
│ ZLIB Block                                                     │
├────────────────────────────────────────────────────────────────┤
│ [4 bytes] Block Type    ← 0x00000100=数据块, 0x00000101=结束    │
│ [4 bytes] Comp Size + 4 ← 压缩大小+4 (little-endian)           │
│ [4 bytes] Uncomp Size   ← 解压大小 (BIG-ENDIAN! 关键!)          │
│ [N bytes] Zlib Data     ← zlib压缩数据                          │
│ [0-3 bytes] Padding     ← 4字节对齐                             │
├────────────────────────────────────────────────────────────────┤
│ [下一个Block...]                                                │
├────────────────────────────────────────────────────────────────┤
│ [4 bytes] 0x00000101    ← 结束标记                              │
│ [4 bytes] 0x00000000    ← 空大小                                │
└────────────────────────────────────────────────────────────────┘

二、解包操作流程(普通人步骤)

步骤1: 准备环境

# 创建工作目录
mkdir -p /tmp/PA600 && cd /tmp/PA600

# 准备原始固件文件
cp /path/to/_Operating_System_v210.pkg ./

# 确认Python环境
python3 --version  # 需要Python 3.6+

步骤2: 列出PKG内所有文件

python3 pkg_tool.py list _Operating_System_v210.pkg

输出示例:

PKG MD5 Hash: 112f8e917c4c97a7d495597fc1bffb67
Files in package:
  1. /omega_sys/EDITRES/ENG/Z103AENG.PEG (971561 bytes, ZLIB)
  2. /omega_sys/STARTUP/ALL/EDITRES/FONTS.PEG (113743 bytes, ZLIB)
  3. /omega_sys/STARTUP/ALL/EDITRES/verdana11.BDF (100326 bytes, ZLIB)
  ...
  24. /omega_sys/OPOS (11240692 bytes, ENCRYPTED)  ← 加密文件,不可修改!

步骤3: 提取目标文件

# 提取字体配置文件
python3 pkg_tool.py extract _Operating_System_v210.pkg "FONTS.PEG" ./FONTS.PEG

# 提取字体文件
python3 pkg_tool.py extract _Operating_System_v210.pkg "verdana11.BDF" ./verdana11.BDF

# 提取语言资源文件
python3 pkg_tool.py extract _Operating_System_v210.pkg "Z103AENG.PEG" ./Z103AENG.PEG

步骤4: 验证提取结果

# 检查文件大小
ls -la *.PEG *.BDF

# 验证BDF格式
head -20 verdana11.BDF
# 应该看到: STARTFONT 2.1 / FONT -misc-verdana...

三、修改操作流程

3.1 修改FONTS.PEG(启用外部BDF)

原理: 将verdana系列字体的标记从04 b1 80改为ff ff 80

def modify_fonts_peg(data):
    """修改FONTS.PEG - 启用外部BDF加载"""
    data = bytearray(data)
    verdana_files = [
        b'verdana10.BDF', b'verdana11.bdf', b'verdana9.BDF',
        b'verdanab10.BDF', b'verdanab11.BDF', b'verdanab12.BDF',
        b'verdanab13.BDF', b'verdanab19.BDF', b'verdanab9.BDF'
    ]

    modified = 0
    for vf in verdana_files:
        pos = data.find(vf)
        if pos != -1:
            marker_pos = pos - 3  # 标记在文件名前3字节
            if data[marker_pos:marker_pos+3] == bytearray([0x04, 0xb1, 0x80]):
                data[marker_pos] = 0xff
                data[marker_pos+1] = 0xff
                data[marker_pos+2] = 0x80
                modified += 1
                print(f'  {vf.decode()}: 04b180 -> ffff80')

    print(f'修改了 {modified} 个字体引用标记')
    return bytes(data)

3.2 修改BDF字体(注入汉字)

BDF字符条目格式:

STARTCHAR CN_YIN          ← 字符名称
ENCODING 192              ← 字符编码 (0xC0 = 192)
SWIDTH 1000 0             ← 可缩放宽度
DWIDTH 11 0               ← 设备宽度(像素)
BBX 11 11 0 -2            ← 边界框: 宽 高 X偏移 Y偏移
BITMAP                    ← 位图开始
FFE0                      ← 第1行: 11111111 11100000
8020                      ← 第2行: 10000000 00100000
...                       ← (共11行)
ENDCHAR                   ← 字符结束

注入汉字到高位槽位:

def inject_chinese_char(bdf_content, encoding, char_name, bitmap_lines):
    """向BDF添加一个汉字字符"""
    new_entry = f'''STARTCHAR {char_name}
ENCODING {encoding}
SWIDTH 1000 0
DWIDTH 11 0
BBX 11 11 0 -2
BITMAP
{chr(10).join(bitmap_lines)}
ENDCHAR'''

    # 在ENDFONT前插入
    return bdf_content.replace('ENDFONT', new_entry + '\\nENDFONT')

四、封包操作流程(普通人步骤)

这是最关键的部分,必须正确执行否则固件无法使用!

步骤1: 理解封包流程

1. 打开原始PKG
2. 逐个复制Chunk到新文件
3. 遇到目标文件时:
   a. 解压原数据
   b. 应用修改函数
   c. 重新压缩
   d. 计算新MD5
   e. 构建新Chunk
4. 保持其他Chunk不变
5. 重新计算PKG总MD5
6. 写入文件头

步骤2: 使用Python工具封包

方法A: 单文件修改 (pkg_modifier.py)

# 测试修改: SOUND → AUDIO
python3 pkg_modifier.py test _Operating_System_v210.pkg output.pkg

方法B: 多文件修改 (pkg_font_patch.py)

# 定义修改列表
patches = [
    ('FONTS.PEG', modify_fonts_peg),       # 修改字体标记
    ('verdana11.BDF', inject_chinese),     # 注入汉字
    ('verdanab11.BDF', inject_chinese),    # 粗体版本
]

# 执行批量修改
multi_file_patch('_Operating_System_v210.pkg', 'output.pkg', patches)

步骤3: 验证封包结果

# 1. 验证文件完整性
python3 pkg_verify.py verify output.pkg

# 2. 对比原始和修改后的包
python3 pkg_verify.py compare _Operating_System_v210.pkg output.pkg

# 3. 提取修改后的文件验证
python3 pkg_tool.py extract output.pkg "FONTS.PEG" ./FONTS_modified.PEG

验证输出示例:

======================================================================
Verifying: output.pkg
======================================================================
File size: 108,871,656 bytes

MD5 Hash Verification:
  Stored:     1d0860cd5eef178da99caccf6efe907d
  Calculated: 1d0860cd5eef178da99caccf6efe907d
  Status:     ✓ MATCH

Chunk Structure Verification:
#    Type                      Size         Status
---- ------------------------- ------------ ------------------------------
   0 HEADER                         1844    ✓ OK
   1 UPDATE_KERNEL              1608788    ✓ OK
   ...
  24 FILE (FONTS.PEG)             26048    ✓ OK (ZLIB verified)
  ...

Summary:
  Total chunks: 130
  Files: 89 (88 valid, 1 encrypted)
  ✓ Package is VALID

五、ZLIB压缩/解压代码详解

解压函数(完整注释版)

def decompress_zlib_blocks(f, data_size):
    """
    解压PKG中的ZLIB多块数据

    参数:
        f: 文件句柄,位置在压缩数据开始处
        data_size: 期望的解压后总大小

    返回:
        解压后的完整数据 (bytes)

    注意:
        - 块大小字段是小端序
        - 解压大小字段是大端序(关键!)
    """
    result = bytearray()

    while len(result) < data_size:
        # 读取块类型 (4字节, 小端序)
        block_type = struct.unpack('<I', f.read(4))[0]

        # 0x00000101 = 结束标记
        if block_type == 0x00000101:
            f.read(4)  # 跳过后面的0
            break

        # 0x00000100 = 数据块
        if block_type != 0x00000100:
            break

        # 压缩大小 (4字节, 小端序) - 包含4字节头所以要减4
        comp_size = struct.unpack('<I', f.read(4))[0] - 4

        # 解压大小 (4字节, 大端序!) - 这是关键发现
        uncomp_size = struct.unpack('>I', f.read(4))[0]

        # 读取压缩数据
        comp_data = f.read(comp_size)

        # 解压
        decompressed = zlib.decompress(comp_data)
        result.extend(decompressed)

        # 处理4字节对齐填充
        rem = comp_size % 4
        if rem != 0:
            f.seek(4 - rem, 1)  # 跳过填充字节

    return bytes(result)

压缩函数(完整注释版)

def compress_zlib_blocks(data):
    """
    将数据压缩为PKG格式的ZLIB多块

    参数:
        data: 原始数据 (bytes)

    返回:
        PKG格式的压缩数据 (bytes)
    """
    result = bytearray()
    offset = 0
    block_size = 0x100000  # 每块最大1MB

    while offset < len(data):
        # 取一块数据
        chunk = data[offset:offset + block_size]

        # 压缩(最高压缩级别)
        compressed = zlib.compress(chunk, 9)

        # 写入块头
        result.extend(struct.pack('<I', 0x00000100))       # 块类型 (小端)
        result.extend(struct.pack('<I', len(compressed) + 4))  # 压缩大小+4 (小端)
        result.extend(struct.pack('>I', len(chunk)))       # 解压大小 (大端!)

        # 写入压缩数据
        result.extend(compressed)

        # 4字节对齐填充
        rem = len(compressed) % 4
        if rem != 0:
            result.extend(b'\\x00' * (4 - rem))

        offset += block_size

    # 写入结束标记
    result.extend(struct.pack('<I', 0x00000101))
    result.extend(struct.pack('<I', 0x00000000))

    return bytes(result)

六、KorgPackage Java工具说明

6.1 GUI工具的问题

KorgPackage v1.2 是一个JavaFX GUI程序,存在以下问题:

  1. 依赖JavaFX - 需要额外安装JavaFX运行时
  2. 修改OPOS - 重新保存会触碰加密的OPOS文件
  3. 无法自动化 - GUI操作不适合批量处理

6.2 我们的解决方案: KorgPkgCLI

从KorgPackage源码编译命令行版本:

编译步骤:

# 1. 获取源码
git clone <https://github.com/Polprzewodnikowy/KorgPackage.git>
cd KorgPackage

# 2. 编译核心库
mkdir -p classes
cd src/main
javac -d /tmp/PA600/classes korgpkg/*.java

# 3. 编译CLI工具
cd /tmp/PA600
javac -cp classes KorgPkgCLI.java -d classes

使用方法:

# 列出文件
java -cp classes KorgPkgCLI list _Operating_System_v210.pkg

# 提取文件
java -cp classes KorgPkgCLI extract _Operating_System_v210.pkg "FONTS.PEG" output.PEG

# 打补丁
java -cp classes KorgPkgCLI patch input.pkg output.pkg "verdana11.BDF" modified.BDF

6.3 Java vs Python工具对比

特性 Java (KorgPkgCLI) Python (pkg_tool.py)
依赖 JDK 8+ Python 3.6+
速度 中等
灵活性 一般 高(易修改)
OPOS保护 ✅ 不触碰 ✅ 不触碰
推荐场景 简单提取 批量修改

结论: 推荐使用Python工具链进行生产环境操作。


三大需求技术方案

需求一: 固件菜单汉化

目标文件

/omega_sys/EDITRES/ENG/Z103AENG.PEG (971KB)

字符串格式

0a 53 4f 55 4e 44 20 4d 65 6e 75
│  └─────────────────────────────┘
│         "SOUND Menu" (10字节)
└── 长度前缀 (0x0a = 10)

修改约束

铁律: 替换后字节长度必须完全相同!

原文 替换 字节数 结果
SOUND AUDIO 5 ✅ 可行
Cancel 取消 6 (UTF-8) ✅ 可行
OK 2 vs 3 ❌ 需填充

实施代码

def safe_replace(data, old_str, new_str):
    old_bytes = old_str.encode('latin-1')
    new_bytes = new_str.encode('utf-8')

    # 确保长度相同
    if len(new_bytes) < len(old_bytes):
        new_bytes += b' ' * (len(old_bytes) - len(new_bytes))
    elif len(new_bytes) > len(old_bytes):
        new_bytes = new_bytes[:len(old_bytes)]

    return data.replace(old_bytes, new_bytes)

需求二: U盘文件名中文显示

问题分析

测试文件夹 显示结果 含义
AAAA 4个方块 MEDIA使用BDF字体
测试 2个问号 中文被转成0x3F

解决方案: 字体劫持法

原理:

正常: 中文 → OPOS转换 → ? (0x3F) → 渲染问号
劫持: 0xC0-0xFF → OPOS保留 → 渲染汉字

字符映射表:

CHAR_MAP = {
    '音': 0xC0, '乐': 0xC1, '文': 0xC2, '件': 0xC3,
    '夹': 0xC4, '歌': 0xC5, '曲': 0xC6, '测': 0xC7,
    '试': 0xC8, '取': 0xC9, '消': 0xCA, '删': 0xCB,
    '除': 0xCC, '保': 0xCD, '存': 0xCE, '加': 0xCF,
    # ... 最多128个字符 (0x80-0xFF)
}

U盘重命名工具:

def rename_for_pa600(filename):
    """将中文文件名转为PA600可显示格式"""
    result = bytearray()
    for char in filename:
        if char in CHAR_MAP:
            result.append(CHAR_MAP[char])
        else:
            result.extend(char.encode('latin-1', errors='replace'))
    return bytes(result)

# 示例: "音乐" → b'\\xC0\\xC1'

需求三: Style名称中文化

Style文件格式

类型: 标准MIDI + KORG SFF2扩展

结构分析:

0x0000: 4D 54 68 64 00 00 00 06 00 00 00 01 07 80  (MThd MIDI头)
0x0010: 4D 54 72 6B 00 00 2E DD                    (MTrk 轨道)
0x0026: FF 06 04 53 46 46 32                       (FF 06 = Marker "SFF2")
0x002E: FF 03 20 ...                               (FF 03 = Track Name)
         └─ 32字节名称字段

名称存储位置

位置 示例值 PA600显示
文件名 XinJiangJieZou 01.sty 待确认
内部FF03 01.sty (含填充) 待确认

修改方法

def modify_style_name(sty_path, new_name):
    """修改Style文件内部名称"""
    with open(sty_path, 'rb') as f:
        data = bytearray(f.read())

    # 找到SFF2标记
    sff2_pos = data.find(b'SFF2')
    if sff2_pos == -1:
        print("Not a valid SFF2 style file")
        return

    # FF 03跟在SFF2后面约5字节
    name_pos = sff2_pos + 5

    # 检查是否是Track Name事件
    if data[name_pos:name_pos+2] == b'\\xff\\x03':
        length = data[name_pos+2]  # 通常是32字节

        # 编码新名称,填充到指定长度
        encoded = new_name.encode('utf-8')[:length].ljust(length, b'\\x00')
        data[name_pos+3:name_pos+3+length] = encoded

    with open(sty_path, 'wb') as f:
        f.write(data)

Style文件修改流程

STY文件结构详解

┌──────────────────────────────────────────────────────────────┐
│ MIDI Header (MThd)                                           │
│ ├─ Chunk ID: "MThd" (4 bytes)                                │
│ ├─ Chunk Size: 6 (4 bytes, big-endian)                       │
│ ├─ Format: 0 (2 bytes) - Single track                        │
│ ├─ Tracks: 1 (2 bytes)                                       │
│ └─ Division: 1920 (2 bytes) - Ticks per quarter note         │
├──────────────────────────────────────────────────────────────┤
│ MIDI Track (MTrk)                                            │
│ ├─ Chunk ID: "MTrk" (4 bytes)                                │
│ ├─ Chunk Size: N (4 bytes, big-endian)                       │
│ └─ Track Data:                                               │
│     ├─ FF 06 04 "SFF2" - Marker (KORG SFF2标识)              │
│     ├─ FF 03 20 <name> - Track Name (风格名称,32字节)         │
│     ├─ FF 51 03 <tempo> - Tempo                              │
│     ├─ FF 58 04 <time> - Time Signature                      │
│     └─ ... MIDI事件 ...                                      │
└──────────────────────────────────────────────────────────────┘

STY修改操作步骤

步骤1: 分析现有Style

# 查看文件头
xxd XinJiangJieZou\\ 01.sty | head -10

# 搜索SFF2标记
grep -oba "SFF2" XinJiangJieZou\\ 01.sty

步骤2: 定位名称字段

with open('XinJiangJieZou 01.sty', 'rb') as f:
    data = f.read()

sff2_pos = data.find(b'SFF2')
print(f"SFF2 at: 0x{sff2_pos:x}")

# 名称通常在SFF2后约5-10字节
print(f"Context: {data[sff2_pos:sff2_pos+50]}")

步骤3: 修改并保存

# 使用上面的modify_style_name函数
modify_style_name('XinJiangJieZou 01.sty', '新疆节奏')

工具链完整说明

工具位置: 所有工具脚本位于 ./tools/ 目录下

tools.zip

核心工具清单(可复用)

脚本 路径 功能 使用场景
pkg_tool.py ./tools/pkg_tool.py PKG解析/提取 查看和提取文件
pkg_modifier.py ./tools/pkg_modifier.py 完整PKG修改封包 核心修改工具
pkg_font_patch.py ./tools/pkg_font_patch.py 多文件批量修改 字体+配置修改
pkg_verify.py ./tools/pkg_verify.py 完整性验证 刷机前检查
pkg_patch.py ./tools/pkg_patch.py 精确字符串补丁 PEG文字替换
pkg_multi_patch.py ./tools/pkg_multi_patch.py 通用多文件补丁器 批量修改
font_hijack.py ./tools/font_hijack.py 汉字字形注入 字体制作
create_cn_pack.py ./tools/create_cn_pack.py 生成中文汉化包 一键汉化

Java CLI工具(复用KorgPackage库)

文件 路径 说明
KorgPkgCLI.java ./tools/KorgPkgCLI.java 命令行入口
korgpkg/ ./tools/korgpkg/ KorgPackage核心库源码

korgpkg库包含:

  • PackageReader.java - PKG读取器
  • PackageWriter.java - PKG写入器
  • FileChunk.java - 文件块处理
  • HeaderChunk.java - 头部块处理
  • Chunk.java - 基类
  • 其他Chunk类型…

pkg_tool.py 详细用法

# 进入工具目录
cd ./tools/

# 列出所有文件
python3 pkg_tool.py list ../firmware.pkg

# 按关键词过滤
python3 pkg_tool.py list ../firmware.pkg ENG

# 提取单个文件
python3 pkg_tool.py extract ../firmware.pkg "verdana11.BDF" output.BDF

# 分析文件详情
python3 pkg_tool.py analyze ../firmware.pkg "FONTS.PEG"

pkg_modifier.py 详细用法

cd ./tools/

# 测试模式: SOUND → AUDIO
python3 pkg_modifier.py test ../input.pkg ../output.pkg

# 自定义替换
python3 pkg_modifier.py replace ../input.pkg ../output.pkg "Cancel" "取消"

编程接口:

# 导入工具模块
import sys
sys.path.append('./tools')
from pkg_modifier import modify_pkg

def my_modifier(data):
    # 自定义修改逻辑
    return modified_data

modify_pkg('input.pkg', 'output.pkg', 'target_file', my_modifier)

pkg_font_patch.py 详细用法

import sys
sys.path.append('./tools')
from pkg_font_patch import multi_file_patch

# 定义修改函数
def modify_fonts_peg(data):
    # ... 修改逻辑
    return modified_data

def inject_chinese(data):
    # ... 注入汉字
    return modified_data

# 批量修改
patches = [
    ('FONTS.PEG', modify_fonts_peg),
    ('verdana11.BDF', inject_chinese),
]

multi_file_patch('input.pkg', 'output.pkg', patches)

pkg_verify.py 详细用法

# 验证单个包
python3 pkg_verify.py verify firmware.pkg

# 对比两个包
python3 pkg_verify.py compare original.pkg modified.pkg

验证项目:

  • ✅ PKG MD5校验
  • ✅ Chunk结构完整性
  • ✅ 文件MD5验证
  • ✅ ZLIB解压测试
  • ✅ 加密文件识别

测试验证记录

测试固件列表

包名 修改内容 测试目的 结果
fontfix.pkg FONTS.PEG标记 + C→方块 验证外部BDF加载 ✅ 成功
multitest.pkg S/T/Y/L/E/A各改不同图案 识别活跃字体 ✅ verdana11确认
comprehensive.pkg ?→方块 + 添加测/试字形 验证转换路径 ✅ 场景1确认
fontchain.pkg A→方块 验证MEDIA字体 ✅ BDF生效
qmark_test.pkg ?→实心方块 验证问号渲染 ✅ 转换确认

关键测试结论

  1. FONTS.PEG 04b180→ffff80 启用外部BDF加载
  2. verdana11.BDF 是MEDIA界面主要字体
  3. 中文文件名 被OPOS转换为?后渲染
  4. 高位ASCII(0x80-0xFF) 不被转换,可用于劫持

关键警告与注意事项

⚠️ 绝对禁止

  1. 不要修改OPOS文件
    • 路径: /omega_sys/OPOS
    • 类型: ENCRYPTED (16)
    • 后果: 设备变砖
  2. 不要使用KorgPackage GUI重新保存
    • 会触碰OPOS文件
    • 即使不修改也会改变

⚠️ 必须遵守

  1. 字符串替换长度相同

    assert len(old_bytes) == len(new_bytes)
    
  2. BDF高度限制11像素

    BBX 11 11 0 -2  ← 第二个11是高度
    
  3. 刷机前验证

    python3 pkg_verify.py verify output.pkg
    

⚠️ 强烈建议

  1. 保留原始PKG文件备份
  2. 了解PA600恢复刷机方法
  3. 先在非关键设备测试

附录

A. PKG Chunk完整类型表

CHUNK_TYPES = {
    1: "HEADER",
    2: "UPDATE_KERNEL",
    3: "UPDATE_RAMDISK",
    4: "UPDATE_INSTALLER_APP",
    5: "UPDATE_INSTALLER_APP_CONFIG",
    6: "SERVICE_KERNEL",
    7: "SERVICE_RAMDISK",
    8: "SERVICE_APP",
    9: "SERVICE_APP_CONFIG",
    10: "UPDATE_LAUNCHER_APP",
    11: "UPDATE_LAUNCHER_APP_CONFIG",
    12: "MLO",
    13: "UBOOT",
    14: "USER_KERNEL",
    15: "INSTALLER_SCRIPT",
    16: "DIRECTORY",
    17: "FILE",
    18: "LINK",
    19: "ROOT_FS",
}

B. FONTS.PEG字体条目

内部字体 (04 b1 80):
- verdana9/10/11.BDF
- verdanab9/10/11/12/13/19.BDF
- AnMB-11/20.bdf

外部字体 (ff ff 80):
- CK13Small6.bdf
- Kords-14/15.bdf
- Notation-47.bdf

C. 高频汉字映射表

# 基于UI翻译需求的前60个高频字
CHAR_MAP = {
    # 基础操作
    '取': 0xC0, '消': 0xC1, '删': 0xC2, '除': 0xC3,
    '保': 0xC4, '存': 0xC5, '加': 0xC6, '载': 0xC7,
    '退': 0xC8, '出': 0xC9, '返': 0xCA, '回': 0xCB,
    '下': 0xCC, '一': 0xCD, '打': 0xCE, '开': 0xCF,
    # 编辑操作
    '关': 0xD0, '闭': 0xD1, '编': 0xD2, '辑': 0xD3,
    '复': 0xD4, '制': 0xD5, '播': 0xD6, '放': 0xD7,
    '停': 0xD8, '止': 0xD9, '录': 0xDA, '音': 0xDB,
    # 确认操作
    '是': 0xDC, '否': 0xDD, '确': 0xDE, '定': 0xDF,
    # 功能分类
    '风': 0xE0, '格': 0xE1, '色': 0xE2, '歌': 0xE3,
    '曲': 0xE4, '全': 0xE5, '局': 0xE6, '设': 0xE7,
    '置': 0xE8, '菜': 0xE9, '单': 0xEA, '基': 0xEB,
    '础': 0xEC, '量': 0xED, '轨': 0xEE, '道': 0xEF,
    # 效果相关
    '效': 0xF0, '果': 0xF1, '选': 0xF2, '择': 0xF3,
    '错': 0xF4, '误': 0xF5, '警': 0xF6, '告': 0xF7,
}

D. 参考资源

官方资源:

字体资源:

技术标准:

  • BDF格式: X11 Bitmap Distribution Format
  • MIDI格式: Standard MIDI File 1.0

文档版本: 3.0

创建日期: 2025-12-18

最后更新: 2025-12-18 01:29

状态: 技术验证完成,待实施


欢迎指出任何有错误或不够清晰的表达,可以在下面评论区评论。

×

喜欢就点赞,疼爱就打赏

//