diff --git a/build.zig b/build.zig index bca6765..d466f08 100644 --- a/build.zig +++ b/build.zig @@ -18,7 +18,7 @@ const module_list = [_]ModuleDesc{ .{ .name = "pngscreenshots", .desc = "Enable screenshot module", .src_dir = "screenshot" }, .{ .name = "interact", .desc = "Enable interact module", .addon_name = "Interact" }, .{ .name = "outline", .desc = "Enable outline module", .default = false, .addon_name = "Outline" }, - .{ .name = "worldmarkers", .desc = "Enable world markers module", .src_dir = "markers", .addon_name = "WorldMarkers", .addon_hidden = true }, + .{ .name = "worldmarkers", .desc = "Enable world markers module", .addon_name = "WorldMarkers", .addon_hidden = true }, .{ .name = "framecrash", .desc = "Enable framecrash fix", .default = false }, .{ .name = "logsessions", .desc = "Enable log session rotation", .addon_name = "LogSessions" }, .{ .name = "minimapicons", .desc = "Enable custom minimap icons", .addon_name = "MinimapIcons" }, @@ -30,6 +30,7 @@ const module_list = [_]ModuleDesc{ .{ .name = "dpslog", .desc = "Enable structured combat log events for addons", .default = false }, .{ .name = "transform44", .desc = "Enable transformMatrix4x4 hook", .default = false }, .{ .name = "addonperf", .desc = "Enable addon memory/CPU profiling API" }, + .{ .name = "file_perf", .desc = "Enable file access profiling", .default = false, .addon_name = "FilePerf", .addon_hidden = true }, }; pub fn build(b: *std.Build) void { @@ -86,6 +87,14 @@ pub fn build(b: *std.Build) void { opts.addOption(bool, "enable_" ++ m.name, std.mem.eql(u8, m.name, variant_mod.name)); } addFileListOptions(b, opts); + // Variant builds also need the module name list for addons.zig + const names: []const []const u8 = comptime blk: { + var n: [module_list.len][]const u8 = undefined; + for (module_list, 0..) |m2, mi| n[mi] = m2.name; + const final = n; + break :blk &final; + }; + opts.addOption([]const []const u8, "all_module_names", names); const variant_lib = b.addLibrary(.{ .name = variant_mod.name, @@ -116,6 +125,14 @@ fn addModuleOptions(b: *std.Build, opts: *std.Build.Step.Options) void { inline for (module_list) |mod| { opts.addOption(bool, "enable_" ++ mod.name, b.option(bool, mod.name, mod.desc) orelse mod.default); } + // Pass full module name list so addons.zig doesn't need a hardcoded copy + const names: []const []const u8 = comptime blk: { + var n: [module_list.len][]const u8 = undefined; + for (module_list, 0..) |mod, i| n[i] = mod.name; + const final = n; + break :blk &final; + }; + opts.addOption([]const []const u8, "all_module_names", names); addFileListOptions(b, opts); } diff --git a/src/addons.zig b/src/addons.zig index 67a07e8..9538372 100644 --- a/src/addons.zig +++ b/src/addons.zig @@ -22,35 +22,18 @@ var log: logging.Logger = .{}; const build_options = @import("build_options"); // ============================================================================= -// Module list — must match build.zig module_list names. -// This is the only place module names appear; everything else is derived. +// Module list — derived from build.zig via all_module_names build option. +// Convention: each module's main source is at {name}/{name}.zig. // ============================================================================= -const module_names = [_][]const u8{ - "interact", - "outline", - "worldmarkers", - "logsessions", - "minimapicons", - "dpslog", -}; +const module_names = build_options.all_module_names; // ============================================================================= -// is_active wiring — convention-based imports +// is_active wiring — runtime registry populated by main.zig during install(). +// No per-module imports needed here; adding addon_name in build.zig is enough. // ============================================================================= -fn moduleIsActive(comptime name: []const u8) ?*const fn () bool { - if (!@field(build_options, "enable_" ++ name)) return null; - const mod = if (eqlSlice(name, "interact")) @import("interact/interact.zig") - else if (eqlSlice(name, "outline")) @import("outline/api.zig") - else if (eqlSlice(name, "worldmarkers")) @import("markers/markers.zig") - else if (eqlSlice(name, "logsessions")) @import("logsessions/logsessions.zig") - else if (eqlSlice(name, "minimapicons")) @import("minimapicons/minimapicons.zig") - else if (eqlSlice(name, "dpslog")) @import("dpslog/dpslog.zig") - else struct {}; - if (@hasDecl(mod, "isActive")) return &mod.isActive; - return null; -} +const module_active = @import("module_active.zig"); // ============================================================================= // Embedded file types @@ -64,6 +47,8 @@ pub const FileEntry = struct { const AddonPrefix = struct { prefix: []const u8, files: []const FileEntry, + /// Module name for runtime isActive gating. Null = always serve. + module_name: ?[*:0]const u8 = null, }; // ============================================================================= @@ -156,7 +141,7 @@ fn countUniqueAssetDirs(comptime paths: []const []const u8, comptime assets_pref return n; } -fn appendAssetPrefixes(result: anytype, start_idx: usize, comptime paths: []const []const u8, comptime assets_prefix: []const u8) usize { +fn appendAssetPrefixes(result: anytype, start_idx: usize, comptime paths: []const []const u8, comptime assets_prefix: []const u8, comptime mod_name: ?[*:0]const u8) usize { comptime { var dirs: [paths.len][]const u8 = undefined; var n_dirs: usize = 0; @@ -191,7 +176,7 @@ fn appendAssetPrefixes(result: anytype, start_idx: usize, comptime paths: []cons } } const final_files = files; - result[idx] = .{ .prefix = dir, .files = &final_files }; + result[idx] = .{ .prefix = dir, .files = &final_files, .module_name = mod_name }; idx += 1; } return idx; @@ -250,18 +235,20 @@ fn buildAllPrefixes() []const AddonPrefix { if (!hasAddon(name)) continue; const addon_name = @field(build_options, name ++ "_addon_name"); const addon_files = getAddonFiles(name); + const name_z: [*:0]const u8 = (name ++ "\x00").ptr; if (addon_files.len > 0) { const files = embedFiles(addon_files); result[idx] = .{ .prefix = "Interface\\AddOns\\" ++ addon_name ++ "\\", .files = &files, + .module_name = name_z, }; idx += 1; } const asset_files = getAssetFiles(name); if (asset_files.len > 0) { const pfx = assetsPrefixFromOpt(name ++ "_asset_files"); - idx = appendAssetPrefixes(&result, idx, asset_files, pfx); + idx = appendAssetPrefixes(&result, idx, asset_files, pfx, name_z); } } const final = result; @@ -269,16 +256,37 @@ fn buildAllPrefixes() []const AddonPrefix { } } -const addon_prefixes = buildAllPrefixes(); +const all_prefixes = buildAllPrefixes(); // ============================================================================= -// Embedded file lookup +// Runtime-pruned prefix table — only includes prefixes for modules that own +// their mutex. Built once in install() after all modules have claimed mutexes. +// findEmbeddedFile searches only this table, no per-lookup isActive checks. +// ============================================================================= + +var active_prefixes: [all_prefixes.len]*const AddonPrefix = undefined; +var active_count: usize = 0; + +fn pruneInactivePrefixes() void { + active_count = 0; + for (all_prefixes) |*prefix| { + const active = if (prefix.module_name) |mod_name| module_active.isActive(mod_name) else true; + if (active) { + active_prefixes[active_count] = prefix; + active_count += 1; + } + } + log.fmt("addon prefixes: {d}/{d} active\n", .{ active_count, all_prefixes.len }); +} + +// ============================================================================= +// Embedded file lookup — searches only active (mutex-owning) prefixes. // ============================================================================= pub fn findEmbeddedFile(path: [*:0]const u8) ?*const FileEntry { const path_span = std.mem.span(path); - for (addon_prefixes) |*addon| { + for (active_prefixes[0..active_count]) |addon| { if (path_span.len <= addon.prefix.len) continue; // Case-insensitive prefix check @@ -323,13 +331,24 @@ var setup_addons_hook: hook.Detour(fn (u32) callconv(hook.cc.fastcall) void) = . fn setupAddonsDetour(mgr_ptr: u32) callconv(hook.cc.fastcall) void { setup_addons_hook.callOriginal(.{mgr_ptr}); + // Register addons only for active (mutex-owning) modules. + // Use the pruned prefix table — each addon prefix with files corresponds + // to an Interface\AddOns\ entry. We match by module_name to find which + // addon_name to register. inline for (module_names) |name| { if (!@field(build_options, "enable_" ++ name)) continue; if (comptime !hasAddon(name)) continue; if (comptime getAddonFiles(name).len == 0) continue; - const active = if (comptime moduleIsActive(name)) |f| f() else true; - if (active) { + const module_name_z: [*:0]const u8 = comptime (name ++ "\x00").ptr; + // Check if this module survived pruning (i.e., owns its mutex) + const is_active = for (active_prefixes[0..active_count]) |p| { + if (p.module_name) |mn| { + if (std.mem.orderZ(u8, mn, module_name_z) == .eq) break true; + } + } else false; + + if (is_active) { const addon_name = comptime @field(build_options, name ++ "_addon_name"); const name_z: [*:0]const u8 = comptime (addon_name ++ "\x00").ptr; log.fmt("registering embedded addon: {s}\n", .{name_z}); @@ -379,6 +398,7 @@ const has_addons = blk: { pub fn install() void { if (!has_addons) return; log = logging.Logger.open("addons", .console); + pruneInactivePrefixes(); _ = setup_addons_hook.attach(0x51C740, &setupAddonsDetour); } diff --git a/src/main.zig b/src/main.zig index 7d6c04a..b84a210 100644 --- a/src/main.zig +++ b/src/main.zig @@ -20,13 +20,14 @@ const build_opts = struct { const dpslog = @import("build_options").enable_dpslog; const transform44 = @import("build_options").enable_transform44; const addonperf = @import("build_options").enable_addonperf; + const file_perf = @import("build_options").enable_file_perf; }; // Conditional module imports const screenshot = if (build_opts.screenshot) @import("screenshot/screenshot.zig") else struct {}; const interact = if (build_opts.interact) @import("interact/interact.zig") else struct {}; -const outline = if (build_opts.outline) @import("outline/api.zig") else struct {}; -const markers = if (build_opts.worldmarkers) @import("markers/markers.zig") else struct {}; +const outline = if (build_opts.outline) @import("outline/outline.zig") else struct {}; +const markers = if (build_opts.worldmarkers) @import("worldmarkers/worldmarkers.zig") else struct {}; const framecrash = if (build_opts.framecrash) @import("framecrash/framecrash.zig") else struct {}; const logsessions = if (build_opts.logsessions) @import("logsessions/logsessions.zig") else struct {}; const minimapicons = if (build_opts.minimapicons) @import("minimapicons/minimapicons.zig") else struct {}; @@ -38,20 +39,12 @@ const clickthrough = if (build_opts.clickthrough) @import("clickthrough/clickthr const dpslog = if (build_opts.dpslog) @import("dpslog/dpslog.zig") else struct {}; const transform44 = if (build_opts.transform44) @import("transform44/transform44.zig") else struct {}; const addonperf = if (build_opts.addonperf) @import("addonperf/addonperf.zig") else struct {}; +const file_perf = if (build_opts.file_perf) @import("file_perf/file_perf.zig") else struct {}; + +const module_active = @import("module_active.zig"); const WINAPI = std.builtin.CallingConvention.winapi; -const SYSTEMTIME = extern struct { - wYear: u16, - wMonth: u16, - wDayOfWeek: u16, - wDay: u16, - wHour: u16, - wMinute: u16, - wSecond: u16, - wMilliseconds: u16, -}; - // ============================================================================= // Lua Protection Bypass // ============================================================================= @@ -120,6 +113,9 @@ fn registerLuaFunctions() void { registerFunction("ResetAddOnCPUUsage", @intFromPtr(&addonperf.luaResetAddOnCPUUsage)); registerFunction("GetScriptCPUUsage", @intFromPtr(&addonperf.luaGetScriptCPUUsage)); } + if (build_opts.file_perf and file_perf.isActive()) { + registerFunction("ResetFilePerfCounters", @intFromPtr(&file_perf.luaResetCounters)); + } if (build_opts.worldmarkers and markers.isActive()) { // User-facing functions stay global registerFunction("WorldMarker", @intFromPtr(&markers.luaWorldMarker)); @@ -211,7 +207,6 @@ const LoadModelFn = fn (u32, u32, u32) callconv(hook.cc.thiscall) u32; var model_load_hook: hook.Detour(LoadModelFn) = .{}; // Windows API imports for async handling -extern "kernel32" fn GetLocalTime(lpSystemTime: *SYSTEMTIME) callconv(WINAPI) void; extern "kernel32" fn EnterCriticalSection(lpCriticalSection: *anyopaque) callconv(WINAPI) void; extern "kernel32" fn LeaveCriticalSection(lpCriticalSection: *anyopaque) callconv(WINAPI) void; extern "kernel32" fn SetEvent(hEvent: *anyopaque) callconv(WINAPI) i32; @@ -249,11 +244,6 @@ fn openFileDetour( flags: u32, handle_out: *u32, ) callconv(hook.cc.stdcall) u32 { - var st: SYSTEMTIME = undefined; - GetLocalTime(&st); - log.to(.file).fmt("{d}/{d} {d:0>2}:{d:0>2}:{d:0>2}.{d:0>3} {s}\n", .{ - st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, st.wMilliseconds, std.mem.span(path), - }); if (findEmbeddedFile(path)) |entry| { // Allocate and zero-fill 0x60-byte file context const ctx = allocateGameBuffer(0x60) orelse { @@ -283,7 +273,10 @@ fn openFileDetour( return 2; // success (non-zero type code) } - return open_file_hook.callOriginal(.{ archive_ptr, path, flags, handle_out }); + const tsc_start = if (build_opts.file_perf) file_perf.beginOpen() else 0; + const ret = open_file_hook.callOriginal(.{ archive_ptr, path, flags, handle_out }); + if (build_opts.file_perf) file_perf.endOpen(path, tsc_start); + return ret; } // --- Hook 2: GetFileSizeFromHandle (0x6487f0) --- @@ -299,7 +292,17 @@ fn getFileSizeDetour( return size; } - return get_file_size_hook.callOriginal(.{ file_ctx, high_size_out }); + const size = get_file_size_hook.callOriginal(.{ file_ctx, high_size_out }); + + // Track file sizes for cache budget estimation + if (build_opts.file_perf and file_perf.isActive()) { + const path_ptr = hook.readMem(u32, file_ctx + 0x0C); + if (path_ptr != 0) { + file_perf.recordFileSize(@as([*:0]const u8, @ptrFromInt(path_ptr)), size); + } + } + + return size; } // --- Hook 3: ReadFileFromMultipleSources (0x648460) --- @@ -335,7 +338,19 @@ fn readFileDetour( return 1; // success } - return read_file_hook.callOriginal(.{ ctx, buffer, size, bytes_read_out, async_ptr, param6 }); + const ret = read_file_hook.callOriginal(.{ ctx, buffer, size, bytes_read_out, async_ptr, param6 }); + + // Track reads for profiling — caller return address tells us who calls ReadFile + if (build_opts.file_perf and file_perf.isActive() and ret != 0) { + const path_ptr = hook.readMem(u32, ctx + 0x0C); + if (path_ptr != 0) { + const actual_read = if (bytes_read_out) |out| out.* else size; + const caller_addr: u32 = @truncate(@returnAddress()); + file_perf.recordRead(@as([*:0]const u8, @ptrFromInt(path_ptr)), actual_read, caller_addr); + } + } + + return ret; } // --- Hook 4: processAsyncFileOperation (0x647350) --- @@ -647,6 +662,7 @@ const modules = [_]ModuleHooks{ if (build_opts.dpslog) .{ .name = dpslog.module_name, .install = dpslog.installHooks, .remove = dpslog.removeHooks, .is_active = dpslog.isActive } else .{}, if (build_opts.transform44) .{ .name = transform44.module_name, .install = transform44.installHooks, .remove = transform44.removeHooks, .is_active = transform44.isActive } else .{}, if (build_opts.addonperf) .{ .name = addonperf.module_name, .install = addonperf.installHooks, .remove = addonperf.removeHooks, .is_active = addonperf.isActive } else .{}, + if (build_opts.file_perf) .{ .name = file_perf.module_name, .install = file_perf.installHooks, .remove = file_perf.removeHooks, .is_active = file_perf.isActive } else .{}, if (build_opts.worldmarkers) .{ .name = markers.module_name, .install = markers.installHooks, .remove = markers.removeHooks, .is_active = markers.isActive } else .{}, if (build_opts.interact) .{ .name = interact.module_name, .install = interact.installHooks, .remove = interact.removeHooks, .is_active = interact.isActive } else .{}, if (build_opts.outline) .{ .name = outline.module_name, .remove = outline.cleanup, .is_active = outline.isActive } else .{}, @@ -675,6 +691,10 @@ fn install() void { inline for (modules) |m| { if (m.install) |inst| inst(); + // Register isActive for addons.zig runtime lookup + if (m.name) |name| { + if (m.is_active) |f| module_active.register(name, f); + } } addons.install(); diff --git a/src/module_active.zig b/src/module_active.zig new file mode 100644 index 0000000..08fa2f8 --- /dev/null +++ b/src/module_active.zig @@ -0,0 +1,26 @@ +// Runtime registry for module isActive() function pointers. +// Populated by main.zig during install(), queried by addons.zig during login. +// Exists solely to avoid duplicating module imports in addons.zig. + +const std = @import("std"); + +const MAX = 32; +var names: [MAX][*:0]const u8 = undefined; +var fns: [MAX]*const fn () bool = undefined; +var count: usize = 0; + +/// Register a module's isActive function. Called from main.zig install(). +pub fn register(name: [*:0]const u8, f: *const fn () bool) void { + if (count >= MAX) return; + names[count] = name; + fns[count] = f; + count += 1; +} + +/// Check if a module is active by name. Returns true if unknown (no isActive). +pub fn isActive(name: [*:0]const u8) bool { + for (0..count) |i| { + if (std.mem.orderZ(u8, names[i], name) == .eq) return fns[i](); + } + return true; +} diff --git a/src/outline/model_hook.zig b/src/outline/model_hook.zig index e4dcccc..2b5713a 100644 --- a/src/outline/model_hook.zig +++ b/src/outline/model_hook.zig @@ -14,7 +14,7 @@ const std = @import("std"); const hook = @import("zhook"); -const api = @import("api.zig"); +const api = @import("outline.zig"); const o = @import("offsets.zig"); const types = @import("types.zig"); const tracker = @import("tracker.zig"); diff --git a/src/outline/api.zig b/src/outline/outline.zig similarity index 100% rename from src/outline/api.zig rename to src/outline/outline.zig diff --git a/src/markers/MPQ_FILESYSTEM_RESEARCH.md b/src/worldmarkers/MPQ_FILESYSTEM_RESEARCH.md similarity index 100% rename from src/markers/MPQ_FILESYSTEM_RESEARCH.md rename to src/worldmarkers/MPQ_FILESYSTEM_RESEARCH.md diff --git a/src/markers/RESEARCH.md b/src/worldmarkers/RESEARCH.md similarity index 100% rename from src/markers/RESEARCH.md rename to src/worldmarkers/RESEARCH.md diff --git a/src/markers/addon/Bindings.xml b/src/worldmarkers/addon/Bindings.xml similarity index 100% rename from src/markers/addon/Bindings.xml rename to src/worldmarkers/addon/Bindings.xml diff --git a/src/markers/addon/WeirdUtils_WorldMarkers.toc b/src/worldmarkers/addon/WeirdUtils_WorldMarkers.toc similarity index 100% rename from src/markers/addon/WeirdUtils_WorldMarkers.toc rename to src/worldmarkers/addon/WeirdUtils_WorldMarkers.toc diff --git a/src/markers/addon/WorldMarkers.lua b/src/worldmarkers/addon/WorldMarkers.lua similarity index 100% rename from src/markers/addon/WorldMarkers.lua rename to src/worldmarkers/addon/WorldMarkers.lua diff --git a/src/markers/assets/Spells/GRAD2C2.BLP b/src/worldmarkers/assets/Spells/GRAD2C2.BLP similarity index 100% rename from src/markers/assets/Spells/GRAD2C2.BLP rename to src/worldmarkers/assets/Spells/GRAD2C2.BLP diff --git a/src/markers/assets/Spells/GRAD2D.BLP b/src/worldmarkers/assets/Spells/GRAD2D.BLP similarity index 100% rename from src/markers/assets/Spells/GRAD2D.BLP rename to src/worldmarkers/assets/Spells/GRAD2D.BLP diff --git a/src/markers/assets/Spells/NEXUS_FIREBEAM_FAINT_SQUARE_ORA.BLP b/src/worldmarkers/assets/Spells/NEXUS_FIREBEAM_FAINT_SQUARE_ORA.BLP similarity index 100% rename from src/markers/assets/Spells/NEXUS_FIREBEAM_FAINT_SQUARE_ORA.BLP rename to src/worldmarkers/assets/Spells/NEXUS_FIREBEAM_FAINT_SQUARE_ORA.BLP diff --git a/src/markers/assets/Spells/RaidTarget_Diamond.blp b/src/worldmarkers/assets/Spells/RaidTarget_Diamond.blp similarity index 100% rename from src/markers/assets/Spells/RaidTarget_Diamond.blp rename to src/worldmarkers/assets/Spells/RaidTarget_Diamond.blp diff --git a/src/markers/assets/Spells/RaidTarget_Square.blp b/src/worldmarkers/assets/Spells/RaidTarget_Square.blp similarity index 100% rename from src/markers/assets/Spells/RaidTarget_Square.blp rename to src/worldmarkers/assets/Spells/RaidTarget_Square.blp diff --git a/src/markers/assets/Spells/RaidTarget_Star.blp b/src/worldmarkers/assets/Spells/RaidTarget_Star.blp similarity index 100% rename from src/markers/assets/Spells/RaidTarget_Star.blp rename to src/worldmarkers/assets/Spells/RaidTarget_Star.blp diff --git a/src/markers/assets/Spells/RaidTarget_Triangle.blp b/src/worldmarkers/assets/Spells/RaidTarget_Triangle.blp similarity index 100% rename from src/markers/assets/Spells/RaidTarget_Triangle.blp rename to src/worldmarkers/assets/Spells/RaidTarget_Triangle.blp diff --git a/src/markers/assets/Spells/RaidTarget_X.blp b/src/worldmarkers/assets/Spells/RaidTarget_X.blp similarity index 100% rename from src/markers/assets/Spells/RaidTarget_X.blp rename to src/worldmarkers/assets/Spells/RaidTarget_X.blp diff --git a/src/markers/assets/Spells/Raid_UI_FX_Cyan.m2 b/src/worldmarkers/assets/Spells/Raid_UI_FX_Cyan.m2 similarity index 100% rename from src/markers/assets/Spells/Raid_UI_FX_Cyan.m2 rename to src/worldmarkers/assets/Spells/Raid_UI_FX_Cyan.m2 diff --git a/src/markers/assets/Spells/Raid_UI_FX_Green.m2 b/src/worldmarkers/assets/Spells/Raid_UI_FX_Green.m2 similarity index 100% rename from src/markers/assets/Spells/Raid_UI_FX_Green.m2 rename to src/worldmarkers/assets/Spells/Raid_UI_FX_Green.m2 diff --git a/src/markers/assets/Spells/Raid_UI_FX_Purple.m2 b/src/worldmarkers/assets/Spells/Raid_UI_FX_Purple.m2 similarity index 100% rename from src/markers/assets/Spells/Raid_UI_FX_Purple.m2 rename to src/worldmarkers/assets/Spells/Raid_UI_FX_Purple.m2 diff --git a/src/markers/assets/Spells/Raid_UI_FX_Red.m2 b/src/worldmarkers/assets/Spells/Raid_UI_FX_Red.m2 similarity index 100% rename from src/markers/assets/Spells/Raid_UI_FX_Red.m2 rename to src/worldmarkers/assets/Spells/Raid_UI_FX_Red.m2 diff --git a/src/markers/assets/Spells/Raid_UI_FX_Yellow.m2 b/src/worldmarkers/assets/Spells/Raid_UI_FX_Yellow.m2 similarity index 100% rename from src/markers/assets/Spells/Raid_UI_FX_Yellow.m2 rename to src/worldmarkers/assets/Spells/Raid_UI_FX_Yellow.m2 diff --git a/src/markers/assets/Spells/T_VFX_FLARE05_32ALPHA.BLP b/src/worldmarkers/assets/Spells/T_VFX_FLARE05_32ALPHA.BLP similarity index 100% rename from src/markers/assets/Spells/T_VFX_FLARE05_32ALPHA.BLP rename to src/worldmarkers/assets/Spells/T_VFX_FLARE05_32ALPHA.BLP diff --git a/src/markers/assets/World/Expansion01/Doodads/Zulaman/Doors/T_VFX_BORDER6.BLP b/src/worldmarkers/assets/World/Expansion01/Doodads/Zulaman/Doors/T_VFX_BORDER6.BLP similarity index 100% rename from src/markers/assets/World/Expansion01/Doodads/Zulaman/Doors/T_VFX_BORDER6.BLP rename to src/worldmarkers/assets/World/Expansion01/Doodads/Zulaman/Doors/T_VFX_BORDER6.BLP diff --git a/src/markers/assets/World/Expansion01/Doodads/Zulaman/Doors/T_VFX_FIRE03_A.BLP b/src/worldmarkers/assets/World/Expansion01/Doodads/Zulaman/Doors/T_VFX_FIRE03_A.BLP similarity index 100% rename from src/markers/assets/World/Expansion01/Doodads/Zulaman/Doors/T_VFX_FIRE03_A.BLP rename to src/worldmarkers/assets/World/Expansion01/Doodads/Zulaman/Doors/T_VFX_FIRE03_A.BLP diff --git a/src/markers/offsets.zig b/src/worldmarkers/offsets.zig similarity index 100% rename from src/markers/offsets.zig rename to src/worldmarkers/offsets.zig diff --git a/src/markers/markers.zig b/src/worldmarkers/worldmarkers.zig similarity index 100% rename from src/markers/markers.zig rename to src/worldmarkers/worldmarkers.zig