diff --git a/DLL_README.md b/DLL_README.md index 9f80d33..e3d373a 100644 --- a/DLL_README.md +++ b/DLL_README.md @@ -92,17 +92,17 @@ Eliminates FPS drops caused by rapid equipment visual updates when transmogged i --- -### Custom Assets +### Custom Data/ Assets Enables loading loose game asset files (models, textures, etc.) from the `Data/` directory without repacking MPQ archives. Place files in `Data/` mirroring the game's internal paths (e.g. `Data/Character/Troll/Female/TrollFemale.m2`) and they will be used instead of the MPQ version. -At startup, recursively scans `Data/` and indexes all non-MPQ files into an O(1) hash set, so loose file lookups are as fast as MPQ lookups. +Also allows multi-character patch archive names (e.g. `patch-12.mpq`, `patch-jimbo.mpq`). -Also allows multi-character patch archive names (e.g. `patch-12.mpq`, `patch-jimbo.mpq`). Patch archives are sorted case-insensitively by filename — last in the sort gets highest priority, and all patches override the base archives. +Patch archives are sorted case-insensitively by filename — last in the sort gets highest priority, and all patches override the base archives. No configuration needed, install and forget. -**DLL:** `looseassets.dll` +**DLL:** `dataassets.dll` --- diff --git a/README.md b/README.md index cac4263..8068c6b 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ interaction helpers, and an embedded addon with Lua API + keybindings. | **Combatlog** | Combat log fixes. | | **Minimap Icons** | Minimap icon fixes. | | **Transmogfix** | Coalesces transmog durability update packets to prevent death frame drops. | -| **Loose Assets** | Loose file loading, permissive MPQ glob patterns, pre-indexed file hash set. | +| **Data Assets** | Loose file loading, permissive MPQ glob patterns, pre-indexed file hash set. | | **Healtextfix** | Heal text display fix. | | **Embedded Addon** | Virtual addons loaded from DLL memory — .toc, .lua, .xml, .m2, .blp served via file I/O hooks (LoadFile + Storm layer). No on-disk addon folder needed. | | **Lua Protection Bypass** | Stubs the Lua callback address validator to allow C function registration. | @@ -41,7 +41,7 @@ Users can pick the full package or grab only the features they want. ```zig // build.zig options (planned) -const enable_looseassets = b.option(bool, "looseassets", "Enable loose file loading & permissive patch glob") orelse true; +const enable_dataassets = b.option(bool, "dataassets", "Enable loose file loading & permissive patch glob") orelse true; const enable_transmogfix = b.option(bool, "transmogfix", "Enable transmog coalesce fix") orelse true; const enable_interact = b.option(bool, "interact", "Enable interact helpers") orelse true; const enable_outline = b.option(bool, "outline", "Enable outline rendering") orelse true; @@ -52,14 +52,14 @@ const enable_outline = b.option(bool, "outline", "Enable outline rendering") ore zig build # Single-feature builds — one DLL per feature for individual distribution -zig build -Dlooseassets=true -Dtransmogfix=false -Dinteract=false -Doutline=false -zig build -Dlooseassets=false -Dtransmogfix=true -Dinteract=false -Doutline=false +zig build -Ddataassets=true -Dtransmogfix=false -Dinteract=false -Doutline=false +zig build -Ddataassets=false -Dtransmogfix=true -Dinteract=false -Doutline=false # etc. ``` Release artifacts: - `weirdutils.dll` — everything -- `looseassets.dll` — just asset/MPQ fixes +- `dataassets.dll` — just asset/MPQ fixes - `transmogfix.dll` — just transmog coalesce - `interact.dll` — just interact/loot helpers - `outline.dll` — just outline rendering @@ -69,14 +69,14 @@ All built from this repo, all sharing the same hook library and codebase. ### Per-Feature Named Mutex A user might load the full DLL alongside one of the smaller single-feature DLLs -(e.g. they use `weirdutils.dll` for everything but also have `looseassets.dll` from +(e.g. they use `weirdutils.dll` for everything but also have `dataassets.dll` from before they switched). Each feature module claims a **named mutex** on load — if it's already held, that module skips hook installation. This way any combination of DLLs coexists safely with no duplicate hooks. ```zig // Each module creates a process-specific named mutex on init -const mutex = CreateMutexA(null, 1, "Local\\WeirdUtils_LooseAssets_{pid}"); +const mutex = CreateMutexA(null, 1, "Local\\WeirdUtils_DataAssets_{pid}"); if (GetLastError() == ERROR_ALREADY_EXISTS) { // Another DLL already owns this feature's hooks — skip CloseHandle(mutex); @@ -127,7 +127,7 @@ gh release create v1.0 --repo YourName/WeirdUtils \ --title "v1.0" --notes "Release notes" \ ./zig-out/lib/weirdutils.dll \ ./builds/outline.dll \ - ./builds/looseassets.dll + ./builds/dataassets.dll ``` ## Project Structure diff --git a/build.zig b/build.zig index 8b9e3f6..30f05c0 100644 --- a/build.zig +++ b/build.zig @@ -17,7 +17,7 @@ pub fn build(b: *std.Build) void { const enable_combatlog = b.option(bool, "combatlog", "Enable combat log freshness") orelse true; const enable_minimapicons = b.option(bool, "minimapicons", "Enable custom minimap icons") orelse true; const enable_transmogfix = b.option(bool, "transmogfix", "Enable transmog update coalescing") orelse true; - const enable_looseassets = b.option(bool, "looseassets", "Enable loose file loading & permissive patch glob") orelse true; + const enable_dataassets = b.option(bool, "dataassets", "Enable loose file loading & permissive patch glob") orelse true; const enable_healtextfix = b.option(bool, "healtextfix", "Enable SuperWoW heal text fix") orelse true; // Create build options module @@ -30,7 +30,7 @@ pub fn build(b: *std.Build) void { build_options.addOption(bool, "enable_combatlog", enable_combatlog); build_options.addOption(bool, "enable_minimapicons", enable_minimapicons); build_options.addOption(bool, "enable_transmogfix", enable_transmogfix); - build_options.addOption(bool, "enable_looseassets", enable_looseassets); + build_options.addOption(bool, "enable_dataassets", enable_dataassets); build_options.addOption(bool, "enable_healtextfix", enable_healtextfix); const build_options_module = build_options.createModule(); @@ -60,19 +60,19 @@ pub fn build(b: *std.Build) void { const build_all_step = b.step("all-variants", "Build all DLL variants"); // Helper to create a single-module build - const Variant = struct { name: []const u8, screenshot: bool, interact: bool, outline: bool, worldmarkers: bool, framecrash: bool, combatlog: bool, minimapicons: bool, transmogfix: bool, looseassets: bool, healtextfix: bool }; + const Variant = struct { name: []const u8, screenshot: bool, interact: bool, outline: bool, worldmarkers: bool, framecrash: bool, combatlog: bool, minimapicons: bool, transmogfix: bool, dataassets: bool, healtextfix: bool }; inline for (&[_]Variant{ - .{ .name = "full", .screenshot = true, .interact = true, .outline = true, .worldmarkers = true, .framecrash = true, .combatlog = true, .minimapicons = true, .transmogfix = true, .looseassets = true, .healtextfix = true }, - .{ .name = "screenshot", .screenshot = true, .interact = false, .outline = false, .worldmarkers = false, .framecrash = true, .combatlog = true, .minimapicons = false, .transmogfix = false, .looseassets = false, .healtextfix = false }, - .{ .name = "interact", .screenshot = false, .interact = true, .outline = false, .worldmarkers = false, .framecrash = true, .combatlog = true, .minimapicons = false, .transmogfix = false, .looseassets = false, .healtextfix = false }, - .{ .name = "outline", .screenshot = false, .interact = false, .outline = true, .worldmarkers = false, .framecrash = true, .combatlog = true, .minimapicons = false, .transmogfix = false, .looseassets = false, .healtextfix = false }, - .{ .name = "worldmarkers", .screenshot = false, .interact = false, .outline = false, .worldmarkers = true, .framecrash = true, .combatlog = true, .minimapicons = false, .transmogfix = false, .looseassets = false, .healtextfix = false }, - .{ .name = "framecrash", .screenshot = false, .interact = false, .outline = false, .worldmarkers = false, .framecrash = true, .combatlog = false, .minimapicons = false, .transmogfix = false, .looseassets = false, .healtextfix = false }, - .{ .name = "combatlog", .screenshot = false, .interact = false, .outline = false, .worldmarkers = false, .framecrash = false, .combatlog = true, .minimapicons = false, .transmogfix = false, .looseassets = false, .healtextfix = false }, - .{ .name = "minimapicons", .screenshot = false, .interact = false, .outline = false, .worldmarkers = false, .framecrash = true, .combatlog = false, .minimapicons = true, .transmogfix = false, .looseassets = false, .healtextfix = false }, - .{ .name = "transmogfix", .screenshot = false, .interact = false, .outline = false, .worldmarkers = false, .framecrash = false, .combatlog = false, .minimapicons = false, .transmogfix = true, .looseassets = false, .healtextfix = false }, - .{ .name = "looseassets", .screenshot = false, .interact = false, .outline = false, .worldmarkers = false, .framecrash = false, .combatlog = false, .minimapicons = false, .transmogfix = false, .looseassets = true, .healtextfix = false }, - .{ .name = "healtextfix", .screenshot = false, .interact = false, .outline = false, .worldmarkers = false, .framecrash = false, .combatlog = false, .minimapicons = false, .transmogfix = false, .looseassets = false, .healtextfix = true }, + .{ .name = "full", .screenshot = true, .interact = true, .outline = true, .worldmarkers = true, .framecrash = true, .combatlog = true, .minimapicons = true, .transmogfix = true, .dataassets = true, .healtextfix = true }, + .{ .name = "screenshot", .screenshot = true, .interact = false, .outline = false, .worldmarkers = false, .framecrash = true, .combatlog = true, .minimapicons = false, .transmogfix = false, .dataassets = false, .healtextfix = false }, + .{ .name = "interact", .screenshot = false, .interact = true, .outline = false, .worldmarkers = false, .framecrash = true, .combatlog = true, .minimapicons = false, .transmogfix = false, .dataassets = false, .healtextfix = false }, + .{ .name = "outline", .screenshot = false, .interact = false, .outline = true, .worldmarkers = false, .framecrash = true, .combatlog = true, .minimapicons = false, .transmogfix = false, .dataassets = false, .healtextfix = false }, + .{ .name = "worldmarkers", .screenshot = false, .interact = false, .outline = false, .worldmarkers = true, .framecrash = true, .combatlog = true, .minimapicons = false, .transmogfix = false, .dataassets = false, .healtextfix = false }, + .{ .name = "framecrash", .screenshot = false, .interact = false, .outline = false, .worldmarkers = false, .framecrash = true, .combatlog = false, .minimapicons = false, .transmogfix = false, .dataassets = false, .healtextfix = false }, + .{ .name = "combatlog", .screenshot = false, .interact = false, .outline = false, .worldmarkers = false, .framecrash = false, .combatlog = true, .minimapicons = false, .transmogfix = false, .dataassets = false, .healtextfix = false }, + .{ .name = "minimapicons", .screenshot = false, .interact = false, .outline = false, .worldmarkers = false, .framecrash = true, .combatlog = false, .minimapicons = true, .transmogfix = false, .dataassets = false, .healtextfix = false }, + .{ .name = "transmogfix", .screenshot = false, .interact = false, .outline = false, .worldmarkers = false, .framecrash = false, .combatlog = false, .minimapicons = false, .transmogfix = true, .dataassets = false, .healtextfix = false }, + .{ .name = "dataassets", .screenshot = false, .interact = false, .outline = false, .worldmarkers = false, .framecrash = false, .combatlog = false, .minimapicons = false, .transmogfix = false, .dataassets = true, .healtextfix = false }, + .{ .name = "healtextfix", .screenshot = false, .interact = false, .outline = false, .worldmarkers = false, .framecrash = false, .combatlog = false, .minimapicons = false, .transmogfix = false, .dataassets = false, .healtextfix = true }, }) |variant| { const opts = b.addOptions(); opts.addOption(bool, "enable_screenshot", variant.screenshot); @@ -83,7 +83,7 @@ pub fn build(b: *std.Build) void { opts.addOption(bool, "enable_combatlog", variant.combatlog); opts.addOption(bool, "enable_minimapicons", variant.minimapicons); opts.addOption(bool, "enable_transmogfix", variant.transmogfix); - opts.addOption(bool, "enable_looseassets", variant.looseassets); + opts.addOption(bool, "enable_dataassets", variant.dataassets); opts.addOption(bool, "enable_healtextfix", variant.healtextfix); const variant_lib = b.addLibrary(.{ diff --git a/src/looseassets/looseassets.zig b/src/dataassets/dataassets.zig similarity index 97% rename from src/looseassets/looseassets.zig rename to src/dataassets/dataassets.zig index bb7431e..c853fb6 100644 --- a/src/looseassets/looseassets.zig +++ b/src/dataassets/dataassets.zig @@ -1,5 +1,5 @@ // ============================================================================= -// looseassets - Loose file loading & permissive patch glob +// dataassets - Loose file loading & permissive patch glob // ============================================================================= // // 1. Patches patch-?.MPQ → patch-*.MPQ so multi-char patch names work @@ -192,7 +192,7 @@ fn checkFileExistenceDetour(filename_ptr: u32, flags: u32, output_buffer_ptr: u3 if (filename_ptr != 0) { if (looseFilesLookup(filename_ptr)) |disk_path| { const raw: [*]const u8 = @ptrFromInt(filename_ptr); - con.fmt("[looseassets] loose hit: \"{s}\"\n", .{raw[0..cStrLen(raw)]}); + con.fmt("[dataassets] loose hit: \"{s}\"\n", .{raw[0..cStrLen(raw)]}); if (output_buffer_ptr != 0) { const disk_len = cStrLen(disk_path); @@ -281,11 +281,11 @@ var g_mutex: ?*anyopaque = null; var g_is_hook_owner: bool = false; pub fn installHooks() void { - con.print("[looseassets] Module loaded\n"); + con.print("[dataassets] Module loaded\n"); // Multi-DLL safety: only one instance per process should hook var mutex_name_buf: [64]u8 = undefined; - const mutex_name = std.fmt.bufPrint(&mutex_name_buf, "Local\\LooseassetsHook_{d}", .{GetCurrentProcessId()}) catch return; + const mutex_name = std.fmt.bufPrint(&mutex_name_buf, "Local\\DataAssetsHook_{d}", .{GetCurrentProcessId()}) catch return; mutex_name_buf[mutex_name.len] = 0; g_mutex = CreateMutexA(null, 1, @ptrCast(mutex_name_buf[0..mutex_name.len :0])); @@ -295,7 +295,7 @@ pub fn installHooks() void { _ = CloseHandle(g_mutex.?); g_mutex = null; g_is_hook_owner = false; - con.print("[looseassets] Another DLL owns hooks (mutex taken), skipping\n"); + con.print("[dataassets] Another DLL owns hooks (mutex taken), skipping\n"); return; } g_is_hook_owner = true; diff --git a/src/main.zig b/src/main.zig index 32138ad..5bcb583 100644 --- a/src/main.zig +++ b/src/main.zig @@ -12,7 +12,7 @@ const build_opts = struct { const combatlog = @import("build_options").enable_combatlog; const minimapicons = @import("build_options").enable_minimapicons; const transmogfix = @import("build_options").enable_transmogfix; - const looseassets = @import("build_options").enable_looseassets; + const dataassets = @import("build_options").enable_dataassets; const healtextfix = @import("build_options").enable_healtextfix; }; @@ -25,7 +25,7 @@ const framecrash = if (build_opts.framecrash) @import("framecrash/framecrash.zig const combatlog = if (build_opts.combatlog) @import("combatlog/combatlog.zig") else struct {}; const minimapicons = if (build_opts.minimapicons) @import("minimapicons/minimapicons.zig") else struct {}; const transmogfix = if (build_opts.transmogfix) @import("transmogfix/transmogfix.zig") else struct {}; -const looseassets = if (build_opts.looseassets) @import("looseassets/looseassets.zig") else struct {}; +const dataassets = if (build_opts.dataassets) @import("dataassets/dataassets.zig") else struct {}; const healtextfix = if (build_opts.healtextfix) @import("healtextfix/healtextfix.zig") else struct {}; const WINAPI = std.builtin.CallingConvention.winapi; @@ -803,7 +803,7 @@ const ModuleHooks = struct { /// Order matters: modules are installed top-to-bottom, removed bottom-to-top. /// Modules with remove_on_shutdown run their remove during shutdownDetour too. const modules = [_]ModuleHooks{ - if (build_opts.looseassets) .{ .install = looseassets.installHooks, .remove = looseassets.removeHooks } else .{}, + if (build_opts.dataassets) .{ .install = dataassets.installHooks, .remove = dataassets.removeHooks } else .{}, if (build_opts.framecrash) .{ .install = framecrash.installHooks, .remove = framecrash.removeHooks } else .{}, if (build_opts.combatlog) .{ .install = combatlog.installHooks, .remove = combatlog.removeHooks } else .{}, if (build_opts.transmogfix) .{ .install = transmogfix.installHooks, .remove = transmogfix.removeHooks } else .{}, diff --git a/src/markers/MPQ_FILESYSTEM_RESEARCH.md b/src/markers/MPQ_FILESYSTEM_RESEARCH.md index 9cfbef5..17960c1 100644 --- a/src/markers/MPQ_FILESYSTEM_RESEARCH.md +++ b/src/markers/MPQ_FILESYSTEM_RESEARCH.md @@ -408,9 +408,9 @@ A minimal V1 MPQ for ~20 uncompressed files requires: The hash table uses a specific encryption algorithm (documented in wowdev.wiki and StormLib source). Could implement in Zig comptime or use a build-time tool. -### Loose Assets Synergy +### Data Assets Synergy -The [looseassets project](/media/storage/projects/zig/looseassets/) already implements the glob pattern patch (`0x82edc2: '?' → '*'`) for multi-character patch names. If combined with looseassets, the temp file could use any name like `patch-weirdutils.MPQ`. +The [dataassets project](/media/storage/projects/zig/dataassets/) already implements the glob pattern patch (`0x82edc2: '?' → '*'`) for multi-character patch names. If combined with dataassets, the temp file could use any name like `patch-weirdutils.MPQ`. --- @@ -532,11 +532,11 @@ If we could insert entries into this hash table mapping our asset paths to disk --- -## Approach D: CheckFileExistence Hook (looseassets Pattern) +## Approach D: CheckFileExistence Hook (dataassets Pattern) ### Concept -The [looseassets project](/media/storage/projects/zig/looseassets/) takes a different approach entirely: +The [dataassets project](/media/storage/projects/zig/dataassets/) takes a different approach entirely: 1. **NOP two gates** in `File_FindInArchive` (0x654b5c, 0x654b6a) that restrict `CheckFileExistence` to only "Interface/AddOns" paths 2. **Hook `CheckFileExistence`** (0x654DD0) to check a hash map of loose disk files @@ -544,7 +544,7 @@ The [looseassets project](/media/storage/projects/zig/looseassets/) takes a diff This works for **disk-based** loose files but not for in-memory embedded data. However, combined with writing temp files to disk, it provides a clean single-hook solution. -### Key Addresses (from looseassets) +### Key Addresses (from dataassets) | Target | Address | Patch | |--------|---------|-------| @@ -564,7 +564,7 @@ This works for **disk-based** loose files but not for in-memory embedded data. H | Fake contexts | ~20 | **0** | 0 | 1 | 0 | 0 | | Async handling | Manual | **Native** | Native | Uncertain | Native | Native | | Build complexity | Low | **Low** | Medium (MPQ builder) | Medium (MPQ builder) | High (struct RE) | Low | -| RE work needed | Done | **Minimal** (verify path redirect) | Archive_OpenUnified conv | Same as A + async verify | Full struct layout | Already done (looseassets) | +| RE work needed | Done | **Minimal** (verify path redirect) | Archive_OpenUnified conv | Same as A + async verify | Full struct layout | Already done (dataassets) | | Risk | Proven but fragile | **Low** (real OS handles) | Low (native I/O) | Medium (async path?) | High (struct mismatch) | Low | | Cleanup needed | None | **Auto** (DELETE_ON_CLOSE) | Delete temp file | None | Remove from array | Delete temp files | | Purely in-memory | Yes | Mostly (cache-backed) | No | Yes | Yes | No | @@ -595,9 +595,9 @@ This works for **disk-based** loose files but not for in-memory embedded data. H If a single-file solution is preferred over ~20 temp files, building a real MPQ and registering it via `Archive_OpenUnified` eliminates all hooks entirely. The cost is implementing an MPQ V1 builder (hash table encryption, block table, header). Could be combined with Approach E: use temp files now, migrate to MPQ later. -### Worth Combining With: Loose Assets Integration +### Worth Combining With: Data Assets Integration -The looseassets project's `CheckFileExistence` hook and glob pattern patch could complement either approach, especially for supporting user-provided loose asset files alongside our embedded ones. +The dataassets project's `CheckFileExistence` hook and glob pattern patch could complement either approach, especially for supporting user-provided loose asset files alongside our embedded ones. --- @@ -687,6 +687,6 @@ The looseassets project's `CheckFileExistence` hook and glob pattern patch could | Archive search critsec | 0xc54008 | Critical section for archive ops | | Archive search state | 0xc53ff0 | Used by File_FindInArchive | | Patch glob "patch-?.MPQ" | 0x82edbc | Glob pattern for patch discovery | -| Patch glob char | 0x82edc2 | The '?' byte (looseassets patches to '*') | +| Patch glob char | 0x82edc2 | The '?' byte (dataassets patches to '*') | | Data path format | 0x82edc8 | `"Data\%s"` format string | | SArchive RTTI | 0x82e248 | `".PAVSArchive@@"` |