import ctypes, time
from ctypes import wintypes

kernel32 = ctypes.windll.kernel32
user32 = ctypes.windll.user32
psapi = ctypes.windll.psapi

def log(msg):
    with open(r'c:\Projects\JX\logs\patch_sound.log', 'a', encoding='utf-8') as f:
        f.write(f"[{time.strftime('%H:%M:%S')}] {msg}\n")

hdesk = user32.OpenInputDesktop(0, False, 0x01FF)
if hdesk:
    user32.SetThreadDesktop(hdesk)

target_pid = 42272

PROCESS_ALL_ACCESS = 0x1F0FFF
hProcess = kernel32.OpenProcess(PROCESS_ALL_ACCESS, False, target_pid)
log(f"OpenProcess: {hex(hProcess) if hProcess else None}, err: {kernel32.GetLastError()}")

if not hProcess:
    log("Failed to open process!")
    exit(1)

# Find engine.dll base with EnumProcessModulesEx (LIST_MODULES_ALL = 0x03)
hMods = (wintypes.HMODULE * 1024)()
cbNeeded = wintypes.DWORD()
engine_base = None

# LIST_MODULES_ALL = 0x03
LIST_MODULES_ALL = 0x03
success = psapi.EnumProcessModulesEx(hProcess, ctypes.byref(hMods), ctypes.sizeof(hMods), ctypes.byref(cbNeeded), LIST_MODULES_ALL)
log(f"EnumProcessModulesEx success: {success}, needed: {cbNeeded.value}")

if success:
    count = cbNeeded.value // ctypes.sizeof(wintypes.HMODULE)
    log(f"Module count: {count}")
    name_buf = ctypes.create_unicode_buffer(512)
    for i in range(count):
        mod = hMods[i]
        if psapi.GetModuleFileNameExW(hProcess, mod, name_buf, 512):
            if 'engine.dll' in name_buf.value.lower():
                engine_base = mod
                log(f"Found engine.dll at {hex(engine_base)}: {name_buf.value}")
                break

if not engine_base:
    log("Could not find engine.dll in target process!")
    kernel32.CloseHandle(hProcess)
    exit(1)

patch_addr = engine_base + 0x17ed2
log(f"Patch address: {hex(patch_addr)}")

orig_bytes = (ctypes.c_char * 13)()
bytes_read = ctypes.c_size_t()
kernel32.ReadProcessMemory(hProcess, ctypes.c_void_p(patch_addr), orig_bytes, 13, ctypes.byref(bytes_read))
log(f"Original bytes: {bytes(orig_bytes).hex()}")

patch_bytes = b'\xeb\x0b' + b'\x90' * 11

old_protect = wintypes.DWORD()
PAGE_EXECUTE_READWRITE = 0x40
kernel32.VirtualProtectEx(hProcess, ctypes.c_void_p(patch_addr), 13, PAGE_EXECUTE_READWRITE, ctypes.byref(old_protect))

bytes_written = ctypes.c_size_t()
res = kernel32.WriteProcessMemory(hProcess, ctypes.c_void_p(patch_addr), patch_bytes, 13, ctypes.byref(bytes_written))
log(f"WriteProcessMemory result: {res}, bytes written: {bytes_written.value}, err: {kernel32.GetLastError()}")

kernel32.VirtualProtectEx(hProcess, ctypes.c_void_p(patch_addr), 13, old_protect.value, ctypes.byref(old_protect))
kernel32.CloseHandle(hProcess)

# Dismiss existing error dialog
errs = []
def cb(h, _):
    cls_buf = ctypes.create_unicode_buffer(256)
    user32.GetClassNameW(h, cls_buf, 256)
    if cls_buf.value == '#32770':
        pid = wintypes.DWORD()
        user32.GetWindowThreadProcessId(h, ctypes.byref(pid))
        if pid.value == target_pid:
            errs.append(h)
    return True

WNDENUMPROC = ctypes.WINFUNCTYPE(wintypes.BOOL, wintypes.HWND, wintypes.LPARAM)
user32.EnumWindows(WNDENUMPROC(cb), 0)
log(f"Found error dialogs to dismiss: {[hex(x) for x in errs]}")

for dlg in errs:
    def child_cb(ch, _):
        cls_buf = ctypes.create_unicode_buffer(256)
        user32.GetClassNameW(ch, cls_buf, 256)
        if cls_buf.value == 'Button':
            ctrl_id = user32.GetDlgCtrlID(ch)
            user32.PostMessageW(ch, 0x00F5, 0, 0)
            user32.PostMessageW(dlg, 0x0111, ctrl_id, ch)
        return True
    WNDENUMPROC_CHILD = ctypes.WINFUNCTYPE(wintypes.BOOL, wintypes.HWND, wintypes.LPARAM)
    user32.EnumChildWindows(dlg, WNDENUMPROC_CHILD(child_cb), 0)

time.sleep(1)
errs.clear()
user32.EnumWindows(WNDENUMPROC(cb), 0)
log(f"After 1s error dialogs: {[hex(x) for x in errs]}")
time.sleep(2)
errs.clear()
user32.EnumWindows(WNDENUMPROC(cb), 0)
log(f"After 3s error dialogs: {[hex(x) for x in errs]}")
log("Patch & dismiss complete.")
