Files
NexusLauncher/plugins/substancepainter/registry_manager.py
2025-11-23 20:41:50 +08:00

253 lines
10 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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