Add module control API, bigcursor with fractional scaling, shared mutex
Module control API: - Three cdecl exports: WeirdUtils_IsModuleActive, WeirdUtils_DisableModule, WeirdUtils_DisableAll - C header include/weirdutils_api.h for runtime DLL discovery - All modules gain pub module_name, isActive(), shared mutex via src/mutex.zig - build.zig refactored to module_list array of ModuleDesc Bigcursor: - D3D9 vtable hooks on SetCursorProperties/ShowCursor - Scale2x/Scale3x pixel-art upscalers in pure Zig (replaces 12K-line hqx C) - Bilinear resampler for fractional scales (1.0-4.0, default 1.2) - Win32 cursor via CreateIconIndirect bypasses D3D9 32x32 limit - FNV-1a hash cache for ~16 cursor bitmaps - Lua API: SetCursorScale(n) / GetCursorScale() - CVar cursorScale for Config.wtf persistence (tenths) Other: - Add dpslog module stub - Docs and README updates
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
const std = @import("std");
|
||||
const con = @import("console.zig");
|
||||
|
||||
const WINAPI = std.builtin.CallingConvention.winapi;
|
||||
extern "kernel32" fn CreateMutexA(lpMutexAttributes: ?*anyopaque, bInitialOwner: i32, lpName: ?[*:0]const u8) callconv(WINAPI) ?*anyopaque;
|
||||
extern "kernel32" fn ReleaseMutex(hMutex: *anyopaque) callconv(WINAPI) i32;
|
||||
extern "kernel32" fn CloseHandle(hObject: *anyopaque) callconv(WINAPI) i32;
|
||||
extern "kernel32" fn GetLastError() callconv(WINAPI) u32;
|
||||
extern "kernel32" fn GetCurrentProcessId() callconv(WINAPI) u32;
|
||||
|
||||
const ERROR_ALREADY_EXISTS: u32 = 183;
|
||||
|
||||
pub const Result = struct {
|
||||
handle: ?*anyopaque,
|
||||
is_owner: bool,
|
||||
};
|
||||
|
||||
/// Try to acquire the per-process module mutex.
|
||||
/// Format: `Local\WeirdUtils_<module_name>_<PID>`
|
||||
pub fn acquire(module_name: [*:0]const u8) Result {
|
||||
var buf: [80]u8 = undefined;
|
||||
const name_span = std.mem.span(module_name);
|
||||
const formatted = std.fmt.bufPrint(&buf, "Local\\WeirdUtils_{s}_{d}", .{ name_span, GetCurrentProcessId() }) catch return .{ .handle = null, .is_owner = false };
|
||||
return doAcquire(&buf, formatted.len, name_span);
|
||||
}
|
||||
|
||||
/// Acquire with a legacy mutex name format: `Local\<legacy_prefix>_<PID>`
|
||||
/// Used by transmogfix which shipped before the naming convention was established.
|
||||
pub fn acquireLegacy(legacy_prefix: [*:0]const u8, module_name: [*:0]const u8) Result {
|
||||
var buf: [80]u8 = undefined;
|
||||
const formatted = std.fmt.bufPrint(&buf, "Local\\{s}_{d}", .{ std.mem.span(legacy_prefix), GetCurrentProcessId() }) catch return .{ .handle = null, .is_owner = false };
|
||||
return doAcquire(&buf, formatted.len, std.mem.span(module_name));
|
||||
}
|
||||
|
||||
fn doAcquire(buf: *[80]u8, len: usize, log_name: []const u8) Result {
|
||||
buf[len] = 0;
|
||||
|
||||
const mutex = CreateMutexA(null, 1, @ptrCast(buf[0..len :0]));
|
||||
if (mutex == null) return .{ .handle = null, .is_owner = false };
|
||||
|
||||
if (GetLastError() == ERROR_ALREADY_EXISTS) {
|
||||
_ = CloseHandle(mutex.?);
|
||||
con.fmt("[{s}] Another DLL owns hooks (mutex taken), skipping\n", .{log_name});
|
||||
return .{ .handle = null, .is_owner = false };
|
||||
}
|
||||
|
||||
return .{ .handle = mutex, .is_owner = true };
|
||||
}
|
||||
|
||||
/// Release and close a module mutex.
|
||||
pub fn release(mutex: *?*anyopaque) void {
|
||||
if (mutex.*) |m| {
|
||||
_ = ReleaseMutex(m);
|
||||
_ = CloseHandle(m);
|
||||
mutex.* = null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user