diff --git a/functions.py b/functions.py index 32aa376..f7fb342 100644 --- a/functions.py +++ b/functions.py @@ -40,16 +40,16 @@ def todo_function(): return -def check_eeprom_writeable(serial_port: serial.Serial, offset: int, extra: int) -> bool: +def check_eeprom_writeable(serial_port: serial.Serial, addr: int) -> bool: # 读取原始数据 - read_data = serial_utils.read_extra_eeprom(serial_port, offset, extra, 8) + read_data = serial_utils.read_extra_eeprom(serial_port, addr, 8) # 写入随机数据 random_bytes = bytes([random.randint(0, 255) for _ in range(8)]) - serial_utils.write_extra_eeprom(serial_port, offset, extra, random_bytes) + serial_utils.write_extra_eeprom(serial_port, addr, random_bytes) # 读取写入的数据 - read_write_data = serial_utils.read_extra_eeprom(serial_port, offset, extra, 8) + read_write_data = serial_utils.read_extra_eeprom(serial_port, addr, 8) # 恢复原始数据 - serial_utils.write_extra_eeprom(serial_port, offset, extra, read_data) + serial_utils.write_extra_eeprom(serial_port, addr, read_data) return read_write_data == random_bytes @@ -72,7 +72,7 @@ def check_serial_port(serial_port: serial.Serial, for i in range(1, 5): # 128 KiB offset 0x1, 256 KiB offset 0x3, 384 KiB offset 0x5, 512 KiB offset 0x7 # 1 -> 0x1, 2 -> 0x3, 3 -> 0x5, 4 -> 0x7 符合 2n-1 - if check_eeprom_writeable(serial_port, 2 * i - 1, 0x8000): + if check_eeprom_writeable(serial_port, (2 * i - 1) * 0x10000 + 0x8000): eeprom_size = i else: break @@ -114,7 +114,6 @@ def write_data(serial_port: Serial, start_addr: int, data: Union[bytes, List[int total_page = data_len // 128 addr = start_addr current_step = 0 - offset = 0 while addr < start_addr + data_len: percent_float = (current_step / total_page) * 100 percent = int(percent_float) @@ -127,9 +126,7 @@ def write_data(serial_port: Serial, start_addr: int, data: Union[bytes, List[int if start_addr + data_len < 0x10000: serial_utils.write_eeprom(serial_port, addr, writing_data) else: - if addr - offset * 0x10000 >= 0x10000: - offset += 1 - serial_utils.write_extra_eeprom(serial_port, offset, addr - offset * 0x10000, writing_data) + serial_utils.write_extra_eeprom(serial_port, addr, writing_data) addr += step current_step += 1 progress['value'] = 0 @@ -394,18 +391,16 @@ def auto_write_font(serial_port_text: str, window: tk.Tk, progress: ttk.Progress log(f'正在进行 3/{n}: 写入亚音参数') write_tone_options(serial_port_text, window, progress, status_label, eeprom_size, firmware_version, True) if n == 4: - log(f'正在进行 4/{n}: 写入拼音检索表') + log(f'正在进行 4/4: 写入拼音检索表') if version_number == 123: write_pinyin_index(serial_port_text, window, progress, status_label, eeprom_size, firmware_version, True) elif version_number > 123: write_pinyin_index(serial_port_text, window, progress, status_label, eeprom_size, firmware_version, True, True) - reset_radio(serial_port_text, status_label) - messagebox.showinfo('提示', f'{version_number}{version_code}版本字库\n字库配置\n亚音参数\n拼音检索表\n写入成功!') - else: - reset_radio(serial_port_text, status_label) - messagebox.showinfo('提示', f'{version_number}{version_code}版本字库\n字库配置\n亚音参数\n写入成功!') + reset_radio(serial_port_text, status_label) + extra_msg = '拼音检索表\n' if n == 4 else '' + messagebox.showinfo('提示', f'{version_number}{version_code}版本字库\n字库配置\n亚音参数\n{extra_msg}写入成功!') else: messagebox.showinfo('提示', f'非LOSEHU扩容固件,无法写入') @@ -636,3 +631,111 @@ def write_pinyin_index(serial_port_text: str, window: tk.Tk, progress: ttk.Progr serial_utils.reset_radio(serial_port) messagebox.showinfo('提示', '写入拼音检索表成功!') status_label['text'] = '当前操作: 无' + + +def backup_eeprom(serial_port_text: str, window: tk.Tk, progress: ttk.Progressbar, + status_label: tk.Label, eeprom_size: int): + log('开始备份') + log('选择的串口: ' + serial_port_text) + status_label['text'] = f'当前操作: 备份eeprom' + if len(serial_port_text) == 0: + log('没有选择串口!') + messagebox.showerror('错误', '没有选择串口!') + status_label['text'] = '当前操作: 无' + return + + if eeprom_size > 0: + target_eeprom_offset = 0x20000 * eeprom_size + else: + target_eeprom_offset = 0x2000 + + with serial.Serial(serial_port_text, 38400, timeout=2) as serial_port: + serial_check = check_serial_port(serial_port, False) + if not serial_check.status: + messagebox.showerror('错误', serial_check.message) + status_label['text'] = '当前操作: 无' + return + + start_addr = 0x0 # 起始地址为0x0 + total_steps = (target_eeprom_offset - start_addr) // 128 # 计算总步数 + current_step = 0 + addr = start_addr + + backup_data = b'' + + while addr < target_eeprom_offset: # 限制地址范围为start_addr到target_eeprom_offset + if target_eeprom_offset < 0x10000: + read_data = serial_utils.read_eeprom(serial_port, addr, 128) + else: + read_data = serial_utils.read_extra_eeprom(serial_port, addr, 128) + backup_data += read_data + addr += 128 + current_step += 1 + percent_float = (current_step / total_steps) * 100 + log(f'进度: {percent_float:.1f}%, addr={hex(addr)}', '') + progress['value'] = percent_float + window.update() + + # 弹出文件保存对话框 + file_path = filedialog.asksaveasfilename(defaultextension=".bin", + filetypes=[("Binary files", "*.bin"), ("All files", "*.*")]) + + if not file_path: + log('用户取消保存') + messagebox.showinfo('提示', '用户取消保存') + return # 用户取消保存,直接返回 + + with open(file_path, 'wb') as fp: + fp.write(backup_data) + + log('EEPROM 备份完成') + status_label['text'] = '当前操作: 无' + messagebox.showinfo('提示', '保存成功!') + + +def restore_eeprom(serial_port_text: str, window: tk.Tk, progress: ttk.Progressbar, + status_label: tk.Label, eeprom_size: int): + log('开始恢复eeprom') + log('选择的串口: ' + serial_port_text) + status_label['text'] = f'当前操作: 恢复eeprom' + if len(serial_port_text) == 0: + log('没有选择串口!') + messagebox.showerror('错误', '没有选择串口!') + status_label['text'] = '当前操作: 无' + return + + start_addr = 0x0 + if eeprom_size > 0: + target_eeprom_offset = 0x20000 * eeprom_size + else: + target_eeprom_offset = 0x2000 + + file_path = filedialog.askopenfilename(filetypes=[("Binary files", "*.bin"), ("All files", "*.*")]) + + if not file_path: + log('用户取消选择') + messagebox.showinfo('提示', '用户取消选择') + return # 用户取消选择,直接返回 + + with open(file_path, 'rb') as fp: + restore_data = fp.read() + + if len(restore_data) != target_eeprom_offset: + if messagebox.askquestion('提示', f'选择的文件大小为{len(restore_data)},但目标eeprom大小为{target_eeprom_offset},是否继续?') == 'no': + log('用户取消恢复') + messagebox.showinfo('提示', '用户取消恢复') + return # 用户取消恢复,直接返回 + + with serial.Serial(serial_port_text, 38400, timeout=2) as serial_port: + serial_check = check_serial_port(serial_port, False) + if not serial_check.status: + messagebox.showerror('错误', serial_check.message) + status_label['text'] = '当前操作: 无' + return + + write_data(serial_port, start_addr, restore_data, progress, window) + + log('EEPROM恢复完成') + status_label['text'] = '当前操作: 无' + serial_utils.reset_radio(serial_port) + messagebox.showinfo('提示', '写入成功!') \ No newline at end of file diff --git a/main.py b/main.py index d4acc24..124acdc 100644 --- a/main.py +++ b/main.py @@ -19,11 +19,13 @@ read_config, write_config, write_pinyin_index, - todo_function + todo_function, + backup_eeprom, + restore_eeprom ) window = ttk.Window() -version = '0.5' +version = '0.6' appdata_path = os.getenv('APPDATA') if os.getenv('APPDATA') is not None else '' config_dir = os.path.join(appdata_path, 'K5_Tools') @@ -364,26 +366,30 @@ def main(): width=14, command=lambda: write_pinyin_index( serial_port_combo.get(), window, progress, label2, - EEPROM_SIZE.index(eeprom_size_combo.get()), FIRMWARE_VERSION_LIST.index(firmware_combo.get()), True + EEPROM_SIZE.index(eeprom_size_combo.get()), FIRMWARE_VERSION_LIST.index(firmware_combo.get()), False, True ) ) write_pinyin_new_index_button.pack(side='left', padx=3, pady=(2, 15)) - todo_button = tk.Button( + backup_eeprom_button = tk.Button( frame7, - text=translations[language]['todo_button_text'], + text='备份EEPROM', width=14, - command=todo_function + command=lambda:backup_eeprom( + serial_port_combo.get(), window, progress, label2,EEPROM_SIZE.index(eeprom_size_combo.get()) + ) ) - todo_button.pack(side='left', padx=3, pady=(2, 15)) + backup_eeprom_button.pack(side='left', padx=3, pady=(2, 15)) - todo_button = tk.Button( + restore_eeprom_button = tk.Button( frame7, - text=translations[language]['todo_button_text'], + text='恢复EEPROM', width=14, - command=todo_function + command=lambda:restore_eeprom( + serial_port_combo.get(), window, progress, label2,EEPROM_SIZE.index(eeprom_size_combo.get()) + ) ) - todo_button.pack(side='left', padx=3, pady=(2, 15)) + restore_eeprom_button.pack(side='left', padx=3, pady=(2, 15)) todo_button = tk.Button( frame7, @@ -412,24 +418,6 @@ def main(): log('所有操作均有一定的风险,请确保您已备份校准等文件!!!\n') # 在此统一设置tooltip - #Tooltip(serial_port_combo, "点击选择K5/K6所在串口") - #Tooltip(eeprom_size_combo, "EEPROM芯片容量,若自动检测正确则无需修改") - #Tooltip(firmware_combo, "固件版本,若自动检测正确则无需修改") - #Tooltip(clean_eeprom_button, "清除EEPROM中的所有数据") - #Tooltip(auto_write_font_button, "自动写入机器固件所需字库等文件,如不清楚点那个按钮的情况下,点这个总没错") - #Tooltip(read_calibration_button, "校准文件包含硬件参数校准信息,必须备份!建议终身保留以备恢复") - #Tooltip(write_calibration_button, "校准文件只在更换芯片或清除EEPROM数据后写一次即可") - #Tooltip(read_config_button, "配置文件包含了菜单设置信息、开机字符和信道信息等,如无特别需要可不备份,不同固件的菜单设置可能不通用") - #Tooltip(write_config_button, "配置文件如无特别需要,可以不写") - #Tooltip(write_font_conf_button, "写入字库配置,如果不使用自动写入,请在执行完字库写入后点击") - #Tooltip(write_tone_options_button, "写入亚音参数,如果不使用自动写入,请在执行完字库写入后点击") - #Tooltip(write_font_compressed_button, "压缩GB2312字库,萝狮虎118K、123H版本及后续版本使用") - #Tooltip(write_font_uncompressed_button, "全量GB2312字库,用于萝狮虎118H版本,后续未使用") - #Tooltip(write_font_old_button, "萝狮虎117版本及之前版本使用,旧字库") - #Tooltip(write_pinyin_old_index_button, "123版本拼音索引,如果不使用自动写入,请在执行完字库写入后点击") - #Tooltip(write_pinyin_new_index_button, "124及以上版本拼音索引,如果不使用自动写入,请在执行完字库写入后点击") - #Tooltip(language_combo, "Change language.") - Tooltip(language_combo, translations[language]['language_combo_tooltip_text']) Tooltip(eeprom_size_combo, translations[language]['eeprom_size_combo_tooltip_text']) Tooltip(firmware_combo, translations[language]['firmware_combo_tooltip_text']) @@ -447,6 +435,9 @@ def main(): Tooltip(write_font_old_button, translations[language]['write_font_old_button_tooltip_text']) Tooltip(write_pinyin_old_index_button, translations[language]['write_pinyin_old_index_button_tooltip_text']) Tooltip(write_pinyin_new_index_button, translations[language]['write_pinyin_new_index_button_tooltip_text']) + Tooltip(backup_eeprom_button, "备份EEPROM中的数据,使用EEPROM下拉框可以选择所要备份的大小") + Tooltip(restore_eeprom_button, "恢复EEPROM中的数据,使用EEPROM下拉框可以选择所要恢复的大小") + Tooltip(todo_button, "敬请期待") window.mainloop() diff --git a/poetry.lock b/poetry.lock index b31109b..0b6fb9d 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,23 +1,5 @@ # This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. -[[package]] -name = "importlib-resources" -version = "6.1.1" -description = "Read resources from Python packages" -optional = false -python-versions = ">=3.8" -files = [ - {file = "importlib_resources-6.1.1-py3-none-any.whl", hash = "sha256:e8bf90d8213b486f428c9c39714b920041cb02c184686a3dee24905aaa8105d6"}, - {file = "importlib_resources-6.1.1.tar.gz", hash = "sha256:3893a00122eafde6894c59914446a512f728a0c1a45f9bb9b63721b6bacf0b4a"}, -] - -[package.dependencies] -zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""} - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] -testing = ["pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-ruff", "zipp (>=3.17)"] - [[package]] name = "pillow" version = "10.2.0" @@ -131,22 +113,7 @@ files = [ [package.dependencies] pillow = ">=8.2.0" -[[package]] -name = "zipp" -version = "3.17.0" -description = "Backport of pathlib-compatible object wrapper for zip files" -optional = false -python-versions = ">=3.8" -files = [ - {file = "zipp-3.17.0-py3-none-any.whl", hash = "sha256:0e923e726174922dce09c53c59ad483ff7bbb8e572e00c7f7c46b88556409f31"}, - {file = "zipp-3.17.0.tar.gz", hash = "sha256:84e64a1c28cf7e91ed2078bb8cc8c259cb19b76942096c8d7b84947690cabaf0"}, -] - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] -testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy (>=0.9.1)", "pytest-ruff"] - [metadata] lock-version = "2.0" python-versions = "^3.8" -content-hash = "23ca9cff2b2dfe38da366682dee8c7ef088bd7e2b3f7176bfc45a0b79c0bd0f8" +content-hash = "96dd4c3ccedba514555842eb84d6456644585dab2e0a24c1e8ff53099ca156af" diff --git a/pyproject.toml b/pyproject.toml index c1bed7a..0e7c9ae 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "k5-tools" -version = "0.5" +version = "0.6" description = "" authors = ["hank9999 "] readme = "README.md" @@ -8,7 +8,6 @@ readme = "README.md" [tool.poetry.dependencies] python = "^3.8" pyserial = "^3.5" -importlib-resources = "^6.1.1" ttkbootstrap = "^1.10.1" [build-system] diff --git a/requirements.txt b/requirements.txt index bbbdafd..cf29944 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,2 @@ pyserial>=3.5 -ttkbootstrap>=1.10.1 -importlib-resources>=6.1.1 \ No newline at end of file +ttkbootstrap>=1.10.1 \ No newline at end of file diff --git a/serial_utils.py b/serial_utils.py index 13ee5eb..fe6cdab 100644 --- a/serial_utils.py +++ b/serial_utils.py @@ -102,10 +102,9 @@ def read_eeprom(serial_port: serial.Serial, offset: int, length: int): return o[8:] -def read_extra_eeprom(serial_port: serial.Serial, offset: int, extra: int, length: int): - if extra > 0xFFFF: - offset = extra >> 16 - extra = extra & 0xFFFF +def read_extra_eeprom(serial_port: serial.Serial, addr: int, length: int): + offset = addr >> 16 + extra = addr & 0xFFFF extra_bytes = struct.pack(" 0xFFFF: - offset = extra >> 16 - extra = extra & 0xFFFF +def write_extra_eeprom(serial_port: serial.Serial, addr: int, data: bytes): + offset = addr >> 16 + extra = addr & 0xFFFF extra = struct.pack("