From e7c01d096bca42de14545645001c29037e030e0c Mon Sep 17 00:00:00 2001 From: Dusk-92 Date: Fri, 28 Aug 2026 11:04:40 +0200 Subject: [PATCH 1/6] docs: add project README --- README.md | 299 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 299 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..a439c27 --- /dev/null +++ b/README.md @@ -0,0 +1,299 @@ +# 🛠️ WoW Modernization Tool + +[![Build Windows EXE](https://github.com/Dusk-92/Modernization-Tool/actions/workflows/build.yml/badge.svg)](https://github.com/Dusk-92/Modernization-Tool/actions/workflows/build.yml) +[![Latest Release](https://img.shields.io/github/v/release/Dusk-92/Modernization-Tool)](https://github.com/Dusk-92/Modernization-Tool/releases/latest) + +A simple all-in-one setup tool for **Vanilla WoW 1.12 compatible clients**. + +Modernization Tool installs and configures client fixes, performance plugins, Vanilla Tweaks, DXVK and optional visual/audio improvements without requiring users to manually manage DLLs or configuration files. + +> 💡 Hover over any setting inside the tool to see an explanation of what it does. + +--- + +## ⬇️ Download + +Download the latest version from: + +**[GitHub Releases](https://github.com/Dusk-92/Modernization-Tool/releases/latest)** + +Current major release: **v2.0** + +No installation is required. Download and run: + +`WoW_Modernization_Tool.exe` + +--- + +## 🚀 Quick Start + +1. Launch **WoW_Modernization_Tool.exe** +2. Select your Vanilla WoW game folder. +3. Choose your renderer, plugins and optional tweaks. +4. Click **Apply Setup & Tweaks**. +5. Launch the game using **Play Modernized WoW**. + +The launcher shortcut is automatically created: + +- 📁 inside your WoW folder +- 🖥️ on your Windows Desktop + +> ⚠️ Use **Play Modernized WoW** to launch the modernized client. + +--- + +# 🎮 Rendering + +Two rendering modes are available: + +### VanillaFixes — DirectX 9 + +The default option. + +Uses WoW's native DirectX 9 renderer together with VanillaFixes. + +### VanillaFixes + DXVK — Vulkan + +Uses DXVK to translate DirectX 9 to Vulkan. + +The bundled version is currently: + +**DXVK 2.6.1 x86** + +Existing custom DirectX wrapper files are backed up rather than blindly deleted when switching renderers. + +--- + +# 🧩 Client Plugins + +## Recommended Core + +The tool can install and configure several recommended client extensions: + +- **Nampower** — improves spell responsiveness and latency handling. +- **UnitXP_SP3** — adds networking, targeting, Lua and client improvements. +- **SuperWoW** — expands the Vanilla Lua API and improves addon compatibility. +- **TransmogFix** — prevents performance drops caused by rapid appearance updates. +- **PerfBoost** — provides additional unit rendering/performance controls. +- **WeirdPerformance** — client/runtime optimizations aimed at improving smoothness. +- **VanillaHelpers** — extends several Vanilla client limits. +- **ClassicAPI** — adds newer WoW API functions for compatible addons. +- **AuctionQueryThrottle** — removes the fixed delay between Auction House queries. + +## Optional Plugins + +Optional client-side improvements include: + +- **VanillaMultiMonitorFix** — improved resolution, refresh-rate and monitor detection. +- **Interact** — adds a modern Interact key for NPCs, objects, gathering nodes and loot. +- **No1600x1200** +- **BigCursor** +- **CustomAssets** +- **LogSessions** +- **MinimapIcons** +- **PNG Screenshots** +- **WorldMarkers** + +--- + +# ⚙️ Vanilla Tweaks + +Modernization Tool integrates the modern **tubtubs/vanilla-tweaks** patcher. + +Available settings include: + +- Field of View +- Render Distance +- Ground Clutter Distance +- Nameplate Distance +- Camera Distance +- Sound Channels +- Always Auto-Loot +- Background Sounds +- Large Address Aware +- Camera Skip Fix +- DEP compatibility +- Unlimited AddOn Script Memory +- Cross-Faction Resurrection Fix +- Custom Glues Patch +- Automatic WDB management + +When **SuperWoW** already provides a feature, the equivalent vanilla-tweaks patch is automatically skipped to avoid duplicate modifications. + +--- + +# 🌙 Visual Mods + +Optional visual modifications are available directly from the tool: + +- **Bluemoon Patch** — restores the rare blue moon effect. +- **Darker Nights** — darker and more atmospheric nights. +- **Pretty Night Sky** — improved starry night sky. +- **Epoch Water** — alternative water textures. +- **Fog Pushback** — moves environmental fog farther away. +- **Pink Herbs** — makes herb nodes much easier to spot. + +All visual mods are **disabled by default**. + +--- + +# 🔊 Audio Mods + +Optional sound replacements include: + +- **NoErrorSounds** — removes repetitive error/fizzle and interface sounds. +- **FishPing** — replaces the fishing bite sound with a clearer ping. +- **Warlock Muted Demons** — mutes repetitive Warlock demon voice lines. + +All audio mods are **disabled by default**. + +--- + +# 🔄 Automatic Updates & Offline Fallbacks + +Supported components are downloaded directly from their upstream projects when possible. + +Modernization Tool is designed to avoid breaking an existing installation if an online source becomes unavailable. + +Depending on the component, the tool can: + +- keep an already installed valid version; +- use a bundled known-good fallback; +- avoid re-downloading files that are already current. + +Downloaded DLLs are also validated before installation. + +--- + +# 💾 Safe Configuration Management + +Modernization Tool remembers settings separately for each WoW installation. + +It also: + +- preserves manually added `dlls.txt` entries; +- preserves comments and unknown DLL entries; +- detects incompatible plugin combinations; +- validates the selected Vanilla client before modifying it; +- performs important file replacements transactionally; +- protects unknown/custom renderer files; +- safely manages WDB ownership. + +--- + +# 🔐 AutoLogin + +AutoLogin can optionally save account and character shortcuts on the login screen. + +When **AutoLogin + Nampower** are enabled together, Modernization Tool automatically creates or reuses the Windows user encryption key required by Nampower. + +Password encryption is enabled by default when available. + +Existing encryption keys are never replaced. + +> 🔒 Your encryption key is stored in the Windows user environment and is not written to the WoW folder. + +--- + +# 📦 Current Notable Versions + +| Component | Version | +|---|---| +| VanillaFixes | 1.5.3 | +| DXVK | 2.6.1 x86 | +| Nampower | 4.6.1 | +| ClassicAPI | 1.12.7 | +| AuctionQueryThrottle | 1.2.0 | +| UnitXP_SP3 | v90 | +| SuperWoW | 2.2 | +| VanillaHelpers | 1.1.2 | +| WeirdPerformance | 0.7.3 | +| WorldMarkers | 0.7.1 | +| VanillaMultiMonitorFix | 0.2 | +| Interact | 1.0.4 | + +Where supported, the tool will attempt to obtain the latest compatible upstream version automatically. + +--- + +# 📝 Updating Modernization Tool + +Updating from an older version does **not** require reinstalling WoW. + +Simply: + +1. Download the latest Modernization Tool release. +2. Select your existing WoW folder. +3. Review your settings. +4. Click **Apply Setup & Tweaks** again. + +Your per-installation settings will be restored automatically. + +--- + +# 🛡️ Antivirus Notice + +Because Modernization Tool modifies a game executable and installs client-side DLL plugins, some antivirus products may flag the executable or some included components. + +Always download Modernization Tool from the official GitHub Releases page. + +--- + +# 🔗 Useful Links + +- **[Latest Release](https://github.com/Dusk-92/Modernization-Tool/releases/latest)** +- **[v2.0 Release Notes](https://github.com/Dusk-92/Modernization-Tool/releases/tag/v2.0)** +- **[OctoWoW Installation & Modernization Guide](https://octowow.st/forum/viewtopic.php?t=831)** + +--- + +# ❤️ Credits + +Modernization Tool brings together work from several community projects and developers. + +## Rendering & Client Patching + +- [VanillaFixes](https://github.com/hannesmann/vanillafixes) +- [DXVK](https://github.com/doitsujin/dxvk) +- [vanilla-tweaks](https://github.com/tubtubs/vanilla-tweaks) + +## Core Engine & API Plugins + +- [VanillaHelpers](https://github.com/isfir/VanillaHelpers) +- [PerfBoost Settings](https://gitea.com/avitasia/PerfBoostSettings) +- [UnitXP_SP3](https://github.com/brues-code/UnitXP_SP3) +- [SuperWoW](https://github.com/balakethelock/SuperWoW) +- [SuperAPI](https://github.com/balakethelock/SuperAPI) +- [ClassicAPI](https://github.com/brues-code/ClassicAPI) +- [AuctionQueryThrottle](https://github.com/brues-code/AuctionQueryThrottle) +- [Nampower](https://github.com/brues-code/nampower) +- [NampowerSettings](https://github.com/brues-code/NampowerSettings) +- [No1600x1200](https://github.com/RetroCro/TurtleWoW-Mods#no1600x1200) +- [VanillaMultiMonitorFix](https://github.com/Mates1500/VanillaMultiMonitorFix) +- [Interact](https://github.com/lookino/Interact) + +## Visual & Audio Mods + +- [Bluemoon Patch via vanilla-tweaks](https://github.com/tubtubs/vanilla-tweaks) +- [Darker Nights — Project Reforged](https://projectreforged.github.io/vanilla/downloads/turtle/) +- [Pretty Night Sky / Epoch Water / Fog Pushback — RetroCro TurtleWoW Mods](https://github.com/RetroCro/TurtleWoW-Mods) +- [Pink Herbs](https://github.com/seacrabsam/patch-herb) +- [NoErrorSounds](https://github.com/Macumbafeh/NoErrorSounds) +- [FishPing](https://github.com/notsureawake/FishPing) +- [Warlock Muted Demons](https://github.com/spzilyk/Warlock-Muted-Demons) +- [Automatic WDB management guide](https://github.com/RetroCro/TurtleWoW-Mods#automatically-clear-wdb-folder-every-time-you-launch-turtle-wow) + +## Other Bundled Enhancements + +- [Vanilla AutoLogin](https://github.com/MarcelineVQ/turtle-autologin) +- [WeirdUtils](https://codeberg.org/MarcelineVQ/WeirdUtils) + +Additional attribution and source links are available directly in the **Credits** tab of Modernization Tool. + +--- + +## Disclaimer + +Modernization Tool is a community project and is not affiliated with or endorsed by Blizzard Entertainment. + +World of Warcraft and Warcraft are trademarks of Blizzard Entertainment. -- 2.52.0 From 4bc60a3a6a0dde0d20a92e795b074ef8360807d0 Mon Sep 17 00:00:00 2001 From: Dusk-92 Date: Fri, 28 Aug 2026 11:07:58 +0200 Subject: [PATCH 2/6] docs: describe optional WeirdUtils plugins --- README.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index a439c27..970949f 100644 --- a/README.md +++ b/README.md @@ -75,7 +75,7 @@ The tool can install and configure several recommended client extensions: - **SuperWoW** — expands the Vanilla Lua API and improves addon compatibility. - **TransmogFix** — prevents performance drops caused by rapid appearance updates. - **PerfBoost** — provides additional unit rendering/performance controls. -- **WeirdPerformance** — client/runtime optimizations aimed at improving smoothness. +- **WeirdPerformance** — lightweight client optimizations aimed at improving FPS and reducing UI memory leaks for smoother gameplay. - **VanillaHelpers** — extends several Vanilla client limits. - **ClassicAPI** — adds newer WoW API functions for compatible addons. - **AuctionQueryThrottle** — removes the fixed delay between Auction House queries. @@ -86,13 +86,13 @@ Optional client-side improvements include: - **VanillaMultiMonitorFix** — improved resolution, refresh-rate and monitor detection. - **Interact** — adds a modern Interact key for NPCs, objects, gathering nodes and loot. -- **No1600x1200** -- **BigCursor** -- **CustomAssets** -- **LogSessions** -- **MinimapIcons** -- **PNG Screenshots** -- **WorldMarkers** +- **No1600x1200** — fixes the old Vanilla resolution limitation on some systems. +- **BigCursor** — upscales the hardware cursor for better visibility on modern high-resolution displays without sacrificing sharpness. +- **CustomAssets** — allows loose game files to be loaded directly from the `Data/` folder and supports custom multi-character patch names without repacking MPQ archives. +- **LogSessions** — automatically organizes combat and chat logs into clean per-character, per-day files when you log in. +- **MinimapIcons** — adds TBC/WotLK-style minimap tracking icons for NPCs and game objects, with a combined tracking menu and saved preferences. +- **PNG Screenshots** — saves screenshots as compressed PNG files instead of the default uncompressed TGA format, using background processing to minimize frame-time impact. +- **WorldMarkers** — lets party or raid leaders place up to five animated, Cataclysm-style colored world markers for positioning and tactical planning. --- @@ -286,7 +286,7 @@ Modernization Tool brings together work from several community projects and deve ## Other Bundled Enhancements - [Vanilla AutoLogin](https://github.com/MarcelineVQ/turtle-autologin) -- [WeirdUtils](https://codeberg.org/MarcelineVQ/WeirdUtils) +- [WeirdUtils](https://codeberg.org/Dusk92/WeirdUtils) Additional attribution and source links are available directly in the **Credits** tab of Modernization Tool. -- 2.52.0 From e01cdd967e961cc0cc9c8b0f4a33002e2e9e44f3 Mon Sep 17 00:00:00 2001 From: Dusk-92 Date: Fri, 28 Aug 2026 18:08:35 +0200 Subject: [PATCH 3/6] fix: support spaces in elevated DEP script paths --- setup_tool.py | 8 +++---- tests/test_safety.py | 53 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 4 deletions(-) diff --git a/setup_tool.py b/setup_tool.py index 5174f68..42e0e71 100644 --- a/setup_tool.py +++ b/setup_tool.py @@ -1792,12 +1792,12 @@ class WowSetupTool: escaped_script = script_path.replace("'", "''") launcher = ( "$ErrorActionPreference='Stop'; " + f"$scriptPath = '{escaped_script}'; " + "$argumentLine = '-NoProfile -NonInteractive -ExecutionPolicy Bypass -File \"' " + "+ $scriptPath + '\"'; " "try { " "$p = Start-Process -FilePath 'powershell.exe' -Verb RunAs " - "-Wait -PassThru -ArgumentList @(" - "'-NoProfile','-NonInteractive','-ExecutionPolicy','Bypass'," - f"'-File','{escaped_script}'" - "); " + "-Wait -PassThru -ArgumentList $argumentLine; " "exit $p.ExitCode " "} catch { Write-Error $_; exit 1 }" ) diff --git a/tests/test_safety.py b/tests/test_safety.py index 9cc284d..6cc2305 100644 --- a/tests/test_safety.py +++ b/tests/test_safety.py @@ -468,6 +468,59 @@ class AutoLoginEncryptionTests(unittest.TestCase): os.environ["WOW_ENCRYPTION_KEY"] = original +class DepElevationTests(unittest.TestCase): + def test_quotes_script_path_when_game_folder_contains_spaces(self): + tool = WowSetupTool.__new__(WowSetupTool) + + with tempfile.TemporaryDirectory(prefix="01 OctoWoW ") as root: + captured = {} + + def fake_run(args, **kwargs): + captured["args"] = args + script_path = os.path.join( + root, + ".modernization_tool", + "process_mitigation.ps1", + ) + self.assertIn(" ", script_path) + self.assertTrue(os.path.isfile(script_path)) + return mock.Mock(returncode=0, stdout="", stderr="") + + with mock.patch("setup_tool.subprocess.run", side_effect=fake_run): + tool._run_elevated_powershell( + root, + "Set-ProcessMitigation -Name 'WoW_Modernized.exe' " + "-Disable DEP, EmulateAtlThunks", + "Disabling DEP for WoW_Modernized.exe", + ) + + launcher = captured["args"][-1] + escaped_script = os.path.join( + root, + ".modernization_tool", + "process_mitigation.ps1", + ).replace("'", "''") + + self.assertIn( + f"$scriptPath = '{escaped_script}';", + launcher, + ) + self.assertIn( + "'-NoProfile -NonInteractive -ExecutionPolicy Bypass -File \"' " + "+ $scriptPath + '\"'", + launcher, + ) + self.assertFalse( + os.path.exists( + os.path.join( + root, + ".modernization_tool", + "process_mitigation.ps1", + ) + ) + ) + + class PeValidationTests(unittest.TestCase): def write_minimal_x86_pe(self, path): data = bytearray(2048) -- 2.52.0 From 48da2566b0499158c53761488c4065af5440dba7 Mon Sep 17 00:00:00 2001 From: Dusk-92 Date: Fri, 28 Aug 2026 18:10:17 +0200 Subject: [PATCH 4/6] release v2.0 -- 2.52.0 From b0cdc6888d20ec29fda98ab563c0f32778c0fede Mon Sep 17 00:00:00 2001 From: Dusk-92 Date: Fri, 28 Aug 2026 18:28:59 +0200 Subject: [PATCH 5/6] release v2.0 --- .github/workflows/build.yml | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 85b5464..5a5e7fa 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -65,12 +65,23 @@ jobs: if (-not (Test-Path "dist/WoW_Modernization_Tool.exe")) { throw "Build output missing" } Get-Item "dist/WoW_Modernization_Tool.exe" | Format-List Name,Length,LastWriteTime + - name: Create ZIP package + shell: pwsh + run: | + $zip = "dist/WoW_Modernization_Tool.zip" + if (Test-Path $zip) { Remove-Item $zip -Force } + Compress-Archive -Path "dist/WoW_Modernization_Tool.exe" -DestinationPath $zip -CompressionLevel Optimal + if (-not (Test-Path $zip)) { throw "ZIP package missing" } + Get-Item $zip | Format-List Name,Length,LastWriteTime + - name: Upload test artifact if: ${{ !startsWith(github.ref, 'refs/tags/') }} uses: actions/upload-artifact@v4 with: name: WoW_Modernization_Tool-test - path: dist/WoW_Modernization_Tool.exe + path: | + dist/WoW_Modernization_Tool.exe + dist/WoW_Modernization_Tool.zip if-no-files-found: error # One-time production promotion for v2.0. The exact commit message keeps @@ -81,7 +92,9 @@ jobs: with: tag_name: v2.0 target_commitish: ${{ github.sha }} - files: dist/WoW_Modernization_Tool.exe + files: | + dist/WoW_Modernization_Tool.exe + dist/WoW_Modernization_Tool.zip fail_on_unmatched_files: true name: "WoW Modernization Tool v2.0" body_path: RELEASE_NOTES.md @@ -93,7 +106,9 @@ jobs: if: ${{ startsWith(github.ref, 'refs/tags/') }} uses: softprops/action-gh-release@v3 with: - files: dist/WoW_Modernization_Tool.exe + files: | + dist/WoW_Modernization_Tool.exe + dist/WoW_Modernization_Tool.zip fail_on_unmatched_files: true name: "WoW Modernization Tool ${{ github.ref_name }}" body_path: RELEASE_NOTES.md -- 2.52.0 From 2bd21e19151114cc7c24982f67d98bd076c33049 Mon Sep 17 00:00:00 2001 From: Dusk-92 Date: Mon, 31 Aug 2026 10:07:10 +0200 Subject: [PATCH 6/6] Move Pink Herbs to patch-V safely --- remote_packages.py | 105 ++++++++++++++++++++++++++++++++++++++++--- setup_tool.py | 2 +- tests/test_safety.py | 88 ++++++++++++++++++++++++++++++++++++ 3 files changed, 188 insertions(+), 7 deletions(-) diff --git a/remote_packages.py b/remote_packages.py index 4aef108..020d8e3 100644 --- a/remote_packages.py +++ b/remote_packages.py @@ -672,7 +672,7 @@ VISUAL_MOD_REVISIONS = { "visual_pretty_night_sky": "1", "visual_epoch_water": "1", "visual_fog_pushback": "1", - "visual_pink_herbs": "1", + "visual_pink_herbs": "2", } @@ -943,6 +943,74 @@ def _verify_mpq(path): raise RemotePackageError("Downloaded file is not a valid MPQ archive.") + +def _files_match_exactly(path_a, path_b): + """Compare two files byte-for-byte without loading them fully into memory.""" + try: + if os.path.getsize(path_a) != os.path.getsize(path_b): + return False + with open(path_a, "rb") as first, open(path_b, "rb") as second: + while True: + first_chunk = first.read(1024 * 1024) + second_chunk = second.read(1024 * 1024) + if first_chunk != second_chunk: + return False + if not first_chunk: + return True + except OSError: + return False + + +def _migrate_legacy_pink_herbs_patch(target_dir, downloaded_path, progress=None): + """Release legacy patch-H ownership only when doing so is demonstrably safe. + + Pink Herbs used to be installed as Data\\patch-H.mpq. Other visual packs + may also use that filename, so a user may have replaced the old managed + file manually. During the v2 migration we only remove/restore patch-H when + the live file is missing or exactly matches the freshly downloaded Pink + Herbs MPQ. Otherwise the current patch-H is preserved and ownership is + forgotten before Pink Herbs moves to patch-V.mpq. + """ + mod_id = "visual_pink_herbs" + legacy_rel = _safe_relative_path(os.path.join("Data", "patch-H.mpq")) + legacy_key = os.path.normcase(legacy_rel) + files = _load_managed_manifest(target_dir, mod_id) + + if not any(os.path.normcase(rel) == legacy_key for rel in files): + return + + legacy_target = os.path.join(target_dir, legacy_rel) + safe_to_release = not os.path.lexists(legacy_target) + if os.path.isfile(legacy_target): + safe_to_release = _files_match_exactly(legacy_target, downloaded_path) + + if safe_to_release: + _restore_or_remove_managed_file(target_dir, mod_id, legacy_rel) + _emit_progress( + progress, + "Released legacy Pink Herbs patch-H.mpq for migration to patch-V.mpq.", + None, + None, + ) + else: + _emit_progress( + progress, + "Keeping existing patch-H.mpq because it no longer matches Pink Herbs.", + None, + None, + ) + + data = _load_managed_manifest_data(target_dir, mod_id) + remaining = [ + rel for rel in files if os.path.normcase(rel) != legacy_key + ] + _write_managed_manifest( + target_dir, + mod_id, + remaining, + revision=data.get("revision"), + ) + def _install_remote_mpq( target_dir, mod_id, @@ -1038,15 +1106,40 @@ def install_fog_pushback(target_dir, progress=None): def install_pink_herbs(target_dir, progress=None): - _install_remote_mpq( - target_dir, - "visual_pink_herbs", + mod_id = "visual_pink_herbs" + destination = os.path.join("Data", "patch-V.mpq") + temp_path = _download( "https://raw.githubusercontent.com/seacrabsam/patch-herb/main/patch-H.mpq", - os.path.join("Data", "patch-H.mpq"), + suffix=".mpq", progress=progress, label="Downloading Pink Herbs", - revision=VISUAL_MOD_REVISIONS["visual_pink_herbs"], + timeout=300, ) + try: + _verify_mpq(temp_path) + _migrate_legacy_pink_herbs_patch( + target_dir, + temp_path, + progress=progress, + ) + _emit_progress( + progress, + f"Installing {os.path.basename(destination)}...", + None, + None, + ) + _install_managed_files( + target_dir, + mod_id, + [(temp_path, destination)], + revision=VISUAL_MOD_REVISIONS[mod_id], + ) + finally: + try: + os.remove(temp_path) + except OSError: + pass + return "seacrabsam/patch-herb main" diff --git a/setup_tool.py b/setup_tool.py index 5174f68..4b14b31 100644 --- a/setup_tool.py +++ b/setup_tool.py @@ -129,7 +129,7 @@ class WowSetupTool: "pretty_night_sky": "Replaces the Vanilla night sky with a more detailed starry sky. Installed file: Data\\patch-Z.mpq. The original hosted patch-9 name is deliberately changed so the official numeric patch-9.mpq is never overwritten.", "epoch_water": "Replaces Vanilla water textures with the Epoch Water visual pack. Installed file: Data\\patch-W.mpq.", "fog_pushback": "Pushes environmental fog farther back for a clearer long-distance view. Installed file: Data\\patch-Y.mpq. Works best together with an increased Farclip value.", - "pink_herbs": "Turns most herb-node textures bright pink/purple to make gathering nodes easier to spot. Installed file: Data\\patch-H.mpq.", + "pink_herbs": "Turns most herb-node textures bright pink/purple to make gathering nodes easier to spot. Installed file: Data\\patch-V.mpq. The patch-V name avoids conflicts with other visual packs that use patch-H.mpq.", "no_error_sounds": "Installs the complete NoErrorSounds pack: muted spell fizzle sounds plus its included muted interface sounds. Installed as loose WAV files under Sound\\Spells\\Fizzle and Sound\\interface (no MPQ).", "fish_ping": "Replaces the fishing bite sound with a much more noticeable ping. Installed file: Sound\\Spells\\Tradeskills\\FishBite.wav (no MPQ). Designed specifically for WoW Vanilla 1.12.1.", "warlock_muted_demons": "Mutes the repeated voice lines from Warlock demons using Vanilla-compatible loose sound replacements. Installed as loose WAV files under Data\\Sound\\Creature (no MPQ)." diff --git a/tests/test_safety.py b/tests/test_safety.py index 9cc284d..f690c3c 100644 --- a/tests/test_safety.py +++ b/tests/test_safety.py @@ -279,6 +279,94 @@ class ManagedPackageTests(unittest.TestCase): ) ) + def test_pink_herbs_v2_migration_restores_original_patch_h(self): + with tempfile.TemporaryDirectory() as root, tempfile.TemporaryDirectory() as src: + mod_id = "visual_pink_herbs" + legacy_rel = os.path.join("Data", "patch-H.mpq") + new_rel = os.path.join("Data", "patch-V.mpq") + legacy_target = os.path.join(root, legacy_rel) + source = os.path.join(src, "pink-herbs.mpq") + + os.makedirs(os.path.dirname(legacy_target)) + with open(source, "wb") as handle: + handle.write(b"MPQ pink-herbs") + with open(legacy_target, "wb") as handle: + handle.write(b"MPQ pink-herbs") + + _, _, backup_root = remote_packages._managed_locations(root, mod_id) + backup_h = os.path.join(backup_root, legacy_rel) + os.makedirs(os.path.dirname(backup_h)) + with open(backup_h, "wb") as handle: + handle.write(b"MPQ faithful-upscale") + + remote_packages._write_managed_manifest( + root, + mod_id, + [legacy_rel], + revision="1", + ) + + remote_packages._migrate_legacy_pink_herbs_patch(root, source) + remote_packages._install_managed_files( + root, + mod_id, + [(source, new_rel)], + revision="2", + ) + + with open(legacy_target, "rb") as handle: + self.assertEqual(handle.read(), b"MPQ faithful-upscale") + with open(os.path.join(root, new_rel), "rb") as handle: + self.assertEqual(handle.read(), b"MPQ pink-herbs") + + manifest = remote_packages._load_managed_manifest_data(root, mod_id) + self.assertEqual(manifest.get("files"), ["Data/patch-V.mpq"]) + self.assertEqual(manifest.get("revision"), "2") + + def test_pink_herbs_v2_migration_preserves_replaced_patch_h(self): + with tempfile.TemporaryDirectory() as root, tempfile.TemporaryDirectory() as src: + mod_id = "visual_pink_herbs" + legacy_rel = os.path.join("Data", "patch-H.mpq") + new_rel = os.path.join("Data", "patch-V.mpq") + legacy_target = os.path.join(root, legacy_rel) + source = os.path.join(src, "pink-herbs.mpq") + + os.makedirs(os.path.dirname(legacy_target)) + with open(source, "wb") as handle: + handle.write(b"MPQ pink-herbs") + with open(legacy_target, "wb") as handle: + handle.write(b"MPQ replacement-patch-h") + + _, _, backup_root = remote_packages._managed_locations(root, mod_id) + backup_h = os.path.join(backup_root, legacy_rel) + os.makedirs(os.path.dirname(backup_h)) + with open(backup_h, "wb") as handle: + handle.write(b"MPQ older-original-patch-h") + + remote_packages._write_managed_manifest( + root, + mod_id, + [legacy_rel], + revision="1", + ) + + remote_packages._migrate_legacy_pink_herbs_patch(root, source) + remote_packages._install_managed_files( + root, + mod_id, + [(source, new_rel)], + revision="2", + ) + + with open(legacy_target, "rb") as handle: + self.assertEqual(handle.read(), b"MPQ replacement-patch-h") + with open(os.path.join(root, new_rel), "rb") as handle: + self.assertEqual(handle.read(), b"MPQ pink-herbs") + + manifest = remote_packages._load_managed_manifest_data(root, mod_id) + self.assertEqual(manifest.get("files"), ["Data/patch-V.mpq"]) + self.assertEqual(manifest.get("revision"), "2") + def test_transactional_sound_pack_rolls_back_all_files_and_manifest(self): with tempfile.TemporaryDirectory() as root, tempfile.TemporaryDirectory() as src: mod_id = "audio_test" -- 2.52.0