fix: enforce native DirectX 9 without deleting wrappers
This commit is contained in:
+61
-12
@@ -79,7 +79,7 @@ class WowSetupTool:
|
||||
self.descriptions = {
|
||||
# Setup & General
|
||||
"autologin": "Adds Turtle AutoLogin account/character shortcuts on the login screen. IMPORTANT: saved passwords are stored locally by AutoLogin. They are encrypted only when Nampower encryption is available, WOW_ENCRYPTION_KEY is configured and encryption remains enabled; otherwise AutoLogin may store them in plaintext. Modernization Tool itself does not save your account name or password in its settings.",
|
||||
"render_directx9": "Uses VanillaFixes with the game's native DirectX 9 renderer. Choose this if you do not want DXVK/Vulkan.",
|
||||
"render_directx9": "Uses VanillaFixes with the game's native DirectX 9 renderer. Existing d3d9.dll/dxvk.conf proxy files are moved to a Modernization Tool backup so they cannot keep DXVK or another D3D9 wrapper active.",
|
||||
"render_dxvk": "Uses VanillaFixes with DXVK, translating DirectX 9 to Vulkan for smoother frame pacing on many modern systems.",
|
||||
|
||||
# Core DLLs
|
||||
@@ -2038,8 +2038,63 @@ oLink.Save
|
||||
and saved.get("rendering_mode") == "dxvk"
|
||||
)
|
||||
|
||||
def _external_renderer_backup_dir(self, target):
|
||||
return os.path.join(
|
||||
target,
|
||||
".modernization_tool",
|
||||
"backups",
|
||||
"external_renderer",
|
||||
)
|
||||
|
||||
def _park_external_renderer_file(self, target, filename):
|
||||
"""Move an unowned D3D9 wrapper out of the game root without deleting it."""
|
||||
source = os.path.join(target, filename)
|
||||
if not os.path.lexists(source):
|
||||
return
|
||||
|
||||
if os.path.islink(source) or not os.path.isfile(source):
|
||||
raise RuntimeError(
|
||||
f"Cannot safely disable the existing {filename}: it is not a regular file."
|
||||
)
|
||||
|
||||
backup_dir = self._external_renderer_backup_dir(target)
|
||||
backup = os.path.join(backup_dir, filename)
|
||||
os.makedirs(backup_dir, exist_ok=True)
|
||||
|
||||
if os.path.isfile(backup):
|
||||
try:
|
||||
same_file = (
|
||||
self._file_sha256(source) == self._file_sha256(backup)
|
||||
)
|
||||
except OSError as exc:
|
||||
raise RuntimeError(
|
||||
f"Could not compare the existing {filename} with its saved backup."
|
||||
) from exc
|
||||
|
||||
if not same_file:
|
||||
raise RuntimeError(
|
||||
f"Cannot safely disable {filename}: a different saved renderer backup already exists at "
|
||||
f"{backup}. Move or rename that backup manually, then try again."
|
||||
)
|
||||
|
||||
try:
|
||||
os.remove(source)
|
||||
except OSError as exc:
|
||||
raise RuntimeError(
|
||||
f"Could not move {filename} out of the game root. Close WoW and any program using it, then try again."
|
||||
) from exc
|
||||
return
|
||||
|
||||
try:
|
||||
os.replace(source, backup)
|
||||
except OSError as exc:
|
||||
raise RuntimeError(
|
||||
f"Could not back up {filename} before disabling the external renderer. "
|
||||
"Close WoW and any program using it, then try again."
|
||||
) from exc
|
||||
|
||||
def configure_dxvk(self, target):
|
||||
"""Manage DXVK transactionally and never delete unowned renderer files."""
|
||||
"""Apply the selected renderer while preserving pre-existing wrapper files."""
|
||||
payload_dir = os.path.join(get_base_path(), "Payload")
|
||||
d3d9_src = os.path.join(payload_dir, "DXVK_Standard", "d3d9.dll")
|
||||
conf_src = os.path.join(payload_dir, "dxvk.conf")
|
||||
@@ -2060,9 +2115,6 @@ oLink.Save
|
||||
+ ", ".join(os.path.basename(path) for path in missing)
|
||||
)
|
||||
|
||||
# Migrate installations created before renderer ownership manifests
|
||||
# existed. A saved DXVK selection is explicit evidence that these
|
||||
# two paths were managed by this tool on the previous Apply.
|
||||
if not managed_files and self._previous_settings_used_dxvk(target):
|
||||
for path in (conf_target, d3d9_target):
|
||||
if os.path.lexists(path):
|
||||
@@ -2085,15 +2137,9 @@ oLink.Save
|
||||
)
|
||||
return
|
||||
|
||||
# New managed installs restore any files that existed before DXVK was
|
||||
# enabled. This means a user's own d3d9.dll/dxvk.conf is never lost.
|
||||
if managed_files:
|
||||
remote_packages.remove_managed_mod(target, managed_id)
|
||||
return
|
||||
|
||||
# Legacy cleanup is allowed only with explicit previous settings proof.
|
||||
# Unknown/manual renderer files are deliberately preserved.
|
||||
if self._previous_settings_used_dxvk(target):
|
||||
elif self._previous_settings_used_dxvk(target):
|
||||
for path in (conf_target, d3d9_target):
|
||||
if os.path.lexists(path):
|
||||
try:
|
||||
@@ -2104,6 +2150,9 @@ oLink.Save
|
||||
"Close WoW and any program using it, then try again."
|
||||
) from exc
|
||||
|
||||
for filename in ("d3d9.dll", "dxvk.conf"):
|
||||
self._park_external_renderer_file(target, filename)
|
||||
|
||||
|
||||
def _managed_dll_entries(self):
|
||||
"""Return every dlls.txt entry owned by this tool."""
|
||||
|
||||
+12
-6
@@ -170,7 +170,7 @@ class DxvkOwnershipTests(unittest.TestCase):
|
||||
with open(os.path.join(payload, "dxvk.conf"), "wb") as handle:
|
||||
handle.write(b"bundled-conf")
|
||||
|
||||
def test_enable_then_disable_restores_preexisting_renderer_files(self):
|
||||
def test_enable_then_disable_parks_preexisting_renderer_files(self):
|
||||
tool = self.make_tool("dxvk")
|
||||
with tempfile.TemporaryDirectory() as bundle, tempfile.TemporaryDirectory() as target:
|
||||
self.make_payload(bundle)
|
||||
@@ -191,12 +191,15 @@ class DxvkOwnershipTests(unittest.TestCase):
|
||||
tool.rendering_mode.set("directx9")
|
||||
tool.configure_dxvk(target)
|
||||
|
||||
with open(d3d9, "rb") as handle:
|
||||
self.assertFalse(os.path.exists(d3d9))
|
||||
self.assertFalse(os.path.exists(conf))
|
||||
backup = tool._external_renderer_backup_dir(target)
|
||||
with open(os.path.join(backup, "d3d9.dll"), "rb") as handle:
|
||||
self.assertEqual(handle.read(), b"user-d3d9")
|
||||
with open(conf, "rb") as handle:
|
||||
with open(os.path.join(backup, "dxvk.conf"), "rb") as handle:
|
||||
self.assertEqual(handle.read(), b"user-conf")
|
||||
|
||||
def test_directx9_preserves_unowned_renderer_files(self):
|
||||
def test_directx9_parks_unowned_renderer_files(self):
|
||||
tool = self.make_tool("directx9")
|
||||
with tempfile.TemporaryDirectory() as target:
|
||||
d3d9 = os.path.join(target, "d3d9.dll")
|
||||
@@ -208,9 +211,12 @@ class DxvkOwnershipTests(unittest.TestCase):
|
||||
|
||||
tool.configure_dxvk(target)
|
||||
|
||||
with open(d3d9, "rb") as handle:
|
||||
self.assertFalse(os.path.exists(d3d9))
|
||||
self.assertFalse(os.path.exists(conf))
|
||||
backup = tool._external_renderer_backup_dir(target)
|
||||
with open(os.path.join(backup, "d3d9.dll"), "rb") as handle:
|
||||
self.assertEqual(handle.read(), b"manual-d3d9")
|
||||
with open(conf, "rb") as handle:
|
||||
with open(os.path.join(backup, "dxvk.conf"), "rb") as handle:
|
||||
self.assertEqual(handle.read(), b"manual-conf")
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user