#!/usr/bin/env python # -*- coding: utf-8 -*- """ Substance Painter 注册表管理器 用于在启动前配置库,关闭后清理库 """ import winreg import re import os class SPRegistryManager: """SP 注册表管理器""" REGISTRY_KEY = r"SOFTWARE\Adobe\Adobe Substance 3D Painter\Shelf\pathInfos" SHELF_KEY = r"SOFTWARE\Adobe\Adobe Substance 3D Painter\Shelf" @staticmethod def normalize_library_name(name): """规范化库名称(小写、下划线、连字符)""" safe_name = name.lower().replace(' ', '_') safe_name = re.sub(r'[^a-z0-9_-]', '', safe_name) return safe_name @staticmethod def get_all_libraries(): """获取所有库配置 Returns: list: [(id, name, path, disabled), ...] """ try: reg_conn = winreg.ConnectRegistry(None, winreg.HKEY_CURRENT_USER) key = winreg.OpenKey(reg_conn, SPRegistryManager.REGISTRY_KEY, 0, winreg.KEY_READ) libraries = [] sub_key_count = winreg.QueryInfoKey(key)[0] for i in range(sub_key_count): sub_key_name = winreg.EnumKey(key, i) sub_key = winreg.OpenKey(reg_conn, f"{SPRegistryManager.REGISTRY_KEY}\\{sub_key_name}", 0, winreg.KEY_READ) try: name = winreg.QueryValueEx(sub_key, "name")[0] path = winreg.QueryValueEx(sub_key, "path")[0] disabled = winreg.QueryValueEx(sub_key, "disabled")[0] libraries.append((int(sub_key_name), name, path, disabled)) except: pass finally: winreg.CloseKey(sub_key) winreg.CloseKey(key) return libraries except Exception as e: print(f"[Registry] Error reading libraries: {e}") return [] @staticmethod def remove_project_libraries(): """删除所有项目库(保留系统库) 系统库:your_assets, starter_assets, system_fonts, user_fonts """ system_libs = ['your_assets', 'starter_assets', 'system_fonts', 'user_fonts'] try: libraries = SPRegistryManager.get_all_libraries() to_remove = [] for lib_id, name, path, disabled in libraries: if name not in system_libs: to_remove.append(lib_id) print(f"[Registry] Will remove: {name} (ID: {lib_id})") if not to_remove: print(f"[Registry] No project libraries to remove") return True # 删除项目库 reg_conn = winreg.ConnectRegistry(None, winreg.HKEY_CURRENT_USER) for lib_id in to_remove: try: winreg.DeleteKey(winreg.HKEY_CURRENT_USER, f"{SPRegistryManager.REGISTRY_KEY}\\{lib_id}") print(f"[Registry] Removed library ID: {lib_id}") except Exception as e: print(f"[Registry] Error removing {lib_id}: {e}") # 重新编号剩余的库(确保连续) SPRegistryManager._reindex_libraries() return True except Exception as e: print(f"[Registry] Error removing libraries: {e}") return False @staticmethod def _reindex_libraries(): """重新编号库,确保 ID 连续""" try: libraries = SPRegistryManager.get_all_libraries() libraries.sort(key=lambda x: x[0]) # 按 ID 排序 reg_conn = winreg.ConnectRegistry(None, winreg.HKEY_CURRENT_USER) # 如果已经连续,不需要重新编号 expected_ids = list(range(1, len(libraries) + 1)) actual_ids = [lib[0] for lib in libraries] if expected_ids == actual_ids: print(f"[Registry] Library IDs are already continuous") return print(f"[Registry] Reindexing libraries...") # 先将所有库移到临时 ID temp_offset = 1000 for lib_id, name, path, disabled in libraries: old_key_path = f"{SPRegistryManager.REGISTRY_KEY}\\{lib_id}" new_key_path = f"{SPRegistryManager.REGISTRY_KEY}\\{lib_id + temp_offset}" # 读取旧值 old_key = winreg.OpenKey(reg_conn, old_key_path, 0, winreg.KEY_READ) name_val = winreg.QueryValueEx(old_key, "name")[0] path_val = winreg.QueryValueEx(old_key, "path")[0] disabled_val = winreg.QueryValueEx(old_key, "disabled")[0] winreg.CloseKey(old_key) # 创建新键 new_key = winreg.CreateKey(winreg.HKEY_CURRENT_USER, new_key_path) winreg.SetValueEx(new_key, "name", 0, winreg.REG_SZ, name_val) winreg.SetValueEx(new_key, "path", 0, winreg.REG_SZ, path_val) winreg.SetValueEx(new_key, "disabled", 0, winreg.REG_SZ, disabled_val) winreg.CloseKey(new_key) # 删除旧键 winreg.DeleteKey(winreg.HKEY_CURRENT_USER, old_key_path) # 再将临时 ID 移到正确的连续 ID for new_id, (old_id, name, path, disabled) in enumerate(libraries, start=1): temp_id = old_id + temp_offset temp_key_path = f"{SPRegistryManager.REGISTRY_KEY}\\{temp_id}" final_key_path = f"{SPRegistryManager.REGISTRY_KEY}\\{new_id}" # 读取临时键 temp_key = winreg.OpenKey(reg_conn, temp_key_path, 0, winreg.KEY_READ) name_val = winreg.QueryValueEx(temp_key, "name")[0] path_val = winreg.QueryValueEx(temp_key, "path")[0] disabled_val = winreg.QueryValueEx(temp_key, "disabled")[0] winreg.CloseKey(temp_key) # 创建最终键 final_key = winreg.CreateKey(winreg.HKEY_CURRENT_USER, final_key_path) winreg.SetValueEx(final_key, "name", 0, winreg.REG_SZ, name_val) winreg.SetValueEx(final_key, "path", 0, winreg.REG_SZ, path_val) winreg.SetValueEx(final_key, "disabled", 0, winreg.REG_SZ, disabled_val) winreg.CloseKey(final_key) # 删除临时键 winreg.DeleteKey(winreg.HKEY_CURRENT_USER, temp_key_path) # 更新 size key = winreg.OpenKeyEx(reg_conn, SPRegistryManager.REGISTRY_KEY, 0, winreg.KEY_SET_VALUE) winreg.SetValueEx(key, "size", 0, winreg.REG_DWORD, len(libraries)) winreg.CloseKey(key) print(f"[Registry] Reindexing complete, new size: {len(libraries)}") except Exception as e: print(f"[Registry] Error reindexing: {e}") import traceback traceback.print_exc() @staticmethod def add_project_library(library_name, library_path, set_as_default=True): """添加项目库 Args: library_name: 库名称 library_path: 库路径 set_as_default: 是否设置为默认 Returns: bool: 是否成功 """ try: safe_name = SPRegistryManager.normalize_library_name(library_name) normalized_path = library_path.replace('\\', '/') print(f"[Registry] Adding library: {safe_name}") print(f"[Registry] Path: {normalized_path}") # 检查是否已存在 libraries = SPRegistryManager.get_all_libraries() for lib_id, name, path, disabled in libraries: if name == safe_name: print(f"[Registry] Library already exists: {name}") return True # 找到下一个 ID if libraries: next_id = max(lib[0] for lib in libraries) + 1 else: next_id = 1 # 创建新库 reg_conn = winreg.ConnectRegistry(None, winreg.HKEY_CURRENT_USER) new_key_path = f"{SPRegistryManager.REGISTRY_KEY}\\{next_id}" new_key = winreg.CreateKey(winreg.HKEY_CURRENT_USER, new_key_path) winreg.SetValueEx(new_key, "name", 0, winreg.REG_SZ, safe_name) winreg.SetValueEx(new_key, "path", 0, winreg.REG_SZ, normalized_path) winreg.SetValueEx(new_key, "disabled", 0, winreg.REG_SZ, "false") winreg.CloseKey(new_key) # 更新 size key = winreg.OpenKeyEx(reg_conn, SPRegistryManager.REGISTRY_KEY, 0, winreg.KEY_SET_VALUE) winreg.SetValueEx(key, "size", 0, winreg.REG_DWORD, len(libraries) + 1) winreg.CloseKey(key) print(f"[Registry] Library added with ID: {next_id}") # 设置为默认库 if set_as_default: shelf_key = winreg.OpenKeyEx(reg_conn, SPRegistryManager.SHELF_KEY, 0, winreg.KEY_SET_VALUE) winreg.SetValueEx(shelf_key, "writableShelf", 0, winreg.REG_SZ, safe_name) winreg.CloseKey(shelf_key) print(f"[Registry] Set as default library") return True except Exception as e: print(f"[Registry] Error adding library: {e}") import traceback traceback.print_exc() return False @staticmethod def needs_restart(): """检查是否需要重启 SP 以应用更改 Returns: bool: 是否需要重启 """ # 如果 SP 正在运行,注册表更改需要重启才能生效 import psutil for proc in psutil.process_iter(['name']): if 'Adobe Substance 3D Painter' in proc.info['name']: return True return False