From cca6c8c6816f77932ec8d76670f770017b6d141b Mon Sep 17 00:00:00 2001 From: Charlotte Pabst Date: Wed, 18 Jun 2025 18:13:27 +0200 Subject: [PATCH 001/454] ntdll: Treat Rbp as CONTEXT_INTEGER register. (cherry picked from commit 91d858f18e432f3b5896902dec4379a1dac5aee4) --- dlls/dbghelp/tests/dbghelp.c | 2 +- dlls/ntdll/unix/signal_x86_64.c | 4 ++-- dlls/ntdll/unix/thread.c | 20 ++++++++++++++------ server/protocol.def | 4 ++-- server/trace.c | 2 +- tools/make_requests | 2 +- 6 files changed, 21 insertions(+), 13 deletions(-) diff --git a/dlls/dbghelp/tests/dbghelp.c b/dlls/dbghelp/tests/dbghelp.c index f7313c3ab4ff..41398784ef97 100644 --- a/dlls/dbghelp/tests/dbghelp.c +++ b/dlls/dbghelp/tests/dbghelp.c @@ -100,7 +100,7 @@ static void test_stack_walk(void) } while (!count); - ctx.ContextFlags = CONTEXT_CONTROL; + ctx.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER; ret = GetThreadContext(thread, &ctx); ok(ret, "got error %u\n", ret); diff --git a/dlls/ntdll/unix/signal_x86_64.c b/dlls/ntdll/unix/signal_x86_64.c index 10d39bc1098c..473c4b994ad1 100644 --- a/dlls/ntdll/unix/signal_x86_64.c +++ b/dlls/ntdll/unix/signal_x86_64.c @@ -1055,6 +1055,7 @@ NTSTATUS WINAPI NtSetContextThread( HANDLE handle, const CONTEXT *context ) frame->rbx = context->Rbx; frame->rcx = context->Rcx; frame->rdx = context->Rdx; + frame->rbp = context->Rbp; frame->rsi = context->Rsi; frame->rdi = context->Rdi; frame->r8 = context->R8; @@ -1069,7 +1070,6 @@ NTSTATUS WINAPI NtSetContextThread( HANDLE handle, const CONTEXT *context ) if (flags & CONTEXT_CONTROL) { frame->rsp = context->Rsp; - frame->rbp = context->Rbp; frame->rip = context->Rip; frame->eflags = context->EFlags; } @@ -1126,6 +1126,7 @@ NTSTATUS WINAPI NtGetContextThread( HANDLE handle, CONTEXT *context ) context->Rbx = frame->rbx; context->Rcx = frame->rcx; context->Rdx = frame->rdx; + context->Rbp = frame->rbp; context->Rsi = frame->rsi; context->Rdi = frame->rdi; context->R8 = frame->r8; @@ -1141,7 +1142,6 @@ NTSTATUS WINAPI NtGetContextThread( HANDLE handle, CONTEXT *context ) if (needed_flags & CONTEXT_CONTROL) { context->Rsp = frame->rsp; - context->Rbp = frame->rbp; context->Rip = frame->rip; context->EFlags = frame->eflags; context->SegCs = cs64_sel; diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c index 5edca5fcf943..7da61c1a4386 100644 --- a/dlls/ntdll/unix/thread.c +++ b/dlls/ntdll/unix/thread.c @@ -374,12 +374,13 @@ static NTSTATUS context_to_server( struct context_data *to, USHORT to_machine, c if (flags & CONTEXT_I386_CONTROL) { to->flags |= SERVER_CTX_CONTROL; - to->ctl.x86_64_regs.rbp = from->Ebp; to->ctl.x86_64_regs.rsp = from->Esp; to->ctl.x86_64_regs.rip = from->Eip; to->ctl.x86_64_regs.cs = from->SegCs; to->ctl.x86_64_regs.ss = from->SegSs; to->ctl.x86_64_regs.flags = from->EFlags; + + to->integer.x86_64_regs.rbp = from->Ebp; } if (flags & CONTEXT_I386_INTEGER) { @@ -433,7 +434,6 @@ static NTSTATUS context_to_server( struct context_data *to, USHORT to_machine, c if (flags & CONTEXT_AMD64_CONTROL) { to->flags |= SERVER_CTX_CONTROL; - to->ctl.x86_64_regs.rbp = from->Rbp; to->ctl.x86_64_regs.rip = from->Rip; to->ctl.x86_64_regs.rsp = from->Rsp; to->ctl.x86_64_regs.cs = from->SegCs; @@ -447,6 +447,7 @@ static NTSTATUS context_to_server( struct context_data *to, USHORT to_machine, c to->integer.x86_64_regs.rcx = from->Rcx; to->integer.x86_64_regs.rdx = from->Rdx; to->integer.x86_64_regs.rbx = from->Rbx; + to->integer.x86_64_regs.rbp = from->Rbp; to->integer.x86_64_regs.rsi = from->Rsi; to->integer.x86_64_regs.rdi = from->Rdi; to->integer.x86_64_regs.r8 = from->R8; @@ -495,7 +496,6 @@ static NTSTATUS context_to_server( struct context_data *to, USHORT to_machine, c if (flags & CONTEXT_AMD64_CONTROL) { to->flags |= SERVER_CTX_CONTROL; - to->ctl.i386_regs.ebp = from->Rbp; to->ctl.i386_regs.eip = from->Rip; to->ctl.i386_regs.esp = from->Rsp; to->ctl.i386_regs.cs = from->SegCs; @@ -511,6 +511,8 @@ static NTSTATUS context_to_server( struct context_data *to, USHORT to_machine, c to->integer.i386_regs.ebx = from->Rbx; to->integer.i386_regs.esi = from->Rsi; to->integer.i386_regs.edi = from->Rdi; + + to->ctl.i386_regs.ebp = from->Rbp; } if (flags & CONTEXT_AMD64_SEGMENTS) { @@ -781,7 +783,6 @@ static NTSTATUS context_from_server( void *dst, const struct context_data *from, if ((from->flags & SERVER_CTX_CONTROL) && (to_flags & CONTEXT_I386_CONTROL)) { to->ContextFlags |= CONTEXT_I386_CONTROL; - to->Ebp = from->ctl.x86_64_regs.rbp; to->Esp = from->ctl.x86_64_regs.rsp; to->Eip = from->ctl.x86_64_regs.rip; to->SegCs = from->ctl.x86_64_regs.cs; @@ -798,6 +799,10 @@ static NTSTATUS context_from_server( void *dst, const struct context_data *from, to->Esi = from->integer.x86_64_regs.rsi; to->Edi = from->integer.x86_64_regs.rdi; } + if ((from->flags & SERVER_CTX_INTEGER) && (to_flags & CONTEXT_I386_CONTROL)) + { + to->Ebp = from->integer.x86_64_regs.rbp; + } if ((from->flags & SERVER_CTX_SEGMENTS) && (to_flags & CONTEXT_I386_SEGMENTS)) { to->ContextFlags |= CONTEXT_I386_SEGMENTS; @@ -843,7 +848,6 @@ static NTSTATUS context_from_server( void *dst, const struct context_data *from, if ((from->flags & SERVER_CTX_CONTROL) && (to_flags & CONTEXT_AMD64_CONTROL)) { to->ContextFlags |= CONTEXT_AMD64_CONTROL; - to->Rbp = from->ctl.x86_64_regs.rbp; to->Rip = from->ctl.x86_64_regs.rip; to->Rsp = from->ctl.x86_64_regs.rsp; to->SegCs = from->ctl.x86_64_regs.cs; @@ -857,6 +861,7 @@ static NTSTATUS context_from_server( void *dst, const struct context_data *from, to->Rcx = from->integer.x86_64_regs.rcx; to->Rdx = from->integer.x86_64_regs.rdx; to->Rbx = from->integer.x86_64_regs.rbx; + to->Rbp = from->integer.x86_64_regs.rbp; to->Rsi = from->integer.x86_64_regs.rsi; to->Rdi = from->integer.x86_64_regs.rdi; to->R8 = from->integer.x86_64_regs.r8; @@ -906,7 +911,6 @@ static NTSTATUS context_from_server( void *dst, const struct context_data *from, if ((from->flags & SERVER_CTX_CONTROL) && (to_flags & CONTEXT_AMD64_CONTROL)) { to->ContextFlags |= CONTEXT_AMD64_CONTROL; - to->Rbp = from->ctl.i386_regs.ebp; to->Rip = from->ctl.i386_regs.eip; to->Rsp = from->ctl.i386_regs.esp; to->SegCs = from->ctl.i386_regs.cs; @@ -923,6 +927,10 @@ static NTSTATUS context_from_server( void *dst, const struct context_data *from, to->Rsi = from->integer.i386_regs.esi; to->Rdi = from->integer.i386_regs.edi; } + if ((from->flags & SERVER_CTX_CONTROL) && (to_flags & CONTEXT_AMD64_INTEGER)) + { + to->Rbp = from->ctl.i386_regs.ebp; + } if ((from->flags & SERVER_CTX_SEGMENTS) && (to_flags & CONTEXT_AMD64_SEGMENTS)) { to->ContextFlags |= CONTEXT_AMD64_SEGMENTS; diff --git a/server/protocol.def b/server/protocol.def index 72cb633397a4..fedff72c267b 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -143,7 +143,7 @@ struct context_data union { struct { unsigned int eip, ebp, esp, eflags, cs, ss; } i386_regs; - struct { unsigned __int64 rip, rbp, rsp; + struct { unsigned __int64 rip, rsp; unsigned int cs, ss, flags, __pad; } x86_64_regs; struct { unsigned int sp, lr, pc, cpsr; } arm_regs; struct { unsigned __int64 sp, pc, pstate; } arm64_regs; @@ -151,7 +151,7 @@ struct context_data union { struct { unsigned int eax, ebx, ecx, edx, esi, edi; } i386_regs; - struct { unsigned __int64 rax,rbx, rcx, rdx, rsi, rdi, + struct { unsigned __int64 rax, rbx, rcx, rdx, rbp, rsi, rdi, r8, r9, r10, r11, r12, r13, r14, r15; } x86_64_regs; struct { unsigned int r[13]; } arm_regs; struct { unsigned __int64 x[31]; } arm64_regs; diff --git a/server/trace.c b/server/trace.c index 52b219a1f173..80621d579d38 100644 --- a/server/trace.c +++ b/server/trace.c @@ -754,7 +754,6 @@ static void dump_varargs_context( const char *prefix, data_size_t size ) if (ctx.flags & SERVER_CTX_CONTROL) { dump_uint64( ",rip=", &ctx.ctl.x86_64_regs.rip ); - dump_uint64( ",rbp=", &ctx.ctl.x86_64_regs.rbp ); dump_uint64( ",rsp=", &ctx.ctl.x86_64_regs.rsp ); fprintf( stderr, ",cs=%04x,ss=%04x,flags=%08x", ctx.ctl.x86_64_regs.cs, ctx.ctl.x86_64_regs.ss, ctx.ctl.x86_64_regs.flags ); @@ -765,6 +764,7 @@ static void dump_varargs_context( const char *prefix, data_size_t size ) dump_uint64( ",rbx=", &ctx.integer.x86_64_regs.rbx ); dump_uint64( ",rcx=", &ctx.integer.x86_64_regs.rcx ); dump_uint64( ",rdx=", &ctx.integer.x86_64_regs.rdx ); + dump_uint64( ",rbp=", &ctx.integer.x86_64_regs.rbp ); dump_uint64( ",rsi=", &ctx.integer.x86_64_regs.rsi ); dump_uint64( ",rdi=", &ctx.integer.x86_64_regs.rdi ); dump_uint64( ",r8=", &ctx.integer.x86_64_regs.r8 ); diff --git a/tools/make_requests b/tools/make_requests index 53c57c1615c2..7f1c9db3c95f 100755 --- a/tools/make_requests +++ b/tools/make_requests @@ -42,7 +42,7 @@ my %formats = "union apc_call" => [ 64, 8 ], "union apc_result" => [ 40, 8 ], "struct async_data" => [ 40, 8 ], - "struct context_data" => [ 1728, 8 ], + "struct context_data" => [ 1720, 8 ], "struct cursor_pos" => [ 24, 8 ], "union debug_event_data" => [ 160, 8 ], "struct filesystem_event" => [ 12, 4 ], From 0a7371eb60cbb0f02b272747de0bd24360b91c8d Mon Sep 17 00:00:00 2001 From: Arkadiusz Hiler Date: Fri, 4 Jul 2025 15:18:55 +0300 Subject: [PATCH 002/454] cfgmgr32: Preserve registry casing when getting device interface list. This fixes SDL3 wrongly using their HIDAPI backend for XInput devices on Wine due to a case-sensitive check for `&IG_`. (cherry picked from commit f6309b3a885e08b06fd9ab7e8b2c6d68553b6971) CW-Bug-Id: #25572 --- dlls/cfgmgr32/tests/Makefile.in | 2 +- dlls/cfgmgr32/tests/cfgmgr32.c | 41 +++++++++++++++++++++++++++++++++ dlls/setupapi/devinst.c | 20 +++++++--------- 3 files changed, 50 insertions(+), 13 deletions(-) diff --git a/dlls/cfgmgr32/tests/Makefile.in b/dlls/cfgmgr32/tests/Makefile.in index 64a01ccf4923..90f5ac496a39 100644 --- a/dlls/cfgmgr32/tests/Makefile.in +++ b/dlls/cfgmgr32/tests/Makefile.in @@ -1,5 +1,5 @@ TESTDLL = cfgmgr32.dll -IMPORTS = cfgmgr32 setupapi ole32 uuid +IMPORTS = cfgmgr32 setupapi ole32 uuid advapi32 SOURCES = \ cfgmgr32.c diff --git a/dlls/cfgmgr32/tests/cfgmgr32.c b/dlls/cfgmgr32/tests/cfgmgr32.c index 1f3df8f68a20..ece23562483c 100644 --- a/dlls/cfgmgr32/tests/cfgmgr32.c +++ b/dlls/cfgmgr32/tests/cfgmgr32.c @@ -288,6 +288,46 @@ static void test_CM_Get_Device_ID_List(void) free(buf); } +static void check_device_path_casing(const WCHAR *original_path) +{ + HKEY current_key, tmp; + WCHAR *path = wcsdup(original_path); + WCHAR key_name[MAX_PATH]; + WCHAR separator[] = L"#"; + WCHAR *token, *context = NULL; + LSTATUS ret; + DWORD i; + + ret = RegOpenKeyW(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Enum", ¤t_key); + ok(!ret, "Failed to open enum key: %#lx.\n", ret); + + token = wcstok_s(path + 4, separator, &context); /* skip \\?\ */ + while (token) + { + if (token[0] == L'{' && wcslen(token) == 38) break; /* reached GUID part, done */ + + i = 0; + while (!(ret = RegEnumKeyW(current_key, i++, key_name, ARRAY_SIZE(key_name)))) + { + if(!wcscmp(token, key_name)) + { + ret = RegOpenKeyW(current_key, token, &tmp); + ok(!ret, "Failed to open registry key %s: %#lx.\n", debugstr_w(token), ret); + RegCloseKey(current_key); + current_key = tmp; + break; + } + } + ok(!ret, "Failed to find %s in registry: %#lx.\n", debugstr_w(token), ret); + if (ret) break; + + token = wcstok_s(NULL, separator, &context); + } + + RegCloseKey(current_key); + free(path); +} + static void test_CM_Get_Device_Interface_List(void) { BYTE iface_detail_buffer[sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_W) + 256 * sizeof(WCHAR)]; @@ -338,6 +378,7 @@ static void test_CM_Get_Device_Interface_List(void) p = buffer; while (*p) { + check_device_path_casing(p); set = SetupDiCreateDeviceInfoListExW(NULL, NULL, NULL, NULL); ok(set != INVALID_HANDLE_VALUE, "got %p.\n", set); bret = SetupDiOpenDeviceInterfaceW(set, p, 0, &iface); diff --git a/dlls/setupapi/devinst.c b/dlls/setupapi/devinst.c index f6675ee78409..1dc9f8237ca7 100644 --- a/dlls/setupapi/devinst.c +++ b/dlls/setupapi/devinst.c @@ -459,8 +459,6 @@ static LPWSTR SETUPDI_CreateSymbolicLinkPath(LPCWSTR instanceId, } } - CharLowerW(ret); - return ret; } @@ -3169,6 +3167,8 @@ BOOL WINAPI SetupDiGetDeviceInterfaceDetailA(HDEVINFO devinfo, SP_DEVICE_INTERFA else DeviceInterfaceDetailData->DevicePath[0] = '\0'; + CharLowerA(DeviceInterfaceDetailData->DevicePath); + ret = TRUE; } else @@ -3227,6 +3227,8 @@ BOOL WINAPI SetupDiGetDeviceInterfaceDetailW(HDEVINFO devinfo, SP_DEVICE_INTERFA else DeviceInterfaceDetailData->DevicePath[0] = '\0'; + CharLowerW(DeviceInterfaceDetailData->DevicePath); + ret = TRUE; } else @@ -4657,12 +4659,9 @@ static CONFIGRET get_device_interface_list(const GUID *class_guid, DEVINSTID_W d { const ULONG supported_flags = CM_GET_DEVICE_INTERFACE_LIST_ALL_DEVICES; - BYTE iface_detail_buffer[sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_W) + 256 * sizeof(WCHAR)]; SP_DEVICE_INTERFACE_DATA iface = {sizeof(iface)}; - SP_DEVICE_INTERFACE_DETAIL_DATA_W *iface_data; SP_DEVINFO_DATA device = { sizeof(device) }; ULONG query_flags = DIGCF_DEVICEINTERFACE; - CONFIGRET ret = CR_SUCCESS; unsigned int i, id_len; HDEVINFO set; ULONG needed; @@ -4689,15 +4688,12 @@ static CONFIGRET get_device_interface_list(const GUID *class_guid, DEVINSTID_W d if (set == INVALID_HANDLE_VALUE) return CR_SUCCESS; - iface_data = (SP_DEVICE_INTERFACE_DETAIL_DATA_W *)iface_detail_buffer; - iface_data->cbSize = sizeof(*iface_data); - p = buffer; for (i = 0; SetupDiEnumDeviceInterfaces(set, NULL, class_guid, i, &iface); ++i) { - ret = SetupDiGetDeviceInterfaceDetailW(set, &iface, iface_data, sizeof(iface_detail_buffer), NULL, &device); - if (!ret) continue; - id_len = wcslen(iface_data->DevicePath) + 1; + struct device_iface *device_iface; + device_iface = get_device_iface(set, &iface); + id_len = wcslen(device_iface->symlink) + 1; needed += id_len; if (buffer) { @@ -4707,7 +4703,7 @@ static CONFIGRET get_device_interface_list(const GUID *class_guid, DEVINSTID_W d *buffer = 0; return CR_BUFFER_SMALL; } - memcpy(p, iface_data->DevicePath, sizeof(*p) * id_len); + memcpy(p, device_iface->symlink, sizeof(*p) * id_len); p += id_len; } } From 51abe43c9d39cf62090bddeaca339a927111966a Mon Sep 17 00:00:00 2001 From: Conor McCarthy Date: Tue, 3 Jun 2025 13:23:52 +1000 Subject: [PATCH 003/454] rtworkq/tests: Test closing a timer or event handle after submission. Windows allows closure of a waitable timer handle while a work item is waiting on it. Also, the current Wine ntdll implementation calls NtWaitForMultipleObjects() on multiple handles if multiple items are pending, and if one handle is not valid, no items will execute. (cherry picked from commit b6b84ba26b0480ddd40a3a4c65590158b32b6d5c) --- dlls/rtworkq/tests/rtworkq.c | 72 +++++++++++++++++++++++++++++++++++- 1 file changed, 70 insertions(+), 2 deletions(-) diff --git a/dlls/rtworkq/tests/rtworkq.c b/dlls/rtworkq/tests/rtworkq.c index 7655275d7214..440a574aaf3c 100644 --- a/dlls/rtworkq/tests/rtworkq.c +++ b/dlls/rtworkq/tests/rtworkq.c @@ -475,11 +475,14 @@ static void test_work_queue(void) static void test_scheduled_items(void) { - struct test_callback *test_callback; + struct test_callback *test_callback, *test_callback2; RTWQWORKITEM_KEY key, key2; - IRtwqAsyncResult *result; + IRtwqAsyncResult *result, *result2, *callback_result; + HANDLE timer, event, event2; + LARGE_INTEGER time; ULONG refcount; HRESULT hr; + DWORD res; test_callback = create_test_callback(); @@ -527,7 +530,72 @@ static void test_scheduled_items(void) refcount = IRtwqAsyncResult_Release(result); ok(refcount == 0, "Unexpected refcount %lu.\n", refcount); + hr = RtwqStartup(); + ok(hr == S_OK, "Failed to start up, hr %#lx.\n", hr); + + test_callback2 = create_test_callback(); + + timer = CreateWaitableTimerW(NULL, TRUE, NULL); + event = CreateEventA(NULL, FALSE, FALSE, NULL); + + hr = RtwqCreateAsyncResult(NULL, &test_callback->IRtwqAsyncCallback_iface, NULL, &result); + ok(hr == S_OK, "Failed to create result, hr %#lx.\n", hr); + hr = RtwqCreateAsyncResult(NULL, &test_callback2->IRtwqAsyncCallback_iface, NULL, &result2); + ok(hr == S_OK, "Failed to create result, hr %#lx.\n", hr); + + time.QuadPart = -1000000LL; + SetWaitableTimer(timer, &time, 0, NULL, NULL, 0); + hr = RtwqPutWaitingWorkItem(timer, 0, result, NULL); + ok(hr == S_OK, "got %#lx\n", hr); + /* Close the timer handle while the item is pending. This should work and + * not cause failure to execute other waiting items.*/ + CloseHandle(timer); + IRtwqAsyncResult_Release(result); + + hr = RtwqPutWaitingWorkItem(event, 0, result2, NULL); + ok(hr == S_OK, "got %#lx\n", hr); + IRtwqAsyncResult_Release(result2); + res = wait_async_callback_result(&test_callback->IRtwqAsyncCallback_iface, 200, &callback_result); + todo_wine + ok(res == 0, "got %#lx\n", res); + + SetEvent(event); + res = wait_async_callback_result(&test_callback2->IRtwqAsyncCallback_iface, 100, &callback_result); + ok(res == 0, "got %#lx\n", res); + + CloseHandle(event); + + event = CreateEventA(NULL, FALSE, FALSE, NULL); + event2 = CreateEventA(NULL, FALSE, FALSE, NULL); + + hr = RtwqCreateAsyncResult(NULL, &test_callback->IRtwqAsyncCallback_iface, NULL, &result); + ok(hr == S_OK, "Failed to create result, hr %#lx.\n", hr); + hr = RtwqCreateAsyncResult(NULL, &test_callback2->IRtwqAsyncCallback_iface, NULL, &result2); + ok(hr == S_OK, "Failed to create result, hr %#lx.\n", hr); + + hr = RtwqPutWaitingWorkItem(event, 0, result, &key); + ok(hr == S_OK, "got %#lx\n", hr); + /* Abandon the waiting item. This should not cause failure to execute other waiting items. */ + CloseHandle(event); + IRtwqAsyncResult_Release(result); + + hr = RtwqPutWaitingWorkItem(event2, 0, result2, NULL); + ok(hr == S_OK, "got %#lx\n", hr); + IRtwqAsyncResult_Release(result2); + SetEvent(event2); + res = wait_async_callback_result(&test_callback2->IRtwqAsyncCallback_iface, 100, &callback_result); + todo_wine + ok(res == 0, "got %#lx\n", res); + + hr = RtwqCancelWorkItem(key); + ok(hr == S_OK, "Failed to cancel item, hr %#lx.\n", hr); + CloseHandle(event2); + + hr = RtwqShutdown(); + ok(hr == S_OK, "Failed to shut down, hr %#lx.\n", hr); + IRtwqAsyncCallback_Release(&test_callback->IRtwqAsyncCallback_iface); + IRtwqAsyncCallback_Release(&test_callback2->IRtwqAsyncCallback_iface); } static void test_queue_shutdown(void) From a26e1c1720a7bbf2dd8feb7deb34d319a6819578 Mon Sep 17 00:00:00 2001 From: Conor McCarthy Date: Wed, 4 Jun 2025 15:06:30 +1000 Subject: [PATCH 004/454] ntdll/tests: Test early closure of handles used for threadpool waits. (cherry picked from commit e42d2efe140b0ae2c97a672e3b5750e943ff9bba) --- dlls/ntdll/tests/threadpool.c | 91 +++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) diff --git a/dlls/ntdll/tests/threadpool.c b/dlls/ntdll/tests/threadpool.c index cea06fed7917..637a5d14de08 100644 --- a/dlls/ntdll/tests/threadpool.c +++ b/dlls/ntdll/tests/threadpool.c @@ -2387,6 +2387,96 @@ static void test_kernel32_tp_io(void) pTpReleasePool(pool); } +static void test_tp_wait_early_closure(void) +{ + TP_CALLBACK_ENVIRON environment; + TP_WAIT *wait1, *wait2; + struct wait_info info; + HANDLE semaphores[2]; + LARGE_INTEGER when; + HANDLE semaphore; + NTSTATUS status; + TP_POOL *pool; + HANDLE timer; + DWORD result; + + semaphores[0] = CreateSemaphoreW(NULL, 0, 2, NULL); + ok(semaphores[0] != NULL, "failed to create semaphore\n"); + semaphores[1] = CreateSemaphoreW(NULL, 0, 1, NULL); + ok(semaphores[1] != NULL, "failed to create semaphore\n"); + semaphore = CreateSemaphoreW(NULL, 0, 1, NULL); + ok(semaphore != NULL, "failed to create semaphore\n"); + timer = CreateWaitableTimerW(NULL, TRUE, NULL); + ok(timer != NULL, "failed to create waitable timer\n"); + info.semaphore = semaphore; + + /* allocate new threadpool */ + pool = NULL; + status = pTpAllocPool(&pool, NULL); + ok(!status, "TpAllocPool failed with status %lx\n", status); + ok(pool != NULL, "expected pool != NULL\n"); + + /* allocate new wait items */ + memset(&environment, 0, sizeof(environment)); + environment.Version = 1; + environment.Pool = pool; + + wait1 = NULL; + status = pTpAllocWait(&wait1, wait_cb, &info, &environment); + ok(!status, "TpAllocWait failed with status %lx\n", status); + ok(wait1 != NULL, "expected wait1 != NULL\n"); + + wait2 = NULL; + status = pTpAllocWait(&wait2, wait_cb, &info, &environment); + ok(!status, "TpAllocWait failed with status %lx\n", status); + ok(wait2 != NULL, "expected wait2 != NULL\n"); + + /* waitable timer closed immediately, and a semaphore */ + when.QuadPart = (ULONGLONG)100 * -10000; + status = NtSetTimer(timer, &when, NULL, NULL, FALSE, 0, NULL); + ok(!status, "NtSetTimer returned status %lx\n", status); + info.userdata = 0; + pTpSetWait(wait1, timer, NULL); + CloseHandle(timer); + pTpSetWait(wait2, semaphores[0], NULL); + result = WaitForSingleObject(semaphore, 200); + todo_wine + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %#lx\n", result); + ok(info.userdata == 1, "expected info.userdata = 1, got %lu\n", info.userdata); + ReleaseSemaphore(semaphores[0], 1, NULL); + result = WaitForSingleObject(semaphore, 200); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %#lx\n", result); + todo_wine + ok(info.userdata == 2, "expected info.userdata = 2, got %lu\n", info.userdata); + result = WaitForSingleObject(semaphores[0], 0); + ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %#lx\n", result); + + /* two semaphores, the first closed immediately */ + info.userdata = 0; + when.QuadPart = (ULONGLONG)200 * -10000; + pTpSetWait(wait1, semaphores[0], &when); + CloseHandle(semaphores[0]); + pTpSetWait(wait2, semaphores[1], NULL); + ReleaseSemaphore(semaphores[1], 1, NULL); + result = WaitForSingleObject(semaphore, 100); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %#lx\n", result); + todo_wine + ok(info.userdata == 1, "expected info.userdata = 1, got %lu\n", info.userdata); + result = WaitForSingleObject(semaphores[1], 0); + ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %#lx\n", result); + result = WaitForSingleObject(semaphore, 300); + ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %#lx\n", result); + todo_wine + ok(info.userdata == 0x10001, "expected info.userdata = 0x10001, got %lu\n", info.userdata); + + /* cleanup */ + pTpReleaseWait(wait1); + pTpReleaseWait(wait2); + pTpReleasePool(pool); + CloseHandle(semaphores[1]); + CloseHandle(semaphore); +} + START_TEST(threadpool) { test_RtlQueueWorkItem(); @@ -2408,4 +2498,5 @@ START_TEST(threadpool) test_tp_multi_wait(); test_tp_io(); test_kernel32_tp_io(); + test_tp_wait_early_closure(); } From 81b6b5b7eabb876cf56cf7a1e7574de64f2536ae Mon Sep 17 00:00:00 2001 From: Conor McCarthy Date: Tue, 3 Jun 2025 14:49:17 +1000 Subject: [PATCH 005/454] ntdll: Initialise waitable handles with NULL. All waitable objects use NULL as their invalid value. (cherry picked from commit 069708c884c8f59d0962a9bd941c277b211e2484) --- dlls/ntdll/threadpool.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/ntdll/threadpool.c b/dlls/ntdll/threadpool.c index 81cf894d9439..6448477a5cbf 100644 --- a/dlls/ntdll/threadpool.c +++ b/dlls/ntdll/threadpool.c @@ -1436,7 +1436,7 @@ static NTSTATUS tp_waitqueue_lock( struct threadpool_object *wait ) wait->u.wait.bucket = NULL; wait->u.wait.wait_pending = FALSE; wait->u.wait.timeout = 0; - wait->u.wait.handle = INVALID_HANDLE_VALUE; + wait->u.wait.handle = NULL; RtlEnterCriticalSection( &waitqueue.cs ); From cccf87e663d1679aec5d26602a87abef9cb39847 Mon Sep 17 00:00:00 2001 From: Conor McCarthy Date: Tue, 3 Jun 2025 14:53:43 +1000 Subject: [PATCH 006/454] ntdll: Duplicate handles for thread pool waits. Supports waitable timer closure while pending, and we must not wait on invalid handles generally. Details are in code comments. (cherry picked from commit acf72ed171a34b631b4a07013bb82bcc78ff1884) --- dlls/ntdll/tests/threadpool.c | 4 ---- dlls/ntdll/threadpool.c | 25 ++++++++++++++++++++++++- dlls/rtworkq/tests/rtworkq.c | 2 -- 3 files changed, 24 insertions(+), 7 deletions(-) diff --git a/dlls/ntdll/tests/threadpool.c b/dlls/ntdll/tests/threadpool.c index 637a5d14de08..c3f327aab7f6 100644 --- a/dlls/ntdll/tests/threadpool.c +++ b/dlls/ntdll/tests/threadpool.c @@ -2440,13 +2440,11 @@ static void test_tp_wait_early_closure(void) CloseHandle(timer); pTpSetWait(wait2, semaphores[0], NULL); result = WaitForSingleObject(semaphore, 200); - todo_wine ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %#lx\n", result); ok(info.userdata == 1, "expected info.userdata = 1, got %lu\n", info.userdata); ReleaseSemaphore(semaphores[0], 1, NULL); result = WaitForSingleObject(semaphore, 200); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %#lx\n", result); - todo_wine ok(info.userdata == 2, "expected info.userdata = 2, got %lu\n", info.userdata); result = WaitForSingleObject(semaphores[0], 0); ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %#lx\n", result); @@ -2460,13 +2458,11 @@ static void test_tp_wait_early_closure(void) ReleaseSemaphore(semaphores[1], 1, NULL); result = WaitForSingleObject(semaphore, 100); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %#lx\n", result); - todo_wine ok(info.userdata == 1, "expected info.userdata = 1, got %lu\n", info.userdata); result = WaitForSingleObject(semaphores[1], 0); ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %#lx\n", result); result = WaitForSingleObject(semaphore, 300); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %#lx\n", result); - todo_wine ok(info.userdata == 0x10001, "expected info.userdata = 0x10001, got %lu\n", info.userdata); /* cleanup */ diff --git a/dlls/ntdll/threadpool.c b/dlls/ntdll/threadpool.c index 6448477a5cbf..5fb11c356989 100644 --- a/dlls/ntdll/threadpool.c +++ b/dlls/ntdll/threadpool.c @@ -194,6 +194,7 @@ struct threadpool_object struct list wait_entry; ULONGLONG timeout; HANDLE handle; + HANDLE duped_handle; DWORD flags; RTL_WAITORTIMERCALLBACKFUNC rtl_callback; } wait; @@ -1296,7 +1297,10 @@ static void CALLBACK waitqueue_thread_proc( void *param ) assert( num_handles < MAXIMUM_WAITQUEUE_OBJECTS ); InterlockedIncrement( &wait->refcount ); objects[num_handles] = wait; - handles[num_handles] = wait->u.wait.handle; + /* NtWaitForMultipleObjects() fails if any invalid handles are passed, and one invalid handle + * should not affect other waiting items. The calling app is allowed to close waitable timer + * handles immediately after submission, so we need a duplicate for those in particular. */ + handles[num_handles] = wait->u.wait.duped_handle ? wait->u.wait.duped_handle : wait->u.wait.handle; update_serials[num_handles] = wait->update_serial; num_handles++; } @@ -1437,6 +1441,7 @@ static NTSTATUS tp_waitqueue_lock( struct threadpool_object *wait ) wait->u.wait.wait_pending = FALSE; wait->u.wait.timeout = 0; wait->u.wait.handle = NULL; + wait->u.wait.duped_handle = NULL; RtlEnterCriticalSection( &waitqueue.cs ); @@ -2117,6 +2122,15 @@ static void tp_object_prepare_shutdown( struct threadpool_object *object ) tp_ioqueue_unlock( object ); } +static void tp_wait_close_duped_handle( struct threadpool_object *wait ) +{ + if (wait->u.wait.duped_handle) + { + NtClose( wait->u.wait.duped_handle ); + wait->u.wait.duped_handle = NULL; + } +} + /*********************************************************************** * tp_object_release (internal) * @@ -2150,6 +2164,9 @@ static BOOL tp_object_release( struct threadpool_object *object ) tp_group_release( group ); } + if (object->type == TP_OBJECT_TYPE_WAIT) + tp_wait_close_duped_handle( object ); + tp_threadpool_unlock( object->pool ); if (object->race_dll) @@ -3067,6 +3084,12 @@ VOID WINAPI TpSetWait( TP_WAIT *wait, HANDLE handle, LARGE_INTEGER *timeout ) assert( this->u.wait.bucket ); same_handle = this->u.wait.handle == handle; + tp_wait_close_duped_handle( this ); + if (handle && NtDuplicateObject( NtCurrentProcess(), handle, NtCurrentProcess(), + &this->u.wait.duped_handle, 0, 0, DUPLICATE_SAME_ACCESS ) != STATUS_SUCCESS) + { + WARN( "Failed to duplicate handle.\n" ); + } this->u.wait.handle = handle; if (handle || this->u.wait.wait_pending) diff --git a/dlls/rtworkq/tests/rtworkq.c b/dlls/rtworkq/tests/rtworkq.c index 440a574aaf3c..812c1f03c1e5 100644 --- a/dlls/rtworkq/tests/rtworkq.c +++ b/dlls/rtworkq/tests/rtworkq.c @@ -556,7 +556,6 @@ static void test_scheduled_items(void) ok(hr == S_OK, "got %#lx\n", hr); IRtwqAsyncResult_Release(result2); res = wait_async_callback_result(&test_callback->IRtwqAsyncCallback_iface, 200, &callback_result); - todo_wine ok(res == 0, "got %#lx\n", res); SetEvent(event); @@ -584,7 +583,6 @@ static void test_scheduled_items(void) IRtwqAsyncResult_Release(result2); SetEvent(event2); res = wait_async_callback_result(&test_callback2->IRtwqAsyncCallback_iface, 100, &callback_result); - todo_wine ok(res == 0, "got %#lx\n", res); hr = RtwqCancelWorkItem(key); From 65d0f5edbcaef7b658316ba2bb89f2b341410ae7 Mon Sep 17 00:00:00 2001 From: Conor McCarthy Date: Mon, 7 Jul 2025 13:53:55 +1000 Subject: [PATCH 007/454] winegstreamer: HACK: Restore the transcoded audio info if present in user data. CW-Bug-Id: #25538 --- dlls/winegstreamer/wg_transform.c | 32 ++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/dlls/winegstreamer/wg_transform.c b/dlls/winegstreamer/wg_transform.c index 917af7ebce9f..66cbbe0d825e 100644 --- a/dlls/winegstreamer/wg_transform.c +++ b/dlls/winegstreamer/wg_transform.c @@ -674,6 +674,7 @@ static bool transform_create_encoder_element(struct wg_transform *transform, NTSTATUS wg_transform_create(void *args) { struct wg_transform_create_params *params = args; + struct wg_media_type input_type; GstElement *first = NULL, *last = NULL; NTSTATUS status = STATUS_UNSUCCESSFUL; const gchar *input_mime, *output_mime; @@ -695,7 +696,32 @@ NTSTATUS wg_transform_create(void *args) goto out; transform->attrs = params->attrs; - if (!(transform->input_caps = caps_from_media_type(¶ms->input_type))) + memcpy(&input_type, ¶ms->input_type, sizeof(input_type)); + + if (IsEqualGUID(&input_type.major, &MFMediaType_Audio)) + { + size_t data_size = input_type.u.audio->cbSize + sizeof(WAVEFORMATEX) - sizeof(HEAACWAVEINFO); + + /* If an mfsrcsnk hack appended transcoded audio info to the user data, then restore it. + * This happens if the game depends on the input format belonging to a specific set of formats. */ + if (input_type.u.audio->wFormatTag == WAVE_FORMAT_MPEG_HEAAC + && data_size >= sizeof(WAVEFORMATEXTENSIBLE)) + { + const HEAACWAVEFORMAT *hwf = (HEAACWAVEFORMAT *)input_type.u.audio; + WAVEFORMATEXTENSIBLE audio; + + memcpy(&audio, &hwf->pbAudioSpecificConfig[data_size - sizeof(audio)], sizeof(audio)); + if (audio.Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE + && IsEqualGUID(&audio.SubFormat, &MFAudioFormat_Vorbis)) + { + memmove((WAVEFORMATEXTENSIBLE *)input_type.u.audio + 1, hwf->pbAudioSpecificConfig, + data_size - sizeof(audio)); + memcpy(input_type.u.audio, &audio, sizeof(audio)); + } + } + } + + if (!(transform->input_caps = caps_from_media_type(&input_type))) goto out; GST_INFO("transform %p input caps %"GST_PTR_FORMAT, transform, transform->input_caps); input_mime = gst_structure_get_name(gst_caps_get_structure(transform->input_caps, 0)); @@ -708,8 +734,8 @@ NTSTATUS wg_transform_create(void *args) GST_INFO("transform %p output caps %"GST_PTR_FORMAT, transform, transform->output_caps); output_mime = gst_structure_get_name(gst_caps_get_structure(transform->output_caps, 0)); - if (IsEqualGUID(¶ms->input_type.major, &MFMediaType_Video)) - transform->input_info = params->input_type.u.video->videoInfo; + if (IsEqualGUID(&input_type.major, &MFMediaType_Video)) + transform->input_info = input_type.u.video->videoInfo; if (IsEqualGUID(¶ms->output_type.major, &MFMediaType_Video)) transform->output_info = params->output_type.u.video->videoInfo; From 9dd35766c35095192c9f947c30f5816d29549fd0 Mon Sep 17 00:00:00 2001 From: Conor McCarthy Date: Mon, 7 Jul 2025 14:10:33 +1000 Subject: [PATCH 008/454] mfsrcsnk: HACK: Append transcoded audio info to user data and return AAC for Warhammer 40,000: Dakka Squadron. CW-Bug-Id: #25538 --- dlls/mfsrcsnk/media_source.c | 38 +++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/dlls/mfsrcsnk/media_source.c b/dlls/mfsrcsnk/media_source.c index 6ef8a3f8f4cb..cf40cb084c7a 100644 --- a/dlls/mfsrcsnk/media_source.c +++ b/dlls/mfsrcsnk/media_source.c @@ -1462,8 +1462,44 @@ static HRESULT media_type_from_winedmo_format( GUID major, union winedmo_format if (IsEqualGUID( &major, &MFMediaType_Video )) return media_type_from_mf_video_format( &format->video, media_type ); + if (IsEqualGUID( &major, &MFMediaType_Audio )) - return MFCreateAudioMediaType( &format->audio, (IMFAudioMediaType **)media_type ); + { + const char *sgi = getenv("SteamGameId"); + WAVEFORMATEXTENSIBLE *audio = (WAVEFORMATEXTENSIBLE *)&format->audio; + + /* Warhammer 40,000: Dakka Squadron depends on the input format belonging to a specific set of formats. + * Append transcoded audio info to the user data so it can be restored, and create a fake AAC media + * type instead. If decoding support is added, PCM will work without a hack. */ + if (!strcmp(sgi, "1253190") && format->audio.wFormatTag == WAVE_FORMAT_EXTENSIBLE + && IsEqualGUID(&audio->SubFormat, &MFAudioFormat_Vorbis)) + { + size_t config_data_size = format->audio.cbSize + sizeof(WAVEFORMATEX) - sizeof(WAVEFORMATEXTENSIBLE); + size_t data_size = config_data_size + sizeof(WAVEFORMATEXTENSIBLE); + HEAACWAVEFORMAT *hwf; + HRESULT hr; + + if (!(hwf = malloc(offsetof(HEAACWAVEFORMAT, pbAudioSpecificConfig[data_size])))) + return E_OUTOFMEMORY; + + hwf->wfInfo.wfx = audio->Format; + hwf->wfInfo.wfx.wFormatTag = WAVE_FORMAT_MPEG_HEAAC; + hwf->wfInfo.wfx.cbSize = sizeof(HEAACWAVEINFO) + data_size - sizeof(WAVEFORMATEX); + hwf->wfInfo.wPayloadType = 0; + hwf->wfInfo.wAudioProfileLevelIndication = 0; + hwf->wfInfo.wStructType = 0; + hwf->wfInfo.wReserved1 = 0; + hwf->wfInfo.dwReserved2 = 0; + memcpy(hwf->pbAudioSpecificConfig, (BYTE *)(audio + 1), config_data_size); + memcpy(&hwf->pbAudioSpecificConfig[config_data_size], audio, sizeof(*audio)); + + hr = MFCreateAudioMediaType((WAVEFORMATEX *)hwf, (IMFAudioMediaType **)media_type); + free(hwf); + return hr; + } + + return MFCreateAudioMediaType(&format->audio, (IMFAudioMediaType **)media_type); + } FIXME( "Unsupported major type %s\n", debugstr_guid( &major ) ); return E_NOTIMPL; From fff0e896a2fa532c1ddf12ed9027500089cf8e6b Mon Sep 17 00:00:00 2001 From: Conor McCarthy Date: Fri, 11 Jul 2025 00:08:41 +1000 Subject: [PATCH 009/454] winegstreamer: HACK: Add support for > 2 opus channels. Needed for playing av1 transcoded videos if the channel count is high enough. CW-Bug-Id: #25560 --- dlls/winegstreamer/wg_media_type.c | 35 +++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/dlls/winegstreamer/wg_media_type.c b/dlls/winegstreamer/wg_media_type.c index abde06bca979..101f320f70a2 100644 --- a/dlls/winegstreamer/wg_media_type.c +++ b/dlls/winegstreamer/wg_media_type.c @@ -182,7 +182,9 @@ static void init_caps_from_wave_format_vorbis(GstCaps *caps, const WAVEFORMATEX static void init_caps_from_wave_format_opus(GstCaps *caps, const WAVEFORMATEX *format, UINT32 format_size) { - init_caps_codec_data(caps, format + 1, format->cbSize); + const guint8 *codec_data = (const guint8 *)(format + 1); + + init_caps_codec_data(caps, codec_data, format->cbSize); gst_structure_remove_field(gst_caps_get_structure(caps, 0), "format"); gst_structure_set_name(gst_caps_get_structure(caps, 0), "audio/x-opus"); @@ -190,6 +192,37 @@ static void init_caps_from_wave_format_opus(GstCaps *caps, const WAVEFORMATEX *f gst_caps_set_simple(caps, "block_align", G_TYPE_INT, format->nBlockAlign, NULL); gst_caps_set_simple(caps, "depth", G_TYPE_INT, format->wBitsPerSample, NULL); gst_caps_set_simple(caps, "bitrate", G_TYPE_INT, format->nAvgBytesPerSec * 8, NULL); + + if (format->nChannels > 2) + { + GValue mapping = G_VALUE_INIT; + GValue v = G_VALUE_INIT; + gint i; + + if (format->cbSize < 21 + format->nChannels) + { + GST_WARNING("Invalid extra data size %u", format->cbSize); + return; + } + + /* Decoding > 2 channels requires additional values, + * found in the extra data at fixed offsets. */ + gst_caps_set_simple(caps, "channel-mapping-family", G_TYPE_INT, codec_data[18], NULL); + gst_caps_set_simple(caps, "stream-count", G_TYPE_INT, codec_data[19], NULL); + gst_caps_set_simple(caps, "coupled-count", G_TYPE_INT, codec_data[20], NULL); + + g_value_init(&mapping, GST_TYPE_ARRAY); + g_value_init(&v, G_TYPE_INT); + for (i = 0; i < format->nChannels; ++i) + { + g_value_set_int(&v, codec_data[21 + i]); + gst_value_array_append_value(&mapping, &v); + } + + gst_structure_set_value(gst_caps_get_structure(caps, 0), "channel-mapping", &mapping); + g_value_unset(&mapping); + g_value_unset(&v); + } } static void init_caps_from_wave_format(GstCaps *caps, const GUID *subtype, From 955e74b6c22669348ce023f7e10560be6274b548 Mon Sep 17 00:00:00 2001 From: Brendan McGrath Date: Fri, 27 Jun 2025 07:29:55 +1000 Subject: [PATCH 010/454] mfplat: HACK: Fix performance regression in RE:7. Optimise 1d Lock of 2d buffer when pitch equals width. When the pitch and width are the same, the original 2D buffer can be used. This saves a potentially expensive allocation and memory copy. This does not match Windows behaviour though, so this is a hack. CW-Bug-Id: #25609 --- dlls/mfplat/buffer.c | 40 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/dlls/mfplat/buffer.c b/dlls/mfplat/buffer.c index 76894d311751..219cbae40f30 100644 --- a/dlls/mfplat/buffer.c +++ b/dlls/mfplat/buffer.c @@ -290,10 +290,14 @@ static HRESULT WINAPI memory_1d_2d_buffer_QueryInterface(IMFMediaBuffer *iface, return S_OK; } +static HRESULT memory_2d_buffer_lock(struct buffer *buffer, BYTE **scanline0, LONG *pitch, + BYTE **buffer_start, DWORD *buffer_length); + static HRESULT WINAPI memory_1d_2d_buffer_Lock(IMFMediaBuffer *iface, BYTE **data, DWORD *max_length, DWORD *current_length) { struct buffer *buffer = impl_from_IMFMediaBuffer(iface); HRESULT hr = S_OK; + const char *sgi; TRACE("%p, %p, %p, %p.\n", iface, data, max_length, current_length); @@ -305,8 +309,22 @@ static HRESULT WINAPI memory_1d_2d_buffer_Lock(IMFMediaBuffer *iface, BYTE **dat EnterCriticalSection(&buffer->cs); - if (!buffer->_2d.linear_buffer && buffer->_2d.locks) + if (!buffer->_2d.linear_buffer && buffer->_2d.width == buffer->_2d.pitch + && (sgi = getenv("SteamGameId")) && !strcmp(sgi, "418370")) + { + BYTE *scanline; + LONG pitch; + + /* width and pitch are the same, so this avoids a potentially expensive copy + * this is a HACK as it does not match Windows behaviour (Windows will copy the buffer) + * this fixes a performance regression for Resident Evil 7 Biohazard (418370) + */ + hr = memory_2d_buffer_lock(buffer, &scanline, &pitch, data, NULL); + } + else if (!buffer->_2d.linear_buffer && buffer->_2d.locks) + { hr = MF_E_INVALIDREQUEST; + } else if (!buffer->_2d.linear_buffer) { if (!(buffer->_2d.linear_buffer = malloc(buffer->_2d.plane_size))) @@ -323,10 +341,14 @@ static HRESULT WINAPI memory_1d_2d_buffer_Lock(IMFMediaBuffer *iface, BYTE **dat } } - if (SUCCEEDED(hr)) + if (SUCCEEDED(hr) && buffer->_2d.linear_buffer) { ++buffer->_2d.locks; *data = buffer->_2d.linear_buffer; + } + + if (SUCCEEDED(hr)) + { if (max_length) *max_length = buffer->_2d.plane_size; if (current_length) @@ -341,12 +363,22 @@ static HRESULT WINAPI memory_1d_2d_buffer_Lock(IMFMediaBuffer *iface, BYTE **dat static HRESULT WINAPI memory_1d_2d_buffer_Unlock(IMFMediaBuffer *iface) { struct buffer *buffer = impl_from_IMFMediaBuffer(iface); + HRESULT hr = S_OK; + const char *sgi; TRACE("%p.\n", iface); EnterCriticalSection(&buffer->cs); - if (buffer->_2d.linear_buffer && !--buffer->_2d.locks) + if (!buffer->_2d.linear_buffer && buffer->_2d.width == buffer->_2d.pitch + && (sgi = getenv("SteamGameId")) && !strcmp(sgi, "418370")) + { + if (buffer->_2d.locks) + --buffer->_2d.locks; + else + hr = HRESULT_FROM_WIN32(ERROR_WAS_UNLOCKED); + } + else if (buffer->_2d.linear_buffer && !--buffer->_2d.locks) { int pitch = buffer->_2d.pitch; @@ -361,7 +393,7 @@ static HRESULT WINAPI memory_1d_2d_buffer_Unlock(IMFMediaBuffer *iface) LeaveCriticalSection(&buffer->cs); - return S_OK; + return hr; } static const IMFMediaBufferVtbl memory_1d_2d_buffer_vtbl = From a8e3b7e32bf9376cea0c3e4e23fbfb1ddec2aca6 Mon Sep 17 00:00:00 2001 From: Brendan McGrath Date: Wed, 28 May 2025 14:57:31 +1000 Subject: [PATCH 011/454] mfreadwrite: Fix media type output when video processor is used. (cherry picked from commit 5bbe08a78c54eeb1f8528ca41ac0cd99928cccf7) CW-Bug-Id: #25440 --- dlls/mfreadwrite/reader.c | 61 ++++++++++++++++++++++++++++++--- dlls/mfreadwrite/tests/mfplat.c | 2 ++ 2 files changed, 59 insertions(+), 4 deletions(-) diff --git a/dlls/mfreadwrite/reader.c b/dlls/mfreadwrite/reader.c index 6a1013ccd6ce..ba58e0b007e8 100644 --- a/dlls/mfreadwrite/reader.c +++ b/dlls/mfreadwrite/reader.c @@ -688,14 +688,13 @@ static void media_type_try_copy_attr(IMFMediaType *dst, IMFMediaType *src, const /* update a media type with additional attributes reported by upstream element */ /* also present in mf/topology_loader.c pipeline */ -static HRESULT update_media_type_from_upstream(IMFMediaType *media_type, IMFMediaType *upstream_type) +static HRESULT update_media_type_from_upstream(IMFMediaType *media_type, IMFMediaType *upstream_type, BOOL advanced) { HRESULT hr = S_OK; /* propagate common video attributes */ media_type_try_copy_attr(media_type, upstream_type, &MF_MT_FRAME_SIZE, &hr); media_type_try_copy_attr(media_type, upstream_type, &MF_MT_FRAME_RATE, &hr); - media_type_try_copy_attr(media_type, upstream_type, &MF_MT_DEFAULT_STRIDE, &hr); media_type_try_copy_attr(media_type, upstream_type, &MF_MT_VIDEO_ROTATION, &hr); media_type_try_copy_attr(media_type, upstream_type, &MF_MT_FIXED_SIZE_SAMPLES, &hr); media_type_try_copy_attr(media_type, upstream_type, &MF_MT_PIXEL_ASPECT_RATIO, &hr); @@ -710,6 +709,9 @@ static HRESULT update_media_type_from_upstream(IMFMediaType *media_type, IMFMedi media_type_try_copy_attr(media_type, upstream_type, &MF_MT_VIDEO_LIGHTING, &hr); media_type_try_copy_attr(media_type, upstream_type, &MF_MT_VIDEO_NOMINAL_RANGE, &hr); + if (!advanced) + media_type_try_copy_attr(media_type, upstream_type, &MF_MT_DEFAULT_STRIDE, &hr); + /* propagate common audio attributes */ media_type_try_copy_attr(media_type, upstream_type, &MF_MT_AUDIO_NUM_CHANNELS, &hr); media_type_try_copy_attr(media_type, upstream_type, &MF_MT_AUDIO_BLOCK_ALIGNMENT, &hr); @@ -1899,6 +1901,52 @@ static BOOL source_reader_allow_video_processor(struct source_reader *reader, BO return *advanced; } +static void mediatype_set_uint32(IMFMediaType *mediatype, const GUID *attr, unsigned int value, HRESULT *hr) +{ + if (SUCCEEDED(*hr)) + *hr = IMFMediaType_SetUINT32(mediatype, attr, value); +} + +static void mediatype_get_stride_and_sample_size(IMFMediaType *mediatype, LONG *stride, DWORD *sample_size, HRESULT *hr) +{ + UINT64 frame_size; + GUID subtype; + + if (SUCCEEDED(*hr)) + *hr = IMFMediaType_GetGUID(mediatype, &MF_MT_SUBTYPE, &subtype); + + if (SUCCEEDED(*hr)) + *hr = IMFMediaType_GetUINT64(mediatype, &MF_MT_FRAME_SIZE, &frame_size); + + if (SUCCEEDED(*hr)) + *hr = MFGetStrideForBitmapInfoHeader(subtype.Data1, frame_size >> 32, stride); + + if (SUCCEEDED(*hr)) + *hr = MFGetPlaneSize(subtype.Data1, frame_size >> 32, frame_size & 0xffffffff, sample_size); +} + +static HRESULT set_default_video_attributes(struct source_reader *reader, IMFMediaType *output_type) +{ + DWORD sample_size; + BOOL compressed; + LONG stride; + HRESULT hr; + + if (FAILED(hr = IMFMediaType_IsCompressedFormat(output_type, &compressed))) + return hr; + + if (!compressed) + { + mediatype_get_stride_and_sample_size(output_type, &stride, &sample_size, &hr); + + mediatype_set_uint32(output_type, &MF_MT_COMPRESSED, compressed, &hr); + mediatype_set_uint32(output_type, &MF_MT_DEFAULT_STRIDE, abs(stride), &hr); + mediatype_set_uint32(output_type, &MF_MT_SAMPLE_SIZE, sample_size, &hr); + } + + return hr; +} + static HRESULT source_reader_create_transform(struct source_reader *reader, BOOL decoder, BOOL allow_processor, IMFMediaType *input_type, IMFMediaType *output_type, struct transform_entry **out) { @@ -2008,7 +2056,11 @@ static HRESULT source_reader_create_transform(struct source_reader *reader, BOOL if (SUCCEEDED(hr = IMFTransform_SetInputType(transform, 0, input_type, 0)) && SUCCEEDED(hr = IMFTransform_GetInputCurrentType(transform, 0, &media_type))) { - if (SUCCEEDED(hr = update_media_type_from_upstream(output_type, media_type)) + BOOL enable_advanced; + + source_reader_allow_video_processor(reader, &enable_advanced); + + if ((SUCCEEDED(hr = update_media_type_from_upstream(output_type, media_type, enable_advanced))) && FAILED(hr = IMFTransform_SetOutputType(transform, 0, output_type, 0)) && FAILED(hr = set_matching_transform_output_type(transform, output_type)) && allow_processor && SUCCEEDED(hr = IMFTransform_GetOutputAvailableType(transform, 0, 0, &media_type))) @@ -2016,7 +2068,8 @@ static HRESULT source_reader_create_transform(struct source_reader *reader, BOOL struct transform_entry *converter; if (SUCCEEDED(hr = IMFTransform_SetOutputType(transform, 0, media_type, 0)) - && SUCCEEDED(hr = update_media_type_from_upstream(output_type, media_type)) + && SUCCEEDED(hr = update_media_type_from_upstream(output_type, media_type, enable_advanced)) + && (enable_advanced || SUCCEEDED(hr = set_default_video_attributes(reader, output_type))) && SUCCEEDED(hr = source_reader_create_transform(reader, FALSE, FALSE, media_type, output_type, &converter))) list_add_tail(&entry->entry, &converter->entry); diff --git a/dlls/mfreadwrite/tests/mfplat.c b/dlls/mfreadwrite/tests/mfplat.c index e90931253fc8..2e75e5089b2c 100644 --- a/dlls/mfreadwrite/tests/mfplat.c +++ b/dlls/mfreadwrite/tests/mfplat.c @@ -1807,6 +1807,8 @@ static void test_source_reader_transforms(BOOL enable_processing, BOOL enable_ad ATTR_UINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1), ATTR_UINT32(MF_MT_COMPRESSED, 0, .todo = TRUE), ATTR_UINT32(MF_MT_INTERLACE_MODE, 2, .todo_value = TRUE), + ATTR_UINT32(MF_MT_DEFAULT_STRIDE, 96, .not_present = TRUE), + {0}, }; IMFStreamDescriptor *video_stream; IMFSourceReaderEx *reader_ex; From 7ee51621ed43b7b4e506487a75c2f6fc86c72172 Mon Sep 17 00:00:00 2001 From: Brendan McGrath Date: Fri, 25 Apr 2025 11:38:20 +1000 Subject: [PATCH 012/454] mf: Restart transforms and sinks on seek. The order should be: 1. Stop sources; 2. Flush MFTs; 3. Start sources; 4. Request output down the chain of sink inputs; 6. Flush sinks; and 7. Start the clock This takes place whether we pause before we seek or seek without pause. (cherry picked from commit 0627acf6cd3dc2fc1e89277a38f2153e87526660) CW-Bug-Id: #25191 CW-Bug-Id: #25329 CW-Bug-Id: #25439 --- dlls/mf/session.c | 156 +++++++++++++++++++++++++++++---------------- dlls/mf/tests/mf.c | 1 - 2 files changed, 101 insertions(+), 56 deletions(-) diff --git a/dlls/mf/session.c b/dlls/mf/session.c index 3f4eb4f5489e..5ecb1009b25f 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -223,6 +223,7 @@ enum presentation_flags SESSION_FLAG_END_OF_PRESENTATION = 0x10, SESSION_FLAG_PENDING_RATE_CHANGE = 0x20, SESSION_FLAG_PENDING_COMMAND = 0x40, + SESSION_FLAG_RESTARTING = 0x80, }; struct media_session @@ -1013,6 +1014,42 @@ static HRESULT session_subscribe_sources(struct media_session *session) return hr; } +static void session_flush_transforms(struct media_session *session) +{ + struct topo_node *node; + UINT i; + + LIST_FOR_EACH_ENTRY(node, &session->presentation.nodes, struct topo_node, entry) + { + if (node->type == MF_TOPOLOGY_TRANSFORM_NODE) + { + IMFTransform_ProcessMessage(node->object.transform, MFT_MESSAGE_COMMAND_FLUSH, 0); + for (i = 0; i < node->u.transform.output_count; i++) + node->u.transform.outputs[i].requests = 0; /* these requests might have been flushed */ + } + } +} + +static void session_request_sample(struct media_session *session, IMFStreamSink *sink_stream); + +static void session_flush_sinks(struct media_session *session) +{ + struct topo_node *node; + + LIST_FOR_EACH_ENTRY(node, &session->presentation.nodes, struct topo_node, entry) + { + if (node->type == MF_TOPOLOGY_OUTPUT_NODE) + { + if (node->u.sink.requests) + { + node->u.sink.requests--; + session_request_sample(session, node->object.sink_stream); + } + IMFStreamSink_Flush(node->object.sink_stream); + } + } +} + static void session_flush_nodes(struct media_session *session) { struct topo_node *node; @@ -1101,24 +1138,55 @@ static void session_reset_transforms(struct media_session *session, BOOL drop) static void session_start(struct media_session *session, const GUID *time_format, const PROPVARIANT *start_position) { struct media_source *source; - BOOL unpause_seek; MFTIME duration; HRESULT hr; switch (session->state) { - case SESSION_STATE_STOPPED: + case SESSION_STATE_PAUSED: + case SESSION_STATE_STARTED: + if (!IsEqualGUID(time_format, &GUID_NULL) || start_position->vt != VT_EMPTY) + { + /* We are seeking to a new position, check for invalid positions */ + LIST_FOR_EACH_ENTRY(source, &session->presentation.sources, struct media_source, entry) + { + hr = IMFPresentationDescriptor_GetUINT64(source->pd, &MF_PD_DURATION, (UINT64 *)&duration); + if (SUCCEEDED(hr) && IsEqualGUID(time_format, &GUID_NULL) + && start_position->vt == VT_I8 && start_position->hVal.QuadPart > duration) + { + WARN("Start position %s out of range, hr %#lx.\n", wine_dbgstr_longlong(start_position->hVal.QuadPart), hr); + session_command_complete_with_event(session, MESessionStarted, MF_E_INVALID_POSITION, NULL); + return; + } + } + + /* Stop sources */ + LIST_FOR_EACH_ENTRY(source, &session->presentation.sources, struct media_source, entry) + { + if (FAILED(hr = IMFMediaSource_Stop(source->source))) + { + WARN("Failed to stop media source %p, hr %#lx.\n", source->source, hr); + session_command_complete_with_event(session, MESessionStarted, hr, NULL); + return; + } + } + session->presentation.time_format = *time_format; + session->presentation.start_position.vt = VT_EMPTY; + PropVariantCopy(&session->presentation.start_position, start_position); + + /* SESSION_STATE_STARTED -> SESSION_STATE_RESTARTING_SOURCES -> SESSION_STATE_STARTED */ + session->state = SESSION_STATE_RESTARTING_SOURCES; + break; + } + /* fallthrough; we're resuming from the current position */ + case SESSION_STATE_STOPPED: /* Start request with no current topology. */ if (session->presentation.topo_status == MF_TOPOSTATUS_INVALID) { session_command_complete_with_event(session, MESessionStarted, MF_E_INVALIDREQUEST, NULL); break; } - - /* fallthrough */ - case SESSION_STATE_PAUSED: - session->presentation.time_format = *time_format; session->presentation.start_position.vt = VT_EMPTY; PropVariantCopy(&session->presentation.start_position, start_position); @@ -1129,10 +1197,7 @@ static void session_start(struct media_session *session, const GUID *time_format return; } - unpause_seek = start_position->vt == VT_I8; - if (unpause_seek) - session_flush_nodes(session); - session_reset_transforms(session, unpause_seek); + session_reset_transforms(session, FALSE); LIST_FOR_EACH_ENTRY(source, &session->presentation.sources, struct media_source, entry) { @@ -1146,40 +1211,7 @@ static void session_start(struct media_session *session, const GUID *time_format session->state = SESSION_STATE_STARTING_SOURCES; break; - case SESSION_STATE_STARTED: - /* Check for invalid positions */ - LIST_FOR_EACH_ENTRY(source, &session->presentation.sources, struct media_source, entry) - { - hr = IMFPresentationDescriptor_GetUINT64(source->pd, &MF_PD_DURATION, (UINT64 *)&duration); - if (SUCCEEDED(hr) && IsEqualGUID(time_format, &GUID_NULL) - && start_position->vt == VT_I8 && start_position->hVal.QuadPart > duration) - { - WARN("Start position %s out of range, hr %#lx.\n", wine_dbgstr_longlong(start_position->hVal.QuadPart), hr); - session_command_complete_with_event(session, MESessionStarted, MF_E_INVALID_POSITION, NULL); - return; - } - } - /* Stop sources */ - LIST_FOR_EACH_ENTRY(source, &session->presentation.sources, struct media_source, entry) - { - if (FAILED(hr = IMFMediaSource_Stop(source->source))) - { - WARN("Failed to stop media source %p, hr %#lx.\n", source->source, hr); - session_command_complete_with_event(session, MESessionStarted, hr, NULL); - return; - } - } - - session_reset_transforms(session, TRUE); - - session->presentation.time_format = *time_format; - session->presentation.start_position.vt = VT_EMPTY; - PropVariantCopy(&session->presentation.start_position, start_position); - - /* SESSION_STATE_STARTED -> SESSION_STATE_RESTARTING_SOURCES -> SESSION_STATE_STARTED */ - session->state = SESSION_STATE_RESTARTING_SOURCES; - break; default: session_command_complete_with_event(session, MESessionStarted, MF_E_INVALIDREQUEST, NULL); break; @@ -3290,15 +3322,6 @@ static void session_set_source_object_state(struct media_session *session, IUnkn session_set_presentation_clock(session); - /* If sinks are already started, start session immediately. This can happen when doing a - * seek from SESSION_STATE_STARTED */ - if (session_is_output_nodes_state(session, OBJ_STATE_STARTED) - && SUCCEEDED(session_start_clock(session))) - { - session_set_started(session); - return; - } - if ((session->presentation.flags & SESSION_FLAG_NEEDS_PREROLL) && session_is_output_nodes_state(session, OBJ_STATE_STOPPED)) { MFTIME preroll_time = 0; @@ -3333,15 +3356,38 @@ static void session_set_source_object_state(struct media_session *session, IUnkn } session->state = SESSION_STATE_PREROLLING_SINKS; } - else if (SUCCEEDED(session_start_clock(session))) - session->state = SESSION_STATE_STARTING_SINKS; + else + { + if (session->presentation.flags & SESSION_FLAG_RESTARTING) + { + session->presentation.flags &= ~SESSION_FLAG_RESTARTING; + session_flush_sinks(session); + } + + if (SUCCEEDED(hr = session_start_clock(session))) + { + /* If sinks are already started, start session immediately. This can happen when doing a + * seek from SESSION_STATE_STARTED (i.e. a seek without pause/stop) */ + if (session_is_output_nodes_state(session, OBJ_STATE_STARTED)) + session_set_started(session); + else + session->state = SESSION_STATE_STARTING_SINKS; + } + else + { + WARN("Failed to start session clock %p, hr %#lx.\n", session, hr); + session_command_complete_with_event(session, MESessionStarted, hr, NULL); + } + } break; case SESSION_STATE_RESTARTING_SOURCES: if (!session_is_source_nodes_state(session, OBJ_STATE_STOPPED)) break; - session_flush_nodes(session); + session->presentation.flags |= SESSION_FLAG_RESTARTING; + session_reset_transforms(session, TRUE); + session_flush_transforms(session); /* Start sources */ LIST_FOR_EACH_ENTRY(source, &session->presentation.sources, struct media_source, entry) diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index faf82f676db7..b1b4e9979708 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -6407,7 +6407,6 @@ static void test_media_session_Start(void) ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); hr = wait_media_event(session, callback, MESessionStarted, 5000, &propvar); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine_if(initial_state == SOURCE_PAUSED) compare_object_states(&actual_object_state_record, &expected_object_state_records[initial_state]); hr = IMFMediaSession_Stop(session); From b17b1337d93f1d90a6e3a98f21354cae0de40633 Mon Sep 17 00:00:00 2001 From: Brendan McGrath Date: Wed, 7 May 2025 13:08:28 +1000 Subject: [PATCH 013/454] mf: Don't send MFT_MESSAGE_NOTIFY_START_OF_STREAM when seeking. (cherry picked from commit b40d6a3cb4a9e19e0a230abad0f96190815daf5f) CW-Bug-Id: #25191 CW-Bug-Id: #25329 CW-Bug-Id: #25439 --- dlls/mf/session.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/dlls/mf/session.c b/dlls/mf/session.c index 5ecb1009b25f..2a6ac4f61f71 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -1516,12 +1516,6 @@ static void session_set_presentation_clock(struct media_session *session) struct topo_node *node; HRESULT hr; - LIST_FOR_EACH_ENTRY(node, &session->presentation.nodes, struct topo_node, entry) - { - if (node->type == MF_TOPOLOGY_TRANSFORM_NODE) - IMFTransform_ProcessMessage(node->object.transform, MFT_MESSAGE_NOTIFY_START_OF_STREAM, 0); - } - if (!(session->presentation.flags & SESSION_FLAG_PRESENTATION_CLOCK_SET)) { /* Attempt to get time source from the sinks. */ @@ -3275,6 +3269,7 @@ static void session_set_source_object_state(struct media_session *session, IUnkn struct media_source *src; struct media_sink *sink; enum object_state state; + struct topo_node *node; BOOL changed = FALSE; DWORD i, count; HRESULT hr; @@ -3320,6 +3315,15 @@ static void session_set_source_object_state(struct media_session *session, IUnkn session_set_topo_status(session, S_OK, MF_TOPOSTATUS_STARTED_SOURCE); + if (event_type == MESourceStarted || event_type == MEStreamStarted) + { + LIST_FOR_EACH_ENTRY(node, &session->presentation.nodes, struct topo_node, entry) + { + if (node->type == MF_TOPOLOGY_TRANSFORM_NODE) + IMFTransform_ProcessMessage(node->object.transform, MFT_MESSAGE_NOTIFY_START_OF_STREAM, 0); + } + } + session_set_presentation_clock(session); if ((session->presentation.flags & SESSION_FLAG_NEEDS_PREROLL) && session_is_output_nodes_state(session, OBJ_STATE_STOPPED)) From 48f859a7731e780ca150a0e03592fb7f44ba81a9 Mon Sep 17 00:00:00 2001 From: Brendan McGrath Date: Wed, 7 May 2025 16:07:22 +1000 Subject: [PATCH 014/454] mfmediaengine: Don't perform implicit flush on state change. This mirrors Windows behaviour, in that a flush only takes place when it is explicitly requested. (cherry picked from commit 71c5e485125d88c0ab1982695c0b66963667a1ad) CW-Bug-Id: #25191 CW-Bug-Id: #25329 CW-Bug-Id: #25439 --- dlls/mfmediaengine/video_frame_sink.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/dlls/mfmediaengine/video_frame_sink.c b/dlls/mfmediaengine/video_frame_sink.c index 23d8e19e71e5..867bedf893f6 100644 --- a/dlls/mfmediaengine/video_frame_sink.c +++ b/dlls/mfmediaengine/video_frame_sink.c @@ -1013,10 +1013,7 @@ static HRESULT video_frame_sink_set_state(struct video_frame_sink *sink, enum si } if (state == SINK_STATE_RUNNING && sink->state != SINK_STATE_RUNNING) - { - video_frame_sink_sample_queue_flush(sink); video_frame_sink_stream_request_sample(sink); - } if (state != sink->state || state != SINK_STATE_PAUSED) { From 44398073919f99aa073b706abe6366784c0199aa Mon Sep 17 00:00:00 2001 From: Brendan McGrath Date: Wed, 7 May 2025 16:08:30 +1000 Subject: [PATCH 015/454] mfmediaengine: Request sample if we are seeking. (cherry picked from commit 713a4579a520e107d0ccfcee13219616b8770420) CW-Bug-Id: #25191 CW-Bug-Id: #25329 CW-Bug-Id: #25439 --- dlls/mfmediaengine/video_frame_sink.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dlls/mfmediaengine/video_frame_sink.c b/dlls/mfmediaengine/video_frame_sink.c index 867bedf893f6..a79cd5f86446 100644 --- a/dlls/mfmediaengine/video_frame_sink.c +++ b/dlls/mfmediaengine/video_frame_sink.c @@ -1012,7 +1012,8 @@ static HRESULT video_frame_sink_set_state(struct video_frame_sink *sink, enum si video_frame_sink_set_flag(sink, FLAGS_FIRST_FRAME, FALSE); } - if (state == SINK_STATE_RUNNING && sink->state != SINK_STATE_RUNNING) + if (state == SINK_STATE_RUNNING && (sink->state == SINK_STATE_STOPPED || sink->state == SINK_STATE_PAUSED || + (sink->state == SINK_STATE_RUNNING && offset != PRESENTATION_CURRENT_POSITION))) video_frame_sink_stream_request_sample(sink); if (state != sink->state || state != SINK_STATE_PAUSED) From 6ff15d1b1276045002ff5d5986dec4b002f3ad1f Mon Sep 17 00:00:00 2001 From: Brendan McGrath Date: Wed, 11 Jun 2025 12:45:11 +1000 Subject: [PATCH 016/454] winegstreamer: Signal eos on disconnect. (cherry picked from commit 094a02bb06202b41a0230b078ce64fb2fb3b7b5d) CW-Bug-Id: #25191 CW-Bug-Id: #25329 CW-Bug-Id: #25439 --- dlls/winegstreamer/wg_parser.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dlls/winegstreamer/wg_parser.c b/dlls/winegstreamer/wg_parser.c index 989936acb344..9755548790d5 100644 --- a/dlls/winegstreamer/wg_parser.c +++ b/dlls/winegstreamer/wg_parser.c @@ -1985,7 +1985,9 @@ static NTSTATUS wg_parser_disconnect(void *args) for (i = 0; i < parser->stream_count; ++i) { parser->streams[i]->flushing = true; + parser->streams[i]->eos = true; pthread_cond_signal(&parser->streams[i]->event_empty_cond); + pthread_cond_signal(&parser->streams[i]->event_cond); } pthread_mutex_unlock(&parser->mutex); From c829cb391b45f72c198cd33999bfefe5699fa848 Mon Sep 17 00:00:00 2001 From: Brendan McGrath Date: Wed, 11 Jun 2025 09:56:46 +1000 Subject: [PATCH 017/454] winegstreamer: Don't hold lock during wg_parser_stream_get_buffer. (cherry picked from commit 609e42e9368f96b913b77548e14c000dd0c54563) CW-Bug-Id: #25191 CW-Bug-Id: #25329 CW-Bug-Id: #25439 --- dlls/winegstreamer/media_source.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index 0bb2c6b40001..8289d81e8a51 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -791,6 +791,20 @@ static HRESULT media_stream_send_eos(struct media_source *source, struct media_s return S_OK; } +static bool stream_get_buffer(struct media_stream *stream, struct wg_parser_buffer *buffer) +{ + struct media_source *source = impl_from_IMFMediaSource(stream->media_source); + wg_parser_stream_t wg_stream = stream->wg_stream; + wg_parser_t wg_parser = source->wg_parser; + bool ret; + + LeaveCriticalSection(&source->cs); + ret = wg_parser_stream_get_buffer(wg_parser, wg_stream, buffer); + EnterCriticalSection(&source->cs); + + return ret; +} + static HRESULT wait_on_sample(struct media_stream *stream, IUnknown *token) { struct media_source *source = impl_from_IMFMediaSource(stream->media_source); @@ -798,13 +812,16 @@ static HRESULT wait_on_sample(struct media_stream *stream, IUnknown *token) TRACE("%p, %p\n", stream, token); - while (wg_parser_stream_get_buffer(source->wg_parser, stream->wg_stream, &buffer)) + while (stream_get_buffer(stream, &buffer)) { HRESULT hr = media_stream_send_sample(stream, &buffer, token); if (hr != S_FALSE) return hr; } + if (source->state == SOURCE_SHUTDOWN) + return S_OK; + return media_stream_send_eos(source, stream); } From b2923153d7cb5494f3bcf65204bb1777aff5941d Mon Sep 17 00:00:00 2001 From: Brendan McGrath Date: Tue, 24 Jun 2025 15:58:32 +1000 Subject: [PATCH 018/454] mfmediaengine: Only forward the most recent seek time. (cherry picked from commit 7a0aa5808b10363eb69d348f5f2b73357ee99d06) CW-Bug-Id: #25601 --- dlls/mfmediaengine/main.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/dlls/mfmediaengine/main.c b/dlls/mfmediaengine/main.c index d7d3383ab007..49720204c161 100644 --- a/dlls/mfmediaengine/main.c +++ b/dlls/mfmediaengine/main.c @@ -153,6 +153,7 @@ struct media_engine double default_playback_rate; double volume; double duration; + double next_seek; MF_MEDIA_ENGINE_NETWORK network_state; MF_MEDIA_ENGINE_ERR error_code; HRESULT extended_code; @@ -900,6 +901,8 @@ static HRESULT WINAPI media_engine_callback_GetParameters(IMFAsyncCallback *ifac return E_NOTIMPL; } +static HRESULT media_engine_set_current_time(struct media_engine *engine, double seektime); + static HRESULT WINAPI media_engine_session_events_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result) { struct media_engine *engine = impl_from_session_events_IMFAsyncCallback(iface); @@ -965,6 +968,8 @@ static HRESULT WINAPI media_engine_session_events_Invoke(IMFAsyncCallback *iface media_engine_set_flag(engine, FLAGS_ENGINE_SEEKING | FLAGS_ENGINE_IS_ENDED, FALSE); IMFMediaEngineNotify_EventNotify(engine->callback, MF_MEDIA_ENGINE_EVENT_SEEKED, 0, 0); IMFMediaEngineNotify_EventNotify(engine->callback, MF_MEDIA_ENGINE_EVENT_TIMEUPDATE, 0, 0); + if (isfinite(engine->next_seek)) + media_engine_set_current_time(engine, engine->next_seek); } LeaveCriticalSection(&engine->cs); IMFMediaEngineNotify_EventNotify(engine->callback, MF_MEDIA_ENGINE_EVENT_PLAYING, 0, 0); @@ -1841,6 +1846,14 @@ static HRESULT media_engine_set_current_time(struct media_engine *engine, double if (FAILED(hr) || !(caps & MFSESSIONCAP_SEEK)) return hr; + if (engine->flags & FLAGS_ENGINE_SEEKING) + { + engine->next_seek = seektime; + return S_OK; + } + + engine->next_seek = NAN; + position.vt = VT_I8; position.hVal.QuadPart = min(max(0, seektime), engine->duration) * 10000000; @@ -3375,6 +3388,7 @@ static HRESULT init_media_engine(DWORD flags, IMFAttributes *attributes, struct engine->playback_rate = 1.0; engine->volume = 1.0; engine->duration = NAN; + engine->next_seek = NAN; engine->video_frame.pts = MINLONGLONG; InitializeCriticalSection(&engine->cs); From e1df3014f27d8e2c04b482ceb295506d63e09d07 Mon Sep 17 00:00:00 2001 From: Santino Mazza Date: Wed, 18 Jun 2025 13:47:51 -0300 Subject: [PATCH 019/454] winex11.drv: Add programmer dvorak layout. (cherry picked from commit 1321cbca05bd99671f210a2d18bc6d69f43c6c64) Cw-Bug-Id: #25358 --- dlls/winex11.drv/keyboard.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/dlls/winex11.drv/keyboard.c b/dlls/winex11.drv/keyboard.c index efc44d3084a0..0456d819bc8c 100644 --- a/dlls/winex11.drv/keyboard.c +++ b/dlls/winex11.drv/keyboard.c @@ -226,6 +226,15 @@ static const char main_key_US_dvorak[MAIN_LEN][4] = ";:","qQ","jJ","kK","xX","bB","mM","wW","vV","zZ" }; +/*** United States keyboard layout (programmer dvorak version) */ +static const char main_key_US_programmer_dvorak[MAIN_LEN][4] = +{ + "$~","&%","[7","{5","}3","(1","=9","*0",")2","+4","]6","!8","#`", + ";:",",<",".>","pP","yY","fF","gG","cC","rR","lL","/?","@^", + "aA","oO","eE","uU","iI","dD","hH","tT","nN","sS","-_","\\|", + "'\"","qQ","jJ","kK","xX","bB","mM","wW","vV","zZ" +}; + /*** United States keyboard layout (dvorak phantom key version) */ static const char main_key_US_dvorak_phantom[MAIN_LEN][4] = { @@ -865,6 +874,7 @@ static const struct { {0x0409, "United States keyboard layout (phantom key version)", &main_key_US_phantom, &main_key_scan_qwerty, &main_key_vkey_qwerty}, /* Dvorak users tend to run QWERTY keyboards and rely on Windows/X11/Wayland to translate to the correct keysyms */ {0x0409, "United States keyboard layout (dvorak)", &main_key_US_dvorak, &main_key_scan_qwerty, &main_key_vkey_dvorak}, + {0x0409, "United States keyboard layout (programmer dvorak)", &main_key_US_programmer_dvorak, &main_key_scan_qwerty, &main_key_vkey_dvorak}, {0x0409, "United States keyboard layout (dvorak with phantom key)", &main_key_US_dvorak_phantom, &main_key_scan_qwerty, &main_key_vkey_dvorak}, {0x0409, "United States International keyboard layout", &main_key_US_intl, &main_key_scan_qwerty, &main_key_vkey_qwerty}, {0x0809, "British keyboard layout", &main_key_UK, &main_key_scan_qwerty, &main_key_vkey_qwerty}, From 7874f36db54a0f28f13ac72fc9328dcad05e1b2b Mon Sep 17 00:00:00 2001 From: Conor McCarthy Date: Fri, 1 Aug 2025 15:31:49 +1000 Subject: [PATCH 020/454] winegstreamer: HACK: Fix up NV12 alignment mismatch for decoder D3D-aware output. GStreamer uses a hard-coded 4-pixel width alignment for NV12. CW-Bug-Id: #25319 --- dlls/winegstreamer/video_decoder.c | 62 +++++++++++++++++++++++++++++- 1 file changed, 60 insertions(+), 2 deletions(-) diff --git a/dlls/winegstreamer/video_decoder.c b/dlls/winegstreamer/video_decoder.c index f31679b8d08c..f34ca7cbdcd6 100644 --- a/dlls/winegstreamer/video_decoder.c +++ b/dlls/winegstreamer/video_decoder.c @@ -945,11 +945,54 @@ static HRESULT handle_stream_type_change(struct video_decoder *decoder) return MF_E_TRANSFORM_STREAM_CHANGE; } +static void buffer_fixup_nv12(IMFMediaBuffer *buffer, UINT dst_width, UINT src_width, UINT height) +{ + DWORD length = 0; + BYTE *src, *dst; + HRESULT hr; + UINT i; + + dst_width = (dst_width + 1) & ~1; + height = (height + 1) & ~1; + + IMFMediaBuffer_GetCurrentLength(buffer, &length); + if (length < (src_width + src_width / 2u) * height) + { + WARN("Unexpected buffer %p length %lu.\n", buffer, length); + return; + } + + if (FAILED(hr = IMFMediaBuffer_Lock(buffer, &src, NULL, NULL))) + { + WARN("Failed to lock buffer %p, hr %#lx.\n", buffer, hr); + return; + } + + for (i = 0, dst = src; i < height; ++i) + { + memmove(dst, src, dst_width); + dst += dst_width; + src += src_width; + } + for (i = 0; i < height; ++i) + { + memmove(dst, src, dst_width / 2u); + dst += dst_width / 2u; + src += src_width / 2u; + } + + IMFMediaBuffer_Unlock(buffer); + + if (FAILED(hr = IMFMediaBuffer_SetCurrentLength(buffer, (dst_width + dst_width / 2u) * height))) + WARN("Failed to set buffer %p length, hr %#lx.\n", buffer, hr); +} + static HRESULT WINAPI transform_ProcessOutput(IMFTransform *iface, DWORD flags, DWORD count, MFT_OUTPUT_DATA_BUFFER *samples, DWORD *status) { struct video_decoder *decoder = impl_from_IMFTransform(iface); - UINT32 sample_size; + UINT32 sample_size, frame_width; + BOOL fixup_nv12 = FALSE; LONGLONG duration, sample_duration; IMFSample *sample; UINT64 frame_size, frame_rate; @@ -974,7 +1017,20 @@ static HRESULT WINAPI transform_ProcessOutput(IMFTransform *iface, DWORD flags, return hr; if (FAILED(hr = IMFMediaType_GetUINT64(decoder->output_type, &MF_MT_FRAME_SIZE, &frame_size))) return hr; - if (FAILED(hr = MFCalculateImageSize(&subtype, frame_size >> 32, (UINT32)frame_size, &sample_size))) + frame_width = frame_size >> 32; + if ((frame_width & 3) && (frame_width & 3) != 3 && IsEqualGUID(&subtype, &MFVideoFormat_NV12)) + { + /* GStreamer uses a hard-coded 4-pixel width alignment for NV12. So far this has only been an issue + * when decoded from theora (for a cutscene in Resident Evil Village). Streams only provide samples + * to support D3D-awareness, which in Windows supports few compressed formats (theora support here + * is a hack). This issue could also occur when the sample is supplied externally, but there is no + * test case for it. This issue cannot occur with H.264 due to its own alignment requirement. */ + if (!(decoder->output_info.dwFlags & MFT_OUTPUT_STREAM_PROVIDES_SAMPLES)) + FIXME("Alignment fixup for external samples is unimplemented.\n"); + frame_width = (frame_width + 3) & ~3; + fixup_nv12 = TRUE; + } + if (FAILED(hr = MFCalculateImageSize(&subtype, frame_width, (UINT32)frame_size, &sample_size))) return hr; if (decoder->output_info.dwFlags & MFT_OUTPUT_STREAM_PROVIDES_SAMPLES) @@ -1039,6 +1095,8 @@ static HRESULT WINAPI transform_ProcessOutput(IMFTransform *iface, DWORD flags, if (decoder->output_info.dwFlags & MFT_OUTPUT_STREAM_PROVIDES_SAMPLES) { + if (fixup_nv12) + buffer_fixup_nv12(decoder->temp_buffer, frame_size >> 32, frame_width, (UINT32)frame_size); if (hr == S_OK && FAILED(hr = output_sample(decoder, &samples->pSample, sample))) ERR("Failed to output sample, hr %#lx.\n", hr); IMFSample_Release(sample); From 9d9f9a354f5af8dca5cee4c06378567e7a7d3567 Mon Sep 17 00:00:00 2001 From: Conor McCarthy Date: Thu, 14 Aug 2025 12:24:30 +1000 Subject: [PATCH 021/454] mfplat: HACK: Enable the 2d buffer lock optimisation for Metal Gear Solid V. CW-Bug-Id: #25788 --- dlls/mfplat/buffer.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/dlls/mfplat/buffer.c b/dlls/mfplat/buffer.c index 219cbae40f30..33e8728c6794 100644 --- a/dlls/mfplat/buffer.c +++ b/dlls/mfplat/buffer.c @@ -310,14 +310,15 @@ static HRESULT WINAPI memory_1d_2d_buffer_Lock(IMFMediaBuffer *iface, BYTE **dat EnterCriticalSection(&buffer->cs); if (!buffer->_2d.linear_buffer && buffer->_2d.width == buffer->_2d.pitch - && (sgi = getenv("SteamGameId")) && !strcmp(sgi, "418370")) + && (sgi = getenv("SteamGameId")) && (!strcmp(sgi, "418370") || !strcmp(sgi, "287700"))) { BYTE *scanline; LONG pitch; /* width and pitch are the same, so this avoids a potentially expensive copy * this is a HACK as it does not match Windows behaviour (Windows will copy the buffer) - * this fixes a performance regression for Resident Evil 7 Biohazard (418370) + * this fixes performance regressions for Resident Evil 7 Biohazard (418370) and + * Metal Gear Solid V (287700). */ hr = memory_2d_buffer_lock(buffer, &scanline, &pitch, data, NULL); } @@ -371,7 +372,7 @@ static HRESULT WINAPI memory_1d_2d_buffer_Unlock(IMFMediaBuffer *iface) EnterCriticalSection(&buffer->cs); if (!buffer->_2d.linear_buffer && buffer->_2d.width == buffer->_2d.pitch - && (sgi = getenv("SteamGameId")) && !strcmp(sgi, "418370")) + && (sgi = getenv("SteamGameId")) && (!strcmp(sgi, "418370") || !strcmp(sgi, "287700"))) { if (buffer->_2d.locks) --buffer->_2d.locks; From dbbfabf369c9b19212adf17d89ada1684e03817e Mon Sep 17 00:00:00 2001 From: Conor McCarthy Date: Thu, 31 Jul 2025 10:58:16 +1000 Subject: [PATCH 022/454] winedmo: HACK: Set a non-zero value in WAVEFORMATEX wBitsPerSample for transcoded audio in Metal Gear Solid V. CW-Bug-Id: #25682 --- dlls/winedmo/unix_media_type.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/dlls/winedmo/unix_media_type.c b/dlls/winedmo/unix_media_type.c index edde85172b66..eba74c80b694 100644 --- a/dlls/winedmo/unix_media_type.c +++ b/dlls/winedmo/unix_media_type.c @@ -75,6 +75,8 @@ static UINT wave_format_tag_from_codec_id( enum AVCodecID id ) static void wave_format_ex_init( const AVCodecParameters *params, WAVEFORMATEX *format, UINT32 format_size, WORD format_tag ) { + const char *sgi; + memset( format, 0, format_size ); format->cbSize = format_size - sizeof(*format); format->wFormatTag = format_tag; @@ -86,6 +88,13 @@ static void wave_format_ex_init( const AVCodecParameters *params, WAVEFORMATEX * format->nSamplesPerSec = params->sample_rate; format->wBitsPerSample = av_get_bits_per_sample( params->codec_id ); if (!format->wBitsPerSample) format->wBitsPerSample = params->bits_per_coded_sample; + if (!format->wBitsPerSample && (params->codec_id == AV_CODEC_ID_OPUS || params->codec_id == AV_CODEC_ID_VORBIS) + && (sgi = getenv("SteamGameId")) && !strcmp(sgi, "287700")) + { + /* Metal Gear Solid V: The Phantom Pain uses wBitsPerSample as a divisor in an integer division, + * so it must be non-zero, but is zero for transcoded audio. */ + format->wBitsPerSample = 16; + } if (!(format->nBlockAlign = params->block_align)) format->nBlockAlign = format->wBitsPerSample * format->nChannels / 8; if (!(format->nAvgBytesPerSec = params->bit_rate / 8)) format->nAvgBytesPerSec = format->nSamplesPerSec * format->nBlockAlign; } From 88d04985f1d0b15b3658a82c236fd6bdd01987d1 Mon Sep 17 00:00:00 2001 From: fallenworld Date: Wed, 25 Jun 2025 22:26:59 +0800 Subject: [PATCH 023/454] mlang: Force using Microsoft YaHei for GB2312 and ShiftJIS charset. CW-Bug-Id: 23857 --- dlls/mlang/mlang.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/dlls/mlang/mlang.c b/dlls/mlang/mlang.c index 3ff3c8b34bbe..650af2f74239 100644 --- a/dlls/mlang/mlang.c +++ b/dlls/mlang/mlang.c @@ -1327,6 +1327,10 @@ static INT CALLBACK map_font_enum_proc(const LOGFONTW *lf, const TEXTMETRICW *nt UINT charset; struct map_font_enum_data *data = (struct map_font_enum_data *)lParam; + if ((data->charset == GB2312_CHARSET || data->charset == SHIFTJIS_CHARSET) + && wcscmp(lf->lfFaceName, L"Microsoft YaHei") != 0) + return 1; + data->src_lf.lfCharSet = lf->lfCharSet; wcscpy(data->src_lf.lfFaceName, lf->lfFaceName); From 086a0e3a4790ae54182bdd55f1d2553d61c4d223 Mon Sep 17 00:00:00 2001 From: Zhiyi Zhang Date: Fri, 4 Jul 2025 11:07:39 +0800 Subject: [PATCH 024/454] winex11.drv: Disable maximize when emulating non-native aspect ratio display modes. Fix maximized windows jumping up and down when emulating non-native aspect ratio display modes. CW-Bug-Id: #25620 --- dlls/winex11.drv/window.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index ba04b6e16560..992c1f1138f3 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -1433,6 +1433,7 @@ static void update_net_wm_states( struct x11drv_win_data *data ) { static const UINT fullscreen_mask = (1 << NET_WM_STATE_MAXIMIZED) | (1 << NET_WM_STATE_FULLSCREEN); UINT style, ex_style, new_state = 0; + BOOL disable_maximize; if (!data->managed || data->embedded) return; if (data->whole_window == root_window) @@ -1443,17 +1444,30 @@ static void update_net_wm_states( struct x11drv_win_data *data ) return; } + /* Disable maximize when there is an offset difference between emulated display mode and the + * physical display mode. For example, when making a window maximized on an emulated 2560x1080 + * mode on a 3840x2160 physical mode, it doesn't make sense to add NET_WM_STATE_MAXIMIZED to its + * X window. If NET_WM_STATE_MAXIMIZED gets added in such cases, the X window gets resized to + * cover the work area. Then the visible rect is no longer fullscreen, then surface offset set + * by set_surface_window_rects() will be zero. So then the window top-left corner will be at + * (0, 0) instead of (0, 540). What's worse, the window is still considered as fullscreen + * because it covers 2560x1080, so then Wine will add NET_WM_STATE_FULLSCREEN, then the window + * top-left corner will be back at (0, 540). This makes the window look like jumping up and down. + */ + disable_maximize = data->rects.window.left != data->rects.visible.left || + data->rects.window.top != data->rects.visible.top; + style = NtUserGetWindowLongW( data->hwnd, GWL_STYLE ); if (style & WS_MINIMIZE) new_state |= data->desired_state.net_wm_state & fullscreen_mask; if (data->is_fullscreen) { - if ((style & WS_MAXIMIZE) && (style & WS_CAPTION) == WS_CAPTION) + if (!disable_maximize && (style & WS_MAXIMIZE) && (style & WS_CAPTION) == WS_CAPTION) new_state |= (1 << NET_WM_STATE_MAXIMIZED); else if (!(style & WS_MINIMIZE)) new_state |= (1 << NET_WM_STATE_FULLSCREEN); } - else if (style & WS_MAXIMIZE) + else if (!disable_maximize && style & WS_MAXIMIZE) new_state |= (1 << NET_WM_STATE_MAXIMIZED); ex_style = NtUserGetWindowLongW( data->hwnd, GWL_EXSTYLE ); @@ -1778,6 +1792,7 @@ static UINT window_update_client_config( struct x11drv_win_data *data ) static const UINT fullscreen_mask = (1 << NET_WM_STATE_MAXIMIZED) | (1 << NET_WM_STATE_FULLSCREEN); UINT old_style = NtUserGetWindowLongW( data->hwnd, GWL_STYLE ), new_style, flags; RECT rect, old_rect = data->rects.window, new_rect; + BOOL disable_maximize; if (!data->managed) return 0; /* unmanaged windows are managed by the Win32 side */ if (data->desired_state.wm_state != NormalState) return 0; /* ignore config changes on invisible/minimized windows */ @@ -1791,6 +1806,11 @@ static UINT window_update_client_config( struct x11drv_win_data *data ) if (data->current_state.wm_state == IconicState) new_style |= WS_MINIMIZE; if (data->current_state.net_wm_state & (1 << NET_WM_STATE_MAXIMIZED)) new_style |= WS_MAXIMIZE; + /* See update_net_wm_states() regarding disable_maximize */ + disable_maximize = data->rects.window.left != data->rects.visible.left || + data->rects.window.top != data->rects.visible.top; + if (disable_maximize) new_style = (new_style & ~WS_MAXIMIZE) | (old_style & WS_MAXIMIZE); + /* KWin sometimes combines NET_WM_STATE_FULLSCREEN with NET_WM_STATE_MAXIMIZED, but sometimes doesn't. * Don't feed back the maximized state to the Win32 side as it confuses many applications. */ From fcd73d0d0ce0d24ebc6e7546bd158c933d77b444 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 19 Jun 2025 17:22:50 +0200 Subject: [PATCH 025/454] services: Preserve some winebus specific environment variable. CW-Bug-Id: #25366 --- programs/services/services.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/programs/services/services.c b/programs/services/services.c index 2c19f0b14cc0..3fb5ed28ae1a 100644 --- a/programs/services/services.c +++ b/programs/services/services.c @@ -1030,15 +1030,23 @@ static DWORD service_start_process(struct service_entry *service_entry, struct p if (!environment && OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_DUPLICATE, &token)) { - WCHAR val[16]; - CreateEnvironmentBlock(&environment, token, FALSE); - if (GetEnvironmentVariableW( L"WINEBOOTSTRAPMODE", val, ARRAY_SIZE(val) )) + static const WCHAR *preserve[] = { - UNICODE_STRING name = RTL_CONSTANT_STRING(L"WINEBOOTSTRAPMODE"); - UNICODE_STRING value; + L"WINEBOOTSTRAPMODE", + L"WINEBUSCONFIG", + }; + WCHAR buffer[1024]; - RtlInitUnicodeString( &value, val ); - RtlSetEnvironmentVariable( (WCHAR **)&environment, &name, &value ); + CreateEnvironmentBlock(&environment, token, FALSE); + for (size_t i = 0; i < ARRAY_SIZE(preserve); i++) + { + if (GetEnvironmentVariableW( preserve[i], buffer, ARRAY_SIZE(buffer) )) + { + UNICODE_STRING value, name; + RtlInitUnicodeString( &name, preserve[i] ); + RtlInitUnicodeString( &value, buffer ); + RtlSetEnvironmentVariable( (WCHAR **)&environment, &name, &value ); + } } CloseHandle(token); } From 18b92b9a57ad9d9a1bcbdd496e95800ab8531211 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 19 Jun 2025 19:07:45 +0200 Subject: [PATCH 026/454] winebus: Use a single structure for bus options. CW-Bug-Id: #25366 --- dlls/winebus.sys/bus_iohid.c | 4 +- dlls/winebus.sys/bus_sdl.c | 18 ++++---- dlls/winebus.sys/bus_udev.c | 34 +++++++------- dlls/winebus.sys/main.c | 88 ++++++++++++++++++------------------ dlls/winebus.sys/unixlib.h | 18 ++------ 5 files changed, 79 insertions(+), 83 deletions(-) diff --git a/dlls/winebus.sys/bus_iohid.c b/dlls/winebus.sys/bus_iohid.c index 5a210fc6bbd3..67276a26c489 100644 --- a/dlls/winebus.sys/bus_iohid.c +++ b/dlls/winebus.sys/bus_iohid.c @@ -106,7 +106,7 @@ static IOHIDManagerRef hid_manager; static CFRunLoopRef run_loop; static struct list event_queue = LIST_INIT(event_queue); static struct list device_list = LIST_INIT(device_list); -static struct iohid_bus_options options; +static const struct bus_options *options; struct iohid_device { @@ -352,7 +352,7 @@ NTSTATUS iohid_bus_init(void *args) { TRACE("args %p\n", args); - options = *(struct iohid_bus_options *)args; + options = args; if (!(hid_manager = IOHIDManagerCreate(kCFAllocatorDefault, 0L))) { diff --git a/dlls/winebus.sys/bus_sdl.c b/dlls/winebus.sys/bus_sdl.c index c97597dea8bc..aa28d2fccfd7 100644 --- a/dlls/winebus.sys/bus_sdl.c +++ b/dlls/winebus.sys/bus_sdl.c @@ -61,7 +61,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(hid); #ifdef SONAME_LIBSDL2 static pthread_mutex_t sdl_cs = PTHREAD_MUTEX_INITIALIZER; -static struct sdl_bus_options options; +static const struct bus_options *options; static void *sdl_handle = NULL; static UINT quit_event = -1; @@ -303,7 +303,7 @@ static NTSTATUS build_joystick_report_descriptor(struct unix_device *iface) size_t absolute_usages_count = get_absolute_usages(impl, &absolute_usages); axis_count = pSDL_JoystickNumAxes(impl->sdl_joystick); - if (options.split_controllers) axis_count = min(6, axis_count - impl->axis_offset); + if (options->split_controllers) axis_count = min(6, axis_count - impl->axis_offset); if (axis_count > absolute_usages_count) { FIXME("More than %zu absolute axes found, ignoring.\n", absolute_usages_count); @@ -990,7 +990,7 @@ static void sdl_add_device(unsigned int index) } joystick_type = pSDL_JoystickGetType(joystick); - if (options.map_controllers && pSDL_IsGameController(index) + if (options->map_controllers && pSDL_IsGameController(index) && joystick_type != SDL_JOYSTICK_TYPE_WHEEL && joystick_type != SDL_JOYSTICK_TYPE_FLIGHT_STICK) controller = pSDL_GameControllerOpen(index); @@ -1075,7 +1075,7 @@ static void sdl_add_device(unsigned int index) } bus_event_queue_device_created(&event_queue, &impl->unix_device, &desc); - axis_offset += (options.split_controllers ? 6 : axis_count); + axis_offset += (options->split_controllers ? 6 : axis_count); } while (axis_offset < axis_count); } @@ -1098,7 +1098,7 @@ static void process_device_event(SDL_Event *event) if (impl) bus_event_queue_device_removed(&event_queue, &impl->unix_device); else WARN("Failed to find device with id %d\n", id); } - else if (event->type == SDL_JOYAXISMOTION && options.split_controllers) + else if (event->type == SDL_JOYAXISMOTION && options->split_controllers) { id = event->jaxis.which; impl = find_device_from_id_and_axis(id, event->jaxis.axis); @@ -1137,7 +1137,7 @@ NTSTATUS sdl_bus_init(void *args) TRACE("args %p\n", args); - options = *(struct sdl_bus_options *)args; + options = (struct bus_options *)args; if (!(sdl_handle = dlopen(SONAME_LIBSDL2, RTLD_NOW))) { @@ -1240,10 +1240,10 @@ NTSTATUS sdl_bus_init(void *args) if (pSDL_GameControllerAddMapping(mapping) < 0) WARN("Failed to add environment mapping %s\n", pSDL_GetError()); } - else for (i = 0; i < options.mappings_count; ++i) + else for (i = 0; i < options->mappings_count; ++i) { - TRACE("Setting registry mapping %s\n", debugstr_a(options.mappings[i])); - if (pSDL_GameControllerAddMapping(options.mappings[i]) < 0) + TRACE("Setting registry mapping %s\n", debugstr_a(options->mappings[i])); + if (pSDL_GameControllerAddMapping(options->mappings[i]) < 0) WARN("Failed to add registry mapping %s\n", pSDL_GetError()); } } diff --git a/dlls/winebus.sys/bus_udev.c b/dlls/winebus.sys/bus_udev.c index 332e79e3ae5d..a71e979d0650 100644 --- a/dlls/winebus.sys/bus_udev.c +++ b/dlls/winebus.sys/bus_udev.c @@ -100,7 +100,7 @@ static struct udev_monitor *udev_monitor; static int deviceloop_control[2]; static struct list event_queue = LIST_INIT(event_queue); static struct list device_list = LIST_INIT(device_list); -static struct udev_bus_options options; +static const struct bus_options *options; struct base_device { @@ -1781,7 +1781,7 @@ static void build_initial_deviceset_direct(void) int n, len; DIR *dir; - if (!options.disable_hidraw) + if (!options->disable_hidraw) { TRACE("Initial enumeration of /dev/hidraw*\n"); if (!(dir = opendir("/dev"))) WARN("Unable to open /dev: %s\n", strerror(errno)); @@ -1798,7 +1798,7 @@ static void build_initial_deviceset_direct(void) } } #ifdef HAS_PROPER_INPUT_HEADER - if (!options.disable_input) + if (!options->disable_input) { TRACE("Initial enumeration of /dev/input/event*\n"); if (!(dir = opendir("/dev/input"))) WARN("Unable to open /dev/input: %s\n", strerror(errno)); @@ -1827,7 +1827,7 @@ static int create_inotify(void) return fd; } - if (!options.disable_hidraw) + if (!options->disable_hidraw) { /* We need to watch for attribute changes in addition to * creation, because when a device is first created, it has @@ -1839,7 +1839,7 @@ static int create_inotify(void) else systems++; } #ifdef HAS_PROPER_INPUT_HEADER - if (!options.disable_input) + if (!options->disable_input) { devinput_watch = inotify_add_watch(fd, "/dev/input", flags); if (devinput_watch < 0) WARN("Unable to initialize inotify for /dev/input: %s\n", strerror(errno)); @@ -1935,11 +1935,11 @@ static void build_initial_deviceset_udevd(void) return; } - if (!options.disable_hidraw) + if (!options->disable_hidraw) if (udev_enumerate_add_match_subsystem(enumerate, "hidraw") < 0) WARN("Failed to add subsystem 'hidraw' to enumeration\n"); #ifdef HAS_PROPER_INPUT_HEADER - if (!options.disable_input) + if (!options->disable_input) { if (udev_enumerate_add_match_subsystem(enumerate, "input") < 0) WARN("Failed to add subsystem 'input' to enumeration\n"); @@ -1978,7 +1978,7 @@ static struct udev_monitor *create_monitor(int *fd) return NULL; } - if (!options.disable_hidraw) + if (!options->disable_hidraw) { if (udev_monitor_filter_add_match_subsystem_devtype(monitor, "hidraw", NULL) < 0) WARN("Failed to add 'hidraw' subsystem to monitor\n"); @@ -1986,7 +1986,7 @@ static struct udev_monitor *create_monitor(int *fd) systems++; } #ifdef HAS_PROPER_INPUT_HEADER - if (!options.disable_input) + if (!options->disable_input) { if (udev_monitor_filter_add_match_subsystem_devtype(monitor, "input", NULL) < 0) WARN("Failed to add 'input' subsystem to monitor\n"); @@ -2058,10 +2058,12 @@ static void process_monitor_event(struct udev_monitor *monitor) NTSTATUS udev_bus_init(void *args) { int monitor_fd = -1; + BOOL disable_udevd; TRACE("args %p\n", args); - options = *(struct udev_bus_options *)args; + options = (struct bus_options *)args; + disable_udevd = options->disable_udevd; if (pipe(deviceloop_control) != 0) { @@ -2078,15 +2080,15 @@ NTSTATUS udev_bus_init(void *args) if (access("/run/pressure-vessel", R_OK) || access("/.flatpak-info", R_OK)) { TRACE("Container detected, bypassing udevd by default\n"); - options.disable_udevd = TRUE; + disable_udevd = TRUE; } #ifdef HAVE_SYS_INOTIFY_H - if (options.disable_udevd) monitor_fd = create_inotify(); - if (monitor_fd < 0) options.disable_udevd = FALSE; + if (disable_udevd) monitor_fd = create_inotify(); + if (monitor_fd < 0) disable_udevd = FALSE; #else - if (options.disable_udevd) ERR("inotify support not compiled in!\n"); - options.disable_udevd = FALSE; + if (disable_udevd) ERR("inotify support not compiled in!\n"); + disable_udevd = FALSE; #endif if (monitor_fd < 0 && !(udev_monitor = create_monitor(&monitor_fd))) @@ -2105,7 +2107,7 @@ NTSTATUS udev_bus_init(void *args) poll_fds[1].revents = 0; poll_count = 2; - if (!options.disable_udevd) build_initial_deviceset_udevd(); + if (!disable_udevd) build_initial_deviceset_udevd(); #ifdef HAVE_SYS_INOTIFY_H else build_initial_deviceset_direct(); #endif diff --git a/dlls/winebus.sys/main.c b/dlls/winebus.sys/main.c index 30385225674b..dd9b233ec7bb 100644 --- a/dlls/winebus.sys/main.c +++ b/dlls/winebus.sys/main.c @@ -51,6 +51,7 @@ static DEVICE_OBJECT *keyboard_obj; static DEVICE_OBJECT *bus_pdo; static DEVICE_OBJECT *bus_fdo; +static struct bus_options options; static HANDLE driver_key; struct hid_report @@ -460,7 +461,7 @@ static BOOL is_hidraw_enabled(WORD vid, WORD pid, const USAGE_AND_PAGE *usages, SIZE_T len; DWORD size; - if (check_bus_option(L"DisableHidraw", FALSE)) return FALSE; + if (options.disable_hidraw) return FALSE; if (usages->UsagePage == HID_USAGE_PAGE_DIGITIZER) { @@ -496,9 +497,7 @@ static BOOL is_hidraw_enabled(WORD vid, WORD pid, const USAGE_AND_PAGE *usages, } if (usages->Usage != HID_USAGE_GENERIC_GAMEPAD && usages->Usage != HID_USAGE_GENERIC_JOYSTICK) return TRUE; - if (!check_bus_option(L"Enable SDL", 1) && check_bus_option(L"DisableInput", 0)) - prefer_hidraw = TRUE; - + if (options.disable_sdl && options.disable_input) prefer_hidraw = TRUE; if (is_dualshock4_gamepad(vid, pid)) prefer_hidraw = TRUE; if (is_dualsense_gamepad(vid, pid)) prefer_hidraw = TRUE; @@ -978,7 +977,7 @@ static NTSTATUS bus_main_thread_start(struct bus_main_params *bus) return status; } -static void sdl_bus_free_mappings(struct sdl_bus_options *options) +static void sdl_bus_free_mappings(struct bus_options *options) { DWORD count = options->mappings_count; char **mappings = options->mappings; @@ -987,7 +986,7 @@ static void sdl_bus_free_mappings(struct sdl_bus_options *options) RtlFreeHeap(GetProcessHeap(), 0, mappings); } -static void sdl_bus_load_mappings(struct sdl_bus_options *options) +static void sdl_bus_load_mappings(struct bus_options *options) { ULONG idx = 0, len, count = 0, capacity, info_size, info_max_size; UNICODE_STRING path = RTL_CONSTANT_STRING(L"map"); @@ -1054,74 +1053,74 @@ static void sdl_bus_load_mappings(struct sdl_bus_options *options) NtClose(key); } +static void bus_options_init(void) +{ + options.disable_sdl = !check_bus_option(L"Enable SDL", 1); + if (options.disable_sdl) TRACE("SDL devices disabled in registry\n"); + options.disable_hidraw = check_bus_option(L"DisableHidraw", 0); + if (options.disable_hidraw) TRACE("UDEV hidraw devices disabled in registry\n"); + options.disable_input = check_bus_option(L"DisableInput", 0); + if (options.disable_input) TRACE("UDEV input devices disabled in registry\n"); + options.disable_udevd = check_bus_option(L"DisableUdevd", 0); + if (options.disable_udevd) TRACE("UDEV udevd use disabled in registry\n"); + + if (!options.disable_sdl) + { + options.split_controllers = check_bus_option(L"Split Controllers", 0); + if (options.split_controllers) TRACE("SDL controller splitting enabled\n"); + options.map_controllers = check_bus_option(L"Map Controllers", 1); + if (!options.map_controllers) TRACE("SDL controller to XInput HID gamepad mapping disabled\n"); + sdl_bus_load_mappings(&options); + } +} + +static void bus_options_cleanup(void) +{ + if (!options.disable_sdl) sdl_bus_free_mappings(&options); + memset(&options, 0, sizeof(options)); +} + static NTSTATUS sdl_driver_init(void) { - struct sdl_bus_options bus_options; struct bus_main_params bus = { .name = L"SDL", - .init_args = &bus_options, + .init_args = &options, .init_code = sdl_init, .wait_code = sdl_wait, }; - NTSTATUS status; - - bus_options.split_controllers = check_bus_option(L"Split Controllers", 0); - if (bus_options.split_controllers) TRACE("SDL controller splitting enabled\n"); - bus_options.map_controllers = check_bus_option(L"Map Controllers", 1); - if (!bus_options.map_controllers) TRACE("SDL controller to XInput HID gamepad mapping disabled\n"); - sdl_bus_load_mappings(&bus_options); - - status = bus_main_thread_start(&bus); - sdl_bus_free_mappings(&bus_options); - return status; + if (options.disable_sdl) return STATUS_SUCCESS; + return bus_main_thread_start(&bus); } -static NTSTATUS udev_driver_init(BOOL enable_sdl) +static NTSTATUS udev_driver_init(void) { - struct udev_bus_options bus_options; struct bus_main_params bus = { .name = L"UDEV", - .init_args = &bus_options, + .init_args = &options, .init_code = udev_init, .wait_code = udev_wait, }; - - bus_options.disable_hidraw = check_bus_option(L"DisableHidraw", 0); - if (bus_options.disable_hidraw) TRACE("UDEV hidraw devices disabled in registry\n"); - bus_options.disable_input = check_bus_option(L"DisableInput", 0); - if (bus_options.disable_input) TRACE("UDEV input devices disabled in registry\n"); - bus_options.disable_udevd = check_bus_option(L"DisableUdevd", 0); - if (bus_options.disable_udevd) TRACE("UDEV udevd use disabled in registry\n"); - return bus_main_thread_start(&bus); } static NTSTATUS iohid_driver_init(void) { - struct iohid_bus_options bus_options; struct bus_main_params bus = { .name = L"IOHID", - .init_args = &bus_options, + .init_args = &options, .init_code = iohid_init, .wait_code = iohid_wait, }; - - if (check_bus_option(L"DisableHidraw", FALSE)) - { - TRACE("IOHID hidraw devices disabled in registry\n"); - return STATUS_SUCCESS; - } - + if (options.disable_hidraw) return STATUS_SUCCESS; return bus_main_thread_start(&bus); } static NTSTATUS fdo_pnp_dispatch(DEVICE_OBJECT *device, IRP *irp) { IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation(irp); - BOOL enable_sdl; NTSTATUS ret; switch (irpsp->MinorFunction) @@ -1130,12 +1129,13 @@ static NTSTATUS fdo_pnp_dispatch(DEVICE_OBJECT *device, IRP *irp) irp->IoStatus.Status = handle_IRP_MN_QUERY_DEVICE_RELATIONS(irp); break; case IRP_MN_START_DEVICE: + bus_options_init(); + mouse_device_create(); keyboard_device_create(); - if ((enable_sdl = check_bus_option(L"Enable SDL", 1))) - enable_sdl = !sdl_driver_init(); - udev_driver_init(enable_sdl); + sdl_driver_init(); + udev_driver_init(); iohid_driver_init(); irp->IoStatus.Status = STATUS_SUCCESS; @@ -1156,6 +1156,8 @@ static NTSTATUS fdo_pnp_dispatch(DEVICE_OBJECT *device, IRP *irp) ret = IoCallDriver(bus_pdo, irp); IoDetachDevice(bus_pdo); IoDeleteDevice(device); + + bus_options_cleanup(); return ret; default: FIXME("Unhandled minor function %#x.\n", irpsp->MinorFunction); diff --git a/dlls/winebus.sys/unixlib.h b/dlls/winebus.sys/unixlib.h index 02e7a1c6953e..8064cfe6ffac 100644 --- a/dlls/winebus.sys/unixlib.h +++ b/dlls/winebus.sys/unixlib.h @@ -47,26 +47,18 @@ struct device_desc WCHAR serialnumber[MAX_PATH]; }; -struct sdl_bus_options +struct bus_options { + BOOL disable_sdl; + BOOL disable_hidraw; + BOOL disable_input; + BOOL disable_udevd; BOOL split_controllers; BOOL map_controllers; - /* freed after bus_init */ UINT mappings_count; char **mappings; }; -struct udev_bus_options -{ - BOOL disable_hidraw; - BOOL disable_input; - BOOL disable_udevd; -}; - -struct iohid_bus_options -{ -}; - enum bus_event_type { BUS_EVENT_TYPE_NONE, From 64ac44ff83993ae692c9f12a71725aeb552cadde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 10 Jun 2025 19:19:24 +0200 Subject: [PATCH 027/454] winebus: Keep per-device or per-vendor options. CW-Bug-Id: #25366 --- dlls/winebus.sys/main.c | 136 ++++++++++++++++++++++++++++++++++++- dlls/winebus.sys/unixlib.h | 9 +++ 2 files changed, 144 insertions(+), 1 deletion(-) diff --git a/dlls/winebus.sys/main.c b/dlls/winebus.sys/main.c index dd9b233ec7bb..78c75252ffd2 100644 --- a/dlls/winebus.sys/main.c +++ b/dlls/winebus.sys/main.c @@ -19,6 +19,7 @@ */ #include +#include #include #include "ntstatus.h" @@ -51,7 +52,7 @@ static DEVICE_OBJECT *keyboard_obj; static DEVICE_OBJECT *bus_pdo; static DEVICE_OBJECT *bus_fdo; -static struct bus_options options; +static struct bus_options options = {.devices = LIST_INIT(options.devices)}; static HANDLE driver_key; struct hid_report @@ -455,6 +456,7 @@ static BOOL is_hidraw_enabled(WORD vid, WORD pid, const USAGE_AND_PAGE *usages, { char buffer[FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data[1024])]; KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)buffer; + struct device_options *device; WCHAR vidpid[MAX_PATH], *tmp, value[1024]; BOOL prefer_hidraw = FALSE; UNICODE_STRING str; @@ -463,6 +465,14 @@ static BOOL is_hidraw_enabled(WORD vid, WORD pid, const USAGE_AND_PAGE *usages, if (options.disable_hidraw) return FALSE; + LIST_FOR_EACH_ENTRY(device, &options.devices, struct device_options, entry) + { + if (device->vid != vid) continue; + if (device->pid != -1 && device->pid != pid) continue; + if (device->hidraw == -1) continue; + return !!device->hidraw; + } + if (usages->UsagePage == HID_USAGE_PAGE_DIGITIZER) { WARN("Ignoring unsupported %04X:%04X hidraw touchscreen\n", vid, pid); @@ -1053,8 +1063,93 @@ static void sdl_bus_load_mappings(struct bus_options *options) NtClose(key); } +static struct device_options *add_device_options(UINT vid, UINT pid) +{ + struct device_options *device, *next; + + LIST_FOR_EACH_ENTRY(device, &options.devices, struct device_options, entry) + if (device->vid == vid && device->pid == pid) return device; + + if (!(device = calloc(1, sizeof(*device)))) return NULL; + device->vid = vid; + device->pid = pid; + device->hidraw = -1; + + LIST_FOR_EACH_ENTRY(next, &options.devices, struct device_options, entry) + if (next->vid > vid || (next->vid == vid && next->pid > pid)) break; + list_add_before(&next->entry, &device->entry); + + return device; +} + +static void load_device_options(void) +{ + char buffer[FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data[1024])]; + KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)buffer; + UNICODE_STRING path = RTL_CONSTANT_STRING(L"Devices"); + ULONG idx = 0, size, name_max_size; + OBJECT_ATTRIBUTES attr = {0}; + KEY_NAME_INFORMATION *name; + WCHAR name_buffer[32]; + HANDLE key, subkey; + NTSTATUS status; + + InitializeObjectAttributes(&attr, &path, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, driver_key, NULL); + status = NtOpenKey(&key, KEY_ALL_ACCESS, &attr); + if (status) return; + + name_max_size = offsetof(KEY_NAME_INFORMATION, Name) + 512; + name = RtlAllocateHeap(GetProcessHeap(), 0, name_max_size); + + while (!status && name) + { + static const UNICODE_STRING hidraw = RTL_CONSTANT_STRING(L"Hidraw"); + static const UNICODE_STRING backslash = RTL_CONSTANT_STRING(L"\\"); + struct device_options *device; + UNICODE_STRING name_str; + UINT vid, pid; + USHORT pos; + int ret; + + status = NtEnumerateKey(key, idx, KeyNameInformation, name, name_max_size, &size); + while (status == STATUS_BUFFER_OVERFLOW) + { + name_max_size = size; + if (!(name = RtlReAllocateHeap(GetProcessHeap(), 0, name, name_max_size))) break; + status = NtEnumerateKey(key, idx, KeyNameInformation, name, name_max_size, &size); + } + if (status == STATUS_NO_MORE_ENTRIES) break; + idx++; + + name_str.Buffer = name->Name; + name_str.Length = name->NameLength; + InitializeObjectAttributes(&attr, &name_str, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, 0, NULL); + if (NtOpenKey(&subkey, KEY_ALL_ACCESS, &attr)) continue; + + if (!RtlFindCharInUnicodeString(1, &name_str, &backslash, &pos)) pos += sizeof(WCHAR); + if (name->NameLength - pos >= sizeof(name_buffer)) continue; + + memcpy(name_buffer, name->Name + pos / sizeof(WCHAR), name->NameLength - pos); + name_buffer[(name->NameLength - pos) / sizeof(WCHAR)] = 0; + + if ((ret = swscanf(name_buffer, L"%04x/%04x", &vid, &pid)) < 1) continue; + if (!(device = add_device_options(vid, ret == 1 ? -1 : pid))) continue; + + if (!NtQueryValueKey(subkey, &hidraw, KeyValuePartialInformation, info, sizeof(buffer), &size) && info->Type == REG_DWORD) + device->hidraw = *(DWORD *)info->Data; + if (device->hidraw != -1) TRACE("- %04x/%04x: %sabling hidraw\n", device->vid, device->pid, device->hidraw ? "en" : "dis"); + + NtClose(subkey); + } + + RtlFreeHeap(GetProcessHeap(), 0, name); + NtClose(key); +} + static void bus_options_init(void) { + char *env; + options.disable_sdl = !check_bus_option(L"Enable SDL", 1); if (options.disable_sdl) TRACE("SDL devices disabled in registry\n"); options.disable_hidraw = check_bus_option(L"DisableHidraw", 0); @@ -1072,12 +1167,51 @@ static void bus_options_init(void) if (!options.map_controllers) TRACE("SDL controller to XInput HID gamepad mapping disabled\n"); sdl_bus_load_mappings(&options); } + + load_device_options(); + + if ((env = getenv("WINEBUSCONFIG")) && (env = strdup(env))) + { + struct device_options *device; + UINT vid, pid; + int ret; + + TRACE("Parsing WINEBUSCONFIG %s\n", debugstr_a(env)); + + for (const char *next, *opt = strtok(env, ","); opt; opt = strtok(NULL, ",")) + { + if ((ret = sscanf(opt, "%04x/%04x=", &vid, &pid)) < 1) continue; + if (!(device = add_device_options(vid, ret == 1 ? -1 : pid))) break; + + for (opt = strchr(opt + 1, '='); opt; opt = next) + { + if (!strncmp(opt + 1, "hidraw", 6)) device->hidraw = 1; + else if (!strncmp(opt + 1, "nohidraw", 8)) device->hidraw = 0; + + if (!(next = strchr(opt + 1, '/'))) break; + } + + if (device->hidraw != -1) TRACE("- %04x/%04x: %sabling hidraw\n", device->vid, device->pid, device->hidraw ? "en" : "dis"); + } + + free(env); + } } static void bus_options_cleanup(void) { + struct device_options *device, *next; + if (!options.disable_sdl) sdl_bus_free_mappings(&options); + + LIST_FOR_EACH_ENTRY_SAFE(device, next, &options.devices, struct device_options, entry) + { + list_remove(&device->entry); + free(device); + } + memset(&options, 0, sizeof(options)); + list_init(&options.devices); } static NTSTATUS sdl_driver_init(void) diff --git a/dlls/winebus.sys/unixlib.h b/dlls/winebus.sys/unixlib.h index 8064cfe6ffac..8dab8af0d570 100644 --- a/dlls/winebus.sys/unixlib.h +++ b/dlls/winebus.sys/unixlib.h @@ -47,6 +47,14 @@ struct device_desc WCHAR serialnumber[MAX_PATH]; }; +struct device_options +{ + struct list entry; + UINT vid; + UINT pid; + INT hidraw; +}; + struct bus_options { BOOL disable_sdl; @@ -56,6 +64,7 @@ struct bus_options BOOL split_controllers; BOOL map_controllers; UINT mappings_count; + struct list devices; char **mappings; }; From 530ccd7ec5240cdec734caa598b78c08d6af368a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 19 Jun 2025 19:09:04 +0200 Subject: [PATCH 028/454] services: Preserve proton-specific hidraw variables. Link: https://github.com/ValveSoftware/Proton/issues/8672 --- programs/services/services.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/programs/services/services.c b/programs/services/services.c index 3fb5ed28ae1a..78ac1bd34974 100644 --- a/programs/services/services.c +++ b/programs/services/services.c @@ -1034,6 +1034,8 @@ static DWORD service_start_process(struct service_entry *service_entry, struct p { L"WINEBOOTSTRAPMODE", L"WINEBUSCONFIG", + L"PROTON_DISABLE_HIDRAW", + L"PROTON_ENABLE_HIDRAW", }; WCHAR buffer[1024]; From 9a05f43e73d55bc14c44ceb2488ae14d344b213f Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 11 Jul 2025 10:41:26 -0600 Subject: [PATCH 029/454] winebus.sys: Check for udev_device_get_subsystem() error in udev_add_device(). (cherry picked from commit aaa51a981dcd230d5eb1d8f01e79579d44e1d110) --- dlls/winebus.sys/bus_udev.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/dlls/winebus.sys/bus_udev.c b/dlls/winebus.sys/bus_udev.c index a71e979d0650..6ed96eba7cd6 100644 --- a/dlls/winebus.sys/bus_udev.c +++ b/dlls/winebus.sys/bus_udev.c @@ -1593,7 +1593,13 @@ static void udev_add_device(struct udev_device *dev, int fd) get_device_subsystem_info(dev, "usb", "usb_device", &desc, &bus); if (bus == BUS_BLUETOOTH) desc.is_bluetooth = TRUE; - subsystem = udev_device_get_subsystem(dev); + if (!(subsystem = udev_device_get_subsystem(dev))) + { + WARN("udev_device_get_subsystem failed for %s.\n", debugstr_a(devnode)); + close(fd); + return; + } + if (!strcmp(subsystem, "hidraw")) { static const WCHAR hidraw[] = {'h','i','d','r','a','w',0}; From 8ade842f8bedea3437c466876af7cdcddbc8ba3f Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 11 Jul 2025 10:42:27 -0600 Subject: [PATCH 030/454] winebus.sys: Close fd if device is not handled in udev_add_device(). (cherry picked from commit 70cc61adf8e688f0f48b16329b6f98443b0cc7eb) --- dlls/winebus.sys/bus_udev.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/dlls/winebus.sys/bus_udev.c b/dlls/winebus.sys/bus_udev.c index 6ed96eba7cd6..25bae061e040 100644 --- a/dlls/winebus.sys/bus_udev.c +++ b/dlls/winebus.sys/bus_udev.c @@ -1715,6 +1715,10 @@ static void udev_add_device(struct udev_device *dev, int fd) bus_event_queue_device_created(&event_queue, &impl->unix_device, &desc); } #endif + else + { + close(fd); + } } #ifdef HAVE_SYS_INOTIFY_H From 759cd56f5f220f10f7c6b1c5d5bdaf4c1b5c29ce Mon Sep 17 00:00:00 2001 From: Arkadiusz Hiler Date: Fri, 18 Jul 2025 12:09:25 +0300 Subject: [PATCH 031/454] HACK: winebus.sys: Fall back to non-hidraw devices. Based on Proton 9's 72503e35e55f ("HACK: winebus.sys: Prefer devices on UDEV hidraw bus over SDL bus.") CW-Bug-Id: #25270 --- dlls/winebus.sys/main.c | 37 ++++++++++++++++++++++++++++++++----- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/dlls/winebus.sys/main.c b/dlls/winebus.sys/main.c index 78c75252ffd2..2323cb5b75eb 100644 --- a/dlls/winebus.sys/main.c +++ b/dlls/winebus.sys/main.c @@ -371,6 +371,17 @@ static DEVICE_OBJECT *bus_find_unix_device(UINT64 unix_device) return NULL; } +static DEVICE_OBJECT *bus_find_device_from_vid_pid(const BOOL is_hidraw, struct device_desc *desc) +{ + struct device_extension *ext; + + LIST_FOR_EACH_ENTRY(ext, &device_list, struct device_extension, entry) + if (ext->desc.is_hidraw == is_hidraw && ext->desc.vid == desc->vid && + ext->desc.pid == desc->pid) return ext->device; + + return NULL; +} + static void bus_unlink_hid_device(DEVICE_OBJECT *device) { struct device_extension *ext = (struct device_extension *)device->DeviceExtension; @@ -914,7 +925,7 @@ static DWORD CALLBACK bus_main_thread(void *args) UINT buttons; usages = get_device_usages(event->device, &buttons); - if (!desc.is_hidraw != !is_hidraw_enabled(desc.vid, desc.pid, &usages, buttons)) + if (desc.is_hidraw && !is_hidraw_enabled(desc.vid, desc.pid, &usages, buttons)) { struct device_remove_params params = {.device = event->device}; WARN("ignoring %shidraw device %04x:%04x with usages %04x:%04x\n", desc.is_hidraw ? "" : "non-", @@ -922,11 +933,27 @@ static DWORD CALLBACK bus_main_thread(void *args) winebus_call(device_remove, ¶ms); break; } + else if (desc.is_hidraw) + { + RtlEnterCriticalSection(&device_list_cs); + if ((device = bus_find_device_from_vid_pid(!desc.is_hidraw, &event->device_created.desc))) + bus_unlink_hid_device(device); + device = bus_create_hid_device(&event->device_created.desc, event->device); + RtlLeaveCriticalSection(&device_list_cs); + } + else + { + RtlEnterCriticalSection(&device_list_cs); + if (bus_find_device_from_vid_pid(!desc.is_hidraw, &event->device_created.desc)) device = NULL; + else device = bus_create_hid_device(&event->device_created.desc, event->device); + RtlLeaveCriticalSection(&device_list_cs); + } + - TRACE("creating %shidraw device %04x:%04x with usages %04x:%04x\n", desc.is_hidraw ? "" : "non-", - desc.vid, desc.pid, usages.UsagePage, usages.Usage); + if (device) + TRACE("creating %shidraw device %04x:%04x with usages %04x:%04x\n", desc.is_hidraw ? "" : "non-", + desc.vid, desc.pid, usages.UsagePage, usages.Usage); - device = bus_create_hid_device(&event->device_created.desc, event->device); if (device) IoInvalidateDeviceRelations(bus_pdo, BusRelations); else { @@ -1268,8 +1295,8 @@ static NTSTATUS fdo_pnp_dispatch(DEVICE_OBJECT *device, IRP *irp) mouse_device_create(); keyboard_device_create(); - sdl_driver_init(); udev_driver_init(); + sdl_driver_init(); iohid_driver_init(); irp->IoStatus.Status = STATUS_SUCCESS; From 1428de3b0e644272823ec8121878610a4bb4e834 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cs=C3=A1nyi=20Istv=C3=A1n?= Date: Mon, 28 Jul 2025 13:47:04 +0200 Subject: [PATCH 032/454] winebus.sys: Fix DualSense BT quirk. We need to memmove 11 bytes, otherwise buffer[10] ends up as a copy of buffer[9] which resulted in the second byte of button state being wrongly re-used as a third byte as well. The original code did not suffer from this - while the length check was also one off, and likely informed the one off memmove, the buffer pointer was just incremented so everything "moved" correctly. Fixes: c708295ed6de ("winebus: Move Sony controllers report fixups to PE side.") Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=56883 Signed-off-by: Arkadiusz Hiler (cherry picked from commit 22e764c7a067ef8348aee0c21a6fb9b7d0f26088) --- dlls/winebus.sys/main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dlls/winebus.sys/main.c b/dlls/winebus.sys/main.c index 2323cb5b75eb..2fda3e12cafb 100644 --- a/dlls/winebus.sys/main.c +++ b/dlls/winebus.sys/main.c @@ -670,11 +670,11 @@ static void process_hid_report(DEVICE_OBJECT *device, BYTE *report_buf, DWORD re * Extended #41 report: * Prefix X Y Z Rz TriggerLeft TriggerRight Counter Buttons[3] ... */ - if (report->buffer[0] == 0x31 && report->length >= 11) + if (report->buffer[0] == 0x31 && report->length >= 12) { BYTE trigger[2]; - memmove(report->buffer, report->buffer + 1, 10); + memmove(report->buffer, report->buffer + 1, 11); report->buffer[0] = 1; /* fake report #1 */ report->length = 10; From 6f3c1c64c7b2416ea67dacf5522db6cfb03471a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 4 Aug 2025 12:36:42 +0200 Subject: [PATCH 033/454] windows.gaming.input: Forward get_NonRoamableId to Wine provider. (cherry picked from commit d2528d35d711a5707f9d6a3b0b7ce92931295206) --- dlls/windows.gaming.input/controller.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/windows.gaming.input/controller.c b/dlls/windows.gaming.input/controller.c index 1adbc5cce0b5..2276b9e947e3 100644 --- a/dlls/windows.gaming.input/controller.c +++ b/dlls/windows.gaming.input/controller.c @@ -362,7 +362,7 @@ static HRESULT WINAPI raw_controller_2_get_SimpleHapticsControllers( IRawGameCon return hr; } -static HRESULT WINAPI raw_controller_2_get_NonRoamableId( IRawGameController2 *iface, HSTRING* value ) +static HRESULT WINAPI raw_controller_2_get_NonRoamableId( IRawGameController2 *iface, HSTRING *value ) { struct controller *impl = impl_from_IRawGameController2( iface ); return IWineGameControllerProvider_get_NonRoamableId( impl->wine_provider, value ); From a7ca6fb32be1cebe1ebe7a5eafaae716061477dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 19 Aug 2025 14:55:10 +0200 Subject: [PATCH 034/454] windows.gaming.input: Forward get_DisplayName to Wine provider. (cherry picked from commit f5922026b9a37616641072f96c0e26efc8e001e5) --- dlls/dinput/tests/joystick8.c | 14 +++++--------- dlls/windows.gaming.input/controller.c | 6 +++--- dlls/windows.gaming.input/provider.c | 13 +++++++++++++ dlls/windows.gaming.input/provider.idl | 1 + 4 files changed, 22 insertions(+), 12 deletions(-) diff --git a/dlls/dinput/tests/joystick8.c b/dlls/dinput/tests/joystick8.c index f765f5ba4eed..20c959cabb28 100644 --- a/dlls/dinput/tests/joystick8.c +++ b/dlls/dinput/tests/joystick8.c @@ -5215,16 +5215,12 @@ static void test_windows_gaming_input(void) ok( hr == S_OK, "QueryInterface returned %#lx\n", hr ); hr = IRawGameController2_get_DisplayName( raw_controller2, &str ); - todo_wine ok( hr == S_OK, "get_DisplayName returned %#lx\n", hr ); - if (hr == S_OK) - { - buffer = pWindowsGetStringRawBuffer( str, &length ); - todo_wine - ok( !wcscmp( buffer, L"HID-compliant game controller" ), - "get_DisplayName returned %s\n", debugstr_wn( buffer, length ) ); - pWindowsDeleteString( str ); - } + buffer = pWindowsGetStringRawBuffer( str, &length ); + todo_wine + ok( !wcscmp( buffer, L"HID-compliant game controller" ), + "get_DisplayName returned %s\n", debugstr_wn( buffer, length ) ); + pWindowsDeleteString( str ); hr = IRawGameController2_get_NonRoamableId( raw_controller2, &str ); todo_wine diff --git a/dlls/windows.gaming.input/controller.c b/dlls/windows.gaming.input/controller.c index 2276b9e947e3..7e7b570d7f6f 100644 --- a/dlls/windows.gaming.input/controller.c +++ b/dlls/windows.gaming.input/controller.c @@ -368,10 +368,10 @@ static HRESULT WINAPI raw_controller_2_get_NonRoamableId( IRawGameController2 *i return IWineGameControllerProvider_get_NonRoamableId( impl->wine_provider, value ); } -static HRESULT WINAPI raw_controller_2_get_DisplayName( IRawGameController2 *iface, HSTRING* value ) +static HRESULT WINAPI raw_controller_2_get_DisplayName( IRawGameController2 *iface, HSTRING *value ) { - FIXME( "iface %p, value %p stub!\n", iface, value ); - return E_NOTIMPL; + struct controller *impl = impl_from_IRawGameController2( iface ); + return IWineGameControllerProvider_get_DisplayName( impl->wine_provider, value ); } static const struct IRawGameController2Vtbl raw_controller_2_vtbl = diff --git a/dlls/windows.gaming.input/provider.c b/dlls/windows.gaming.input/provider.c index 3515d5c9ba66..7c8dea0df8b7 100644 --- a/dlls/windows.gaming.input/provider.c +++ b/dlls/windows.gaming.input/provider.c @@ -141,6 +141,18 @@ static HRESULT WINAPI wine_provider_GetTrustLevel( IWineGameControllerProvider * return E_NOTIMPL; } +static HRESULT WINAPI wine_provider_get_DisplayName( IWineGameControllerProvider *iface, HSTRING *value ) +{ + struct provider *impl = impl_from_IWineGameControllerProvider( iface ); + DIDEVICEINSTANCEW instance = {.dwSize = sizeof(DIDEVICEINSTANCEW)}; + HRESULT hr; + + TRACE( "iface %p, value %p\n", iface, value ); + + if (FAILED(hr = IDirectInputDevice8_GetDeviceInfo( impl->dinput_device, &instance ))) return hr; + return WindowsCreateString( instance.tszProductName, wcslen( instance.tszProductName ), value ); +} + static BOOL CALLBACK count_ffb_axes( const DIDEVICEOBJECTINSTANCEW *obj, void *args ) { DWORD *count = args; @@ -416,6 +428,7 @@ static const struct IWineGameControllerProviderVtbl wine_provider_vtbl = wine_provider_GetTrustLevel, /* IWineGameControllerProvider methods */ wine_provider_get_NonRoamableId, + wine_provider_get_DisplayName, wine_provider_get_Type, wine_provider_get_AxisCount, wine_provider_get_ButtonCount, diff --git a/dlls/windows.gaming.input/provider.idl b/dlls/windows.gaming.input/provider.idl index a6fcc6e84f3d..fb7614b5c1c4 100644 --- a/dlls/windows.gaming.input/provider.idl +++ b/dlls/windows.gaming.input/provider.idl @@ -174,6 +174,7 @@ namespace Windows.Gaming.Input.Custom { requires Windows.Gaming.Input.Custom.IGameControllerProvider { [propget] HRESULT NonRoamableId([out, retval] HSTRING *value); + [propget] HRESULT DisplayName([out, retval] HSTRING *value); [propget] HRESULT Type([out, retval] WineGameControllerType *value); [propget] HRESULT AxisCount([out, retval] INT32 *value); From 6d21af414cf1641b0a16f5e3e7dff200585b0ecc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Paku=C5=82a?= Date: Mon, 24 Mar 2025 22:00:43 +0100 Subject: [PATCH 035/454] dinput/tests: Add tests for 6-axis force feedback joystick. (cherry picked from commit d2dbc8db0a5995279a04f9bfe3c59dbc348e98ff) CW-Bug-Id: #25366 --- dlls/dinput/tests/force_feedback.c | 747 ++++++++++++++++++++++++++++- 1 file changed, 746 insertions(+), 1 deletion(-) diff --git a/dlls/dinput/tests/force_feedback.c b/dlls/dinput/tests/force_feedback.c index 14bf9575e8f5..89caa86af9d6 100644 --- a/dlls/dinput/tests/force_feedback.c +++ b/dlls/dinput/tests/force_feedback.c @@ -3659,6 +3659,750 @@ static BOOL test_force_feedback_joystick( DWORD version ) return device != NULL; } +static void test_condition_effect_six_axes( IDirectInputDevice8W *device, HANDLE file ) +{ + struct hid_expect expect_create[] = + { + /* set condition */ + { + .code = IOCTL_HID_WRITE_REPORT, + .report_id = 7, + .report_len = 9, + .report_buf = {0x07,0x00,0x00,0x80,0x80,0x80,0x00,0x00,0x00}, + }, + /* set condition */ + { + .code = IOCTL_HID_WRITE_REPORT, + .report_id = 7, + .report_len = 9, + .report_buf = {0x07,0x00,0x00,0x7f,0x7f,0x7f,0xff,0xff,0xff}, + }, + /* set condition */ + { + .code = IOCTL_HID_WRITE_REPORT, + .report_id = 7, + .report_len = 9, + .report_buf = {0x07,0x00,0x00,0x80,0x7f,0x80,0xff,0xff,0x00}, + }, + /* set condition */ + { + .code = IOCTL_HID_WRITE_REPORT, + .report_id = 7, + .report_len = 9, + .report_buf = {0x07,0x00,0x00,0x7f,0xff,0x80,0x00,0xff,0xff}, + }, + /* set condition */ + { + .code = IOCTL_HID_WRITE_REPORT, + .report_id = 7, + .report_len = 9, + .report_buf = {0x07,0x00,0x00,0xff,0x7f,0xff,0xff,0x00,0x00}, + }, + /* set condition */ + { + .code = IOCTL_HID_WRITE_REPORT, + .report_id = 7, + .report_len = 9, + .report_buf = {0x07,0x00,0x00,0x7f,0x7f,0xff,0xff,0x00,0xff}, + }, + /* create effect */ + { + .code = IOCTL_HID_WRITE_REPORT, + .report_id = 3, + .report_len = 15, + .report_buf = {0x03,0x01,0x03,0x40,0x01,0x00,0x06,0x00,0x01,0x7f,0x2a,0xea,0xf5,0x1f,0x00}, + }, + }; + struct hid_expect expect_destroy[] = + { + /* effect operation */ + { + .code = IOCTL_HID_WRITE_REPORT, + .report_id = 2, + .report_len = 4, + .report_buf = {0x02,0x01,0x03,0x00}, + }, + }; + static const DWORD expect_axes[6] = + { + DIDFT_ABSAXIS | DIDFT_MAKEINSTANCE( 4 ) | DIDFT_FFACTUATOR, + DIDFT_ABSAXIS | DIDFT_MAKEINSTANCE( 2 ) | DIDFT_FFACTUATOR, + DIDFT_ABSAXIS | DIDFT_MAKEINSTANCE( 5 ) | DIDFT_FFACTUATOR, + DIDFT_ABSAXIS | DIDFT_MAKEINSTANCE( 0 ) | DIDFT_FFACTUATOR, + DIDFT_ABSAXIS | DIDFT_MAKEINSTANCE( 3 ) | DIDFT_FFACTUATOR, + DIDFT_ABSAXIS | DIDFT_MAKEINSTANCE( 1 ) | DIDFT_FFACTUATOR, + }; + static const LONG expect_directions[6] = + { + 9000, + 6000, + 33000, + 34500, + 4500, + 0, + }; + static const DIENVELOPE expect_envelope = + { + .dwSize = sizeof(DIENVELOPE), + .dwAttackLevel = 1000, + .dwAttackTime = 2000, + .dwFadeLevel = 3000, + .dwFadeTime = 4000, + }; + static const DICONDITION expect_condition[6] = + { + { + .lOffset = -10000, + .lPositiveCoefficient = -10000, + .lNegativeCoefficient = -10000, + .dwPositiveSaturation = 0, + .dwNegativeSaturation = 0, + .lDeadBand = 0, + }, + { + .lOffset = 10000, + .lPositiveCoefficient = 10000, + .lNegativeCoefficient = 10000, + .dwPositiveSaturation = 10000, + .dwNegativeSaturation = 10000, + .lDeadBand = 10000, + }, + { + .lOffset = -10000, + .lPositiveCoefficient = +10000, + .lNegativeCoefficient = -10000, + .dwPositiveSaturation = +10000, + .dwNegativeSaturation = -10000, + .lDeadBand = 0, + }, + { + .lOffset = +10000, + .lPositiveCoefficient = 0, + .lNegativeCoefficient = -10000, + .dwPositiveSaturation = 0, + .dwNegativeSaturation = -10000, + .lDeadBand = 20000, + }, + { + .lOffset = 0, + .lPositiveCoefficient = +10000, + .lNegativeCoefficient = 0, + .dwPositiveSaturation = +10000, + .dwNegativeSaturation = 0, + .lDeadBand = 0, + }, + { + .lOffset = 10000, + .lPositiveCoefficient = +10000, + .lNegativeCoefficient = 0, + .dwPositiveSaturation = +10000, + .dwNegativeSaturation = 0, + .lDeadBand = 20000, + }, + }; + const DIEFFECT expect_desc = + { + .dwSize = sizeof(DIEFFECT_DX6), + .dwFlags = DIEFF_SPHERICAL | DIEFF_OBJECTIDS, + .dwDuration = 1000, + .dwSamplePeriod = 2000, + .dwGain = 3000, + .dwTriggerButton = DIDFT_PSHBUTTON | DIDFT_MAKEINSTANCE( 0 ) | DIDFT_FFEFFECTTRIGGER, + .dwTriggerRepeatInterval = 5000, + .cAxes = 6, + .rgdwAxes = (void *)expect_axes, + .rglDirection = (void *)expect_directions, + .lpEnvelope = (void *)&expect_envelope, + .cbTypeSpecificParams = 6 * sizeof(DICONDITION), + .lpvTypeSpecificParams = (void *)expect_condition, + .dwStartDelay = 6000, + }; + struct check_created_effect_params check_params = {0}; + DIENVELOPE envelope = {.dwSize = sizeof(DIENVELOPE)}; + DICONDITION condition[6] = {{0}}; + IDirectInputEffect *effect; + LONG directions[6] = {0}; + DWORD axes[6] = {0}; + DIEFFECT desc = + { + .dwSize = sizeof(DIEFFECT_DX6), + .dwFlags = DIEFF_SPHERICAL | DIEFF_OBJECTIDS, + .cAxes = 6, + .rgdwAxes = axes, + .rglDirection = directions, + .lpEnvelope = &envelope, + .cbTypeSpecificParams = 6 * sizeof(DICONDITION), + .lpvTypeSpecificParams = condition, + }; + HRESULT hr; + ULONG ref; + GUID guid; + + set_hid_expect( file, expect_create, sizeof(expect_create) ); + hr = IDirectInputDevice8_CreateEffect( device, &GUID_Spring, &expect_desc, &effect, NULL ); + ok( hr == DI_OK, "CreateEffect returned %#lx\n", hr ); + set_hid_expect( file, NULL, 0 ); + + check_params.expect_effect = effect; + hr = IDirectInputDevice8_EnumCreatedEffectObjects( device, check_created_effect_objects, &check_params, 0 ); + ok( hr == DI_OK, "EnumCreatedEffectObjects returned %#lx\n", hr ); + ok( check_params.count == 1, "got count %lu, expected 1\n", check_params.count ); + + hr = IDirectInputEffect_GetEffectGuid( effect, &guid ); + ok( hr == DI_OK, "GetEffectGuid returned %#lx\n", hr ); + ok( IsEqualGUID( &guid, &GUID_Spring ), "got guid %s, expected %s\n", debugstr_guid( &guid ), + debugstr_guid( &GUID_Spring ) ); + + hr = IDirectInputEffect_GetParameters( effect, &desc, DIEP_ALLPARAMS ); + ok( hr == DI_OK, "GetParameters returned %#lx\n", hr ); + check_member( desc, expect_desc, "%lu", cAxes ); + check_member( desc, expect_desc, "%#lx", rgdwAxes[0] ); + check_member( desc, expect_desc, "%#lx", rgdwAxes[1] ); + check_member( desc, expect_desc, "%#lx", rgdwAxes[2] ); + check_member( desc, expect_desc, "%#lx", rgdwAxes[3] ); + check_member( desc, expect_desc, "%#lx", rgdwAxes[4] ); + check_member( desc, expect_desc, "%#lx", rgdwAxes[5] ); + check_member( desc, expect_desc, "%ld", rglDirection[0] ); + check_member( desc, expect_desc, "%ld", rglDirection[1] ); + check_member( desc, expect_desc, "%ld", rglDirection[2] ); + check_member( desc, expect_desc, "%ld", rglDirection[3] ); + check_member( desc, expect_desc, "%ld", rglDirection[4] ); + check_member( desc, expect_desc, "%ld", rglDirection[5] ); + check_member( desc, expect_desc, "%lu", cbTypeSpecificParams ); + + set_hid_expect( file, expect_destroy, sizeof(expect_destroy) ); + ref = IDirectInputEffect_Release( effect ); + ok( ref == 0, "Release returned %ld\n", ref ); + set_hid_expect( file, NULL, 0 ); +} + +static BOOL test_force_feedback_six_axes(void) +{ +#include "psh_hid_macros.h" + const unsigned char report_descriptor[] = + { + USAGE_PAGE(1, HID_USAGE_PAGE_GENERIC), + USAGE(1, HID_USAGE_GENERIC_JOYSTICK), + COLLECTION(1, Application), + USAGE(1, HID_USAGE_GENERIC_JOYSTICK), + COLLECTION(1, Report), + REPORT_ID(1, 1), + + USAGE(1, HID_USAGE_GENERIC_X), + USAGE(1, HID_USAGE_GENERIC_Y), + USAGE(1, HID_USAGE_GENERIC_Z), + USAGE(1, HID_USAGE_GENERIC_RX), + USAGE(1, HID_USAGE_GENERIC_RY), + USAGE(1, HID_USAGE_GENERIC_RZ), + LOGICAL_MINIMUM(1, 0), + LOGICAL_MAXIMUM(1, 0x7f), + PHYSICAL_MINIMUM(1, 0), + PHYSICAL_MAXIMUM(1, 0x7f), + REPORT_SIZE(1, 8), + REPORT_COUNT(1, 6), + INPUT(1, Data|Var|Abs), + + USAGE_PAGE(1, HID_USAGE_PAGE_BUTTON), + USAGE_MINIMUM(1, 1), + USAGE_MAXIMUM(1, 2), + LOGICAL_MINIMUM(1, 0), + LOGICAL_MAXIMUM(1, 1), + PHYSICAL_MINIMUM(1, 0), + PHYSICAL_MAXIMUM(1, 1), + REPORT_SIZE(1, 1), + REPORT_COUNT(1, 2), + INPUT(1, Data|Var|Abs), + REPORT_COUNT(1, 6), + INPUT(1, Cnst|Var|Abs), + END_COLLECTION, + + USAGE_PAGE(1, HID_USAGE_PAGE_PID), + USAGE(1, PID_USAGE_STATE_REPORT), + COLLECTION(1, Report), + REPORT_ID(1, 2), + + USAGE(1, PID_USAGE_DEVICE_PAUSED), + USAGE(1, PID_USAGE_ACTUATORS_ENABLED), + USAGE(1, PID_USAGE_SAFETY_SWITCH), + USAGE(1, PID_USAGE_ACTUATOR_OVERRIDE_SWITCH), + USAGE(1, PID_USAGE_ACTUATOR_POWER), + LOGICAL_MINIMUM(1, 0), + LOGICAL_MAXIMUM(1, 1), + PHYSICAL_MINIMUM(1, 0), + PHYSICAL_MAXIMUM(1, 1), + REPORT_SIZE(1, 1), + REPORT_COUNT(1, 5), + INPUT(1, Data|Var|Abs), + REPORT_COUNT(1, 3), + INPUT(1, Cnst|Var|Abs), + + USAGE(1, PID_USAGE_EFFECT_PLAYING), + LOGICAL_MINIMUM(1, 0), + LOGICAL_MAXIMUM(1, 1), + PHYSICAL_MINIMUM(1, 0), + PHYSICAL_MAXIMUM(1, 1), + REPORT_SIZE(1, 1), + REPORT_COUNT(1, 1), + INPUT(1, Data|Var|Abs), + + USAGE(1, PID_USAGE_EFFECT_BLOCK_INDEX), + LOGICAL_MAXIMUM(1, 0x7f), + LOGICAL_MINIMUM(1, 0x00), + REPORT_SIZE(1, 7), + REPORT_COUNT(1, 1), + INPUT(1, Data|Var|Abs), + END_COLLECTION, + + USAGE_PAGE(1, HID_USAGE_PAGE_PID), + USAGE(1, PID_USAGE_DEVICE_CONTROL_REPORT), + COLLECTION(1, Report), + REPORT_ID(1, 1), + + USAGE(1, PID_USAGE_DEVICE_CONTROL), + COLLECTION(1, Logical), + USAGE(1, PID_USAGE_DC_DEVICE_RESET), + USAGE(1, PID_USAGE_DC_STOP_ALL_EFFECTS), + LOGICAL_MINIMUM(1, 1), + LOGICAL_MAXIMUM(1, 2), + REPORT_SIZE(1, 8), + REPORT_COUNT(1, 1), + OUTPUT(1, Data|Ary|Abs), + END_COLLECTION, + END_COLLECTION, + + USAGE(1, PID_USAGE_EFFECT_OPERATION_REPORT), + COLLECTION(1, Report), + REPORT_ID(1, 2), + + USAGE(1, PID_USAGE_EFFECT_BLOCK_INDEX), + LOGICAL_MINIMUM(1, 0), + LOGICAL_MAXIMUM(1, 0x7f), + PHYSICAL_MINIMUM(1, 0), + PHYSICAL_MAXIMUM(1, 0x7f), + REPORT_SIZE(1, 8), + REPORT_COUNT(1, 1), + OUTPUT(1, Data|Var|Abs), + + USAGE(1, PID_USAGE_EFFECT_OPERATION), + COLLECTION(1, NamedArray), + USAGE(1, PID_USAGE_OP_EFFECT_START), + USAGE(1, PID_USAGE_OP_EFFECT_START_SOLO), + USAGE(1, PID_USAGE_OP_EFFECT_STOP), + LOGICAL_MINIMUM(1, 1), + LOGICAL_MAXIMUM(1, 3), + PHYSICAL_MINIMUM(1, 1), + PHYSICAL_MAXIMUM(1, 3), + REPORT_SIZE(1, 8), + REPORT_COUNT(1, 1), + OUTPUT(1, Data|Ary|Abs), + END_COLLECTION, + + USAGE(1, PID_USAGE_LOOP_COUNT), + LOGICAL_MINIMUM(1, 0), + LOGICAL_MAXIMUM(1, 0x7f), + PHYSICAL_MINIMUM(1, 0), + PHYSICAL_MAXIMUM(1, 0x7f), + REPORT_SIZE(1, 8), + REPORT_COUNT(1, 1), + OUTPUT(1, Data|Var|Abs), + END_COLLECTION, + + USAGE(1, PID_USAGE_SET_EFFECT_REPORT), + COLLECTION(1, Report), + REPORT_ID(1, 3), + + USAGE(1, PID_USAGE_EFFECT_BLOCK_INDEX), + LOGICAL_MINIMUM(1, 0), + LOGICAL_MAXIMUM(1, 0x7f), + PHYSICAL_MINIMUM(1, 0), + PHYSICAL_MAXIMUM(1, 0x7f), + REPORT_SIZE(1, 8), + REPORT_COUNT(1, 1), + OUTPUT(1, Data|Var|Abs), + + USAGE(1, PID_USAGE_EFFECT_TYPE), + COLLECTION(1, NamedArray), + USAGE(1, PID_USAGE_ET_SQUARE), + USAGE(1, PID_USAGE_ET_SINE), + USAGE(1, PID_USAGE_ET_SPRING), + USAGE(1, PID_USAGE_ET_CONSTANT_FORCE), + LOGICAL_MINIMUM(1, 1), + LOGICAL_MAXIMUM(1, 4), + PHYSICAL_MINIMUM(1, 1), + PHYSICAL_MAXIMUM(1, 4), + REPORT_SIZE(1, 8), + REPORT_COUNT(1, 1), + OUTPUT(1, Data|Ary|Abs), + END_COLLECTION, + + USAGE(1, PID_USAGE_AXES_ENABLE), + COLLECTION(1, Logical), + USAGE(4, (HID_USAGE_PAGE_GENERIC << 16)|HID_USAGE_GENERIC_X), + USAGE(4, (HID_USAGE_PAGE_GENERIC << 16)|HID_USAGE_GENERIC_Y), + USAGE(4, (HID_USAGE_PAGE_GENERIC << 16)|HID_USAGE_GENERIC_Z), + USAGE(4, (HID_USAGE_PAGE_GENERIC << 16)|HID_USAGE_GENERIC_RX), + USAGE(4, (HID_USAGE_PAGE_GENERIC << 16)|HID_USAGE_GENERIC_RY), + USAGE(4, (HID_USAGE_PAGE_GENERIC << 16)|HID_USAGE_GENERIC_RZ), + LOGICAL_MINIMUM(1, 0), + LOGICAL_MAXIMUM(1, 1), + PHYSICAL_MINIMUM(1, 0), + PHYSICAL_MAXIMUM(1, 1), + REPORT_SIZE(1, 1), + REPORT_COUNT(1, 6), + OUTPUT(1, Data|Var|Abs), + END_COLLECTION, + USAGE(1, PID_USAGE_DIRECTION_ENABLE), + REPORT_COUNT(1, 1), + OUTPUT(1, Data|Var|Abs), + REPORT_COUNT(1, 1), + OUTPUT(1, Cnst|Var|Abs), + + USAGE(1, PID_USAGE_DURATION), + USAGE(1, PID_USAGE_START_DELAY), + UNIT(2, 0x1003), /* Eng Lin:Time */ + UNIT_EXPONENT(1, -3), /* 10^-3 */ + LOGICAL_MINIMUM(1, 0), + LOGICAL_MAXIMUM(2, 0x7fff), + PHYSICAL_MINIMUM(1, 0), + PHYSICAL_MAXIMUM(2, 0x7fff), + REPORT_SIZE(1, 16), + REPORT_COUNT(1, 2), + OUTPUT(1, Data|Var|Abs), + UNIT(1, 0), + UNIT_EXPONENT(1, 0), + + USAGE(1, PID_USAGE_TRIGGER_BUTTON), + LOGICAL_MINIMUM(1, 1), + LOGICAL_MAXIMUM(1, 0x08), + PHYSICAL_MINIMUM(1, 1), + PHYSICAL_MAXIMUM(1, 0x08), + REPORT_SIZE(1, 8), + REPORT_COUNT(1, 1), + OUTPUT(1, Data|Var|Abs), + + USAGE(1, PID_USAGE_DIRECTION), + COLLECTION(1, Logical), + USAGE(4, (HID_USAGE_PAGE_ORDINAL << 16)|1), + USAGE(4, (HID_USAGE_PAGE_ORDINAL << 16)|2), + USAGE(4, (HID_USAGE_PAGE_ORDINAL << 16)|3), + USAGE(4, (HID_USAGE_PAGE_ORDINAL << 16)|4), + USAGE(4, (HID_USAGE_PAGE_ORDINAL << 16)|5), + USAGE(4, (HID_USAGE_PAGE_ORDINAL << 16)|6), + UNIT(1, 0x14), /* Eng Rot:Angular Pos */ + UNIT_EXPONENT(1, -2), /* 10^-2 */ + LOGICAL_MINIMUM(1, 0), + LOGICAL_MAXIMUM(2, 0x00ff), + PHYSICAL_MINIMUM(1, 0), + PHYSICAL_MAXIMUM(4, 0x00008ca0), + UNIT(1, 0), + REPORT_SIZE(1, 8), + REPORT_COUNT(1, 6), + OUTPUT(1, Data|Var|Abs), + UNIT_EXPONENT(1, 0), + UNIT(1, 0), + END_COLLECTION, + END_COLLECTION, + + USAGE(1, PID_USAGE_SET_PERIODIC_REPORT), + COLLECTION(1, Logical), + REPORT_ID(1, 5), + + USAGE(1, PID_USAGE_MAGNITUDE), + LOGICAL_MINIMUM(1, 0), + LOGICAL_MAXIMUM(2, 0x00ff), + PHYSICAL_MINIMUM(1, 0), + PHYSICAL_MAXIMUM(2, 0x2710), + REPORT_SIZE(1, 8), + REPORT_COUNT(1, 1), + OUTPUT(1, Data|Var|Abs), + END_COLLECTION, + + USAGE(1, PID_USAGE_SET_ENVELOPE_REPORT), + COLLECTION(1, Logical), + REPORT_ID(1, 6), + + USAGE(1, PID_USAGE_ATTACK_LEVEL), + USAGE(1, PID_USAGE_FADE_LEVEL), + LOGICAL_MINIMUM(1, 0), + LOGICAL_MAXIMUM(2, 0x00ff), + PHYSICAL_MINIMUM(1, 0), + PHYSICAL_MAXIMUM(2, 0x2710), + REPORT_SIZE(1, 8), + REPORT_COUNT(1, 2), + OUTPUT(1, Data|Var|Abs), + + USAGE(1, PID_USAGE_ATTACK_TIME), + USAGE(1, PID_USAGE_FADE_TIME), + UNIT(2, 0x1003), /* Eng Lin:Time */ + UNIT_EXPONENT(1, -3), /* 10^-3 */ + LOGICAL_MINIMUM(1, 0), + LOGICAL_MAXIMUM(2, 0x7fff), + PHYSICAL_MINIMUM(1, 0), + PHYSICAL_MAXIMUM(2, 0x7fff), + REPORT_SIZE(1, 16), + REPORT_COUNT(1, 2), + OUTPUT(1, Data|Var|Abs), + PHYSICAL_MAXIMUM(1, 0), + UNIT_EXPONENT(1, 0), + UNIT(1, 0), + END_COLLECTION, + + USAGE(1, PID_USAGE_SET_CONDITION_REPORT), + COLLECTION(1, Logical), + REPORT_ID(1, 7), + + USAGE(1, PID_USAGE_TYPE_SPECIFIC_BLOCK_OFFSET), + COLLECTION(1, Logical), + USAGE(4, (HID_USAGE_PAGE_ORDINAL << 16)|1), + USAGE(4, (HID_USAGE_PAGE_ORDINAL << 16)|2), + USAGE(4, (HID_USAGE_PAGE_ORDINAL << 16)|3), + USAGE(4, (HID_USAGE_PAGE_ORDINAL << 16)|4), + USAGE(4, (HID_USAGE_PAGE_ORDINAL << 16)|5), + USAGE(4, (HID_USAGE_PAGE_ORDINAL << 16)|6), + LOGICAL_MINIMUM(1, 0), + LOGICAL_MAXIMUM(1, 1), + PHYSICAL_MINIMUM(1, 0), + PHYSICAL_MAXIMUM(1, 1), + REPORT_SIZE(1, 2), + REPORT_COUNT(1, 6), + OUTPUT(1, Data|Var|Abs), + END_COLLECTION, + REPORT_SIZE(1, 2), + REPORT_COUNT(1, 2), + OUTPUT(1, Cnst|Var|Abs), + + USAGE(1, PID_USAGE_CP_OFFSET), + USAGE(1, PID_USAGE_POSITIVE_COEFFICIENT), + USAGE(1, PID_USAGE_NEGATIVE_COEFFICIENT), + LOGICAL_MINIMUM(1, 0x80), + LOGICAL_MAXIMUM(1, 0x7f), + PHYSICAL_MINIMUM(2, 0xd8f0), + PHYSICAL_MAXIMUM(2, 0x2710), + REPORT_SIZE(1, 8), + REPORT_COUNT(1, 3), + OUTPUT(1, Data|Var|Abs), + + USAGE(1, PID_USAGE_POSITIVE_SATURATION), + USAGE(1, PID_USAGE_NEGATIVE_SATURATION), + USAGE(1, PID_USAGE_DEAD_BAND), + LOGICAL_MINIMUM(1, 0), + LOGICAL_MAXIMUM(2, 0x00ff), + PHYSICAL_MINIMUM(1, 0), + PHYSICAL_MAXIMUM(2, 0x2710), + REPORT_SIZE(1, 8), + REPORT_COUNT(1, 3), + OUTPUT(1, Data|Var|Abs), + END_COLLECTION, + + + USAGE(1, PID_USAGE_DEVICE_GAIN_REPORT), + COLLECTION(1, Logical), + REPORT_ID(1, 8), + + USAGE(1, PID_USAGE_DEVICE_GAIN), + LOGICAL_MINIMUM(1, 0), + LOGICAL_MAXIMUM(2, 0x00ff), + PHYSICAL_MINIMUM(1, 0), + PHYSICAL_MAXIMUM(2, 0x2710), + REPORT_SIZE(1, 8), + REPORT_COUNT(1, 1), + OUTPUT(1, Data|Var|Abs), + END_COLLECTION, + + USAGE(1, PID_USAGE_SET_CONSTANT_FORCE_REPORT), + COLLECTION(1, Logical), + REPORT_ID(1, 9), + + USAGE(1, PID_USAGE_MAGNITUDE), + LOGICAL_MINIMUM(2, 0xd8f0), + LOGICAL_MAXIMUM(2, 0x2710), + PHYSICAL_MINIMUM(2, 0xd8f0), + PHYSICAL_MAXIMUM(2, 0x2710), + REPORT_SIZE(1, 16), + REPORT_COUNT(1, 1), + OUTPUT(1, Data|Var|Abs), + END_COLLECTION, + + END_COLLECTION, + }; + C_ASSERT(sizeof(report_descriptor) < MAX_HID_DESCRIPTOR_LEN); +#include "pop_hid_macros.h" + + struct hid_device_desc desc = + { + .use_report_id = TRUE, + .caps = + { + .InputReportByteLength = 8, + .OutputReportByteLength = 15, + }, + .attributes = default_attributes, + }; + const DIDEVCAPS expect_caps = + { + .dwSize = sizeof(DIDEVCAPS), + .dwFlags = DIDC_FORCEFEEDBACK | DIDC_ATTACHED | DIDC_EMULATED | DIDC_STARTDELAY | + DIDC_FFFADE | DIDC_FFATTACK | DIDC_DEADBAND | DIDC_SATURATION, + .dwDevType = DIDEVTYPE_HID | (DI8DEVTYPE1STPERSON_LIMITED << 8) | DI8DEVTYPE_1STPERSON, + .dwAxes = 6, + .dwButtons = 2, + .dwFFSamplePeriod = 1000000, + .dwFFMinTimeResolution = 1000000, + .dwHardwareRevision = 1, + .dwFFDriverVersion = 1, + }; + const DIDEVICEINSTANCEW expect_devinst = + { + .dwSize = sizeof(DIDEVICEINSTANCEW), + .guidInstance = expect_guid_product, + .guidProduct = expect_guid_product, + .dwDevType = DIDEVTYPE_HID | (DI8DEVTYPE1STPERSON_LIMITED << 8) | DI8DEVTYPE_1STPERSON, + .tszInstanceName = L"Wine Test", + .tszProductName = L"Wine Test", + .guidFFDriver = IID_IDirectInputPIDDriver, + .wUsagePage = HID_USAGE_PAGE_GENERIC, + .wUsage = HID_USAGE_GENERIC_JOYSTICK, + }; + DIPROPDWORD prop_dword = + { + .diph = + { + .dwSize = sizeof(DIPROPDWORD), + .dwHeaderSize = sizeof(DIPROPHEADER), + .dwHow = DIPH_DEVICE, + }, + }; + DIPROPGUIDANDPATH prop_guid_path = + { + .diph = + { + .dwSize = sizeof(DIPROPGUIDANDPATH), + .dwHeaderSize = sizeof(DIPROPHEADER), + .dwHow = DIPH_DEVICE, + }, + }; + struct hid_expect expect_acquire[] = + { + /* device control */ + { + .code = IOCTL_HID_WRITE_REPORT, + .report_id = 1, + .report_len = 2, + .report_buf = {1, 0x01}, + }, + /* device gain */ + { + .code = IOCTL_HID_WRITE_REPORT, + .report_id = 8, + .report_len = 2, + .report_buf = {8, 0xff}, + }, + }; + struct hid_expect expect_reset[] = + { + /* device control */ + { + .code = IOCTL_HID_WRITE_REPORT, + .report_id = 1, + .report_len = 2, + .report_buf = {1, 0x01}, + }, + }; + DIDEVICEINSTANCEW devinst = {.dwSize = sizeof(DIDEVICEINSTANCEW)}; + IDirectInputDevice8W *device = NULL; + DIDEVCAPS caps = {0}; + DWORD version = 0x800; + HANDLE file; + HRESULT hr; + HWND hwnd; + ULONG ref; + + winetest_push_context( "%#lx", version ); + cleanup_registry_keys(); + + desc.report_descriptor_len = sizeof(report_descriptor); + memcpy( desc.report_descriptor_buf, report_descriptor, sizeof(report_descriptor) ); + fill_context( desc.context, ARRAY_SIZE(desc.context) ); + + if (!hid_device_start( &desc, 1 )) goto done; + if (FAILED(hr = dinput_test_create_device( version, &devinst, &device ))) goto done; + + check_dinput_devices( version, &devinst ); + + hr = IDirectInputDevice8_GetDeviceInfo( device, &devinst ); + ok( hr == DI_OK, "GetDeviceInfo returned %#lx\n", hr ); + check_member( devinst, expect_devinst, "%lu", dwSize ); + check_member_guid( devinst, expect_devinst, guidProduct ); + todo_wine check_member( devinst, expect_devinst, "%#lx", dwDevType ); + check_member_wstr( devinst, expect_devinst, tszInstanceName ); + check_member_wstr( devinst, expect_devinst, tszProductName ); + check_member_guid( devinst, expect_devinst, guidFFDriver ); + check_member( devinst, expect_devinst, "%04x", wUsagePage ); + check_member( devinst, expect_devinst, "%04x", wUsage ); + + caps.dwSize = sizeof(DIDEVCAPS); + hr = IDirectInputDevice8_GetCapabilities( device, &caps ); + ok( hr == DI_OK, "GetCapabilities returned %#lx\n", hr ); + check_member( caps, expect_caps, "%lu", dwSize ); + check_member( caps, expect_caps, "%#lx", dwFlags ); + todo_wine check_member( caps, expect_caps, "%#lx", dwDevType ); + check_member( caps, expect_caps, "%lu", dwAxes ); + check_member( caps, expect_caps, "%lu", dwButtons ); + check_member( caps, expect_caps, "%lu", dwPOVs ); + check_member( caps, expect_caps, "%lu", dwFFSamplePeriod ); + check_member( caps, expect_caps, "%lu", dwFFMinTimeResolution ); + check_member( caps, expect_caps, "%lu", dwFirmwareRevision ); + check_member( caps, expect_caps, "%lu", dwHardwareRevision ); + check_member( caps, expect_caps, "%lu", dwFFDriverVersion ); + + prop_dword.dwData = 0xdeadbeef; + hr = IDirectInputDevice8_GetProperty( device, DIPROP_FFGAIN, &prop_dword.diph ); + ok( hr == DI_OK, "GetProperty DIPROP_FFGAIN returned %#lx\n", hr ); + ok( prop_dword.dwData == 10000, "got %lu expected %u\n", prop_dword.dwData, 10000 ); + + hr = IDirectInputDevice8_GetProperty( device, DIPROP_FFLOAD, &prop_dword.diph ); + ok( hr == DIERR_NOTEXCLUSIVEACQUIRED, "GetProperty DIPROP_FFLOAD returned %#lx\n", hr ); + + hr = IDirectInputDevice8_SetDataFormat( device, &c_dfDIJoystick2 ); + ok( hr == DI_OK, "SetDataFormat returned: %#lx\n", hr ); + + hr = IDirectInputDevice8_GetProperty( device, DIPROP_GUIDANDPATH, &prop_guid_path.diph ); + ok( hr == DI_OK, "GetProperty DIPROP_GUIDANDPATH returned %#lx\n", hr ); + + file = CreateFileW( prop_guid_path.wszPath, FILE_READ_ACCESS | FILE_WRITE_ACCESS, + FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, + FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING, NULL ); + ok( file != INVALID_HANDLE_VALUE, "got error %lu\n", GetLastError() ); + + hwnd = create_foreground_window( FALSE ); + + hr = IDirectInputDevice8_SetCooperativeLevel( device, hwnd, DISCL_BACKGROUND | DISCL_EXCLUSIVE ); + ok( hr == DI_OK, "SetCooperativeLevel returned: %#lx\n", hr ); + set_hid_expect( file, expect_acquire, sizeof(expect_acquire) ); + hr = IDirectInputDevice8_Acquire( device ); + ok( hr == DI_OK, "Acquire returned: %#lx\n", hr ); + wait_hid_expect( file, 100 ); + + test_condition_effect_six_axes( device, file ); + + set_hid_expect( file, expect_reset, sizeof(struct hid_expect) ); + hr = IDirectInputDevice8_Unacquire( device ); + ok( hr == DI_OK, "Unacquire returned: %#lx\n", hr ); + set_hid_expect( file, NULL, 0 ); + + ref = IDirectInputDevice8_Release( device ); + ok( ref == 0, "Release returned %ld\n", ref ); + + DestroyWindow( hwnd ); + CloseHandle( file ); + +done: + hid_device_stop( &desc, 1 ); + cleanup_registry_keys(); + winetest_pop_context(); + return device != NULL; +} + static void test_device_managed_effect(void) { #include "psh_hid_macros.h" @@ -7225,7 +7969,8 @@ START_TEST( force_feedback ) test_force_feedback_joystick( 0x500 ); test_force_feedback_joystick( 0x700 ); test_device_managed_effect(); - test_windows_gaming_input(); + test_force_feedback_six_axes(); + test_windows_gaming_input(); /* keep it last, seems to mess with other tests */ } done: From cc0354a23823cd3334f2d406e0321ed27e8fcbf8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Paku=C5=82a?= Date: Tue, 25 Feb 2025 10:43:37 +0100 Subject: [PATCH 036/454] include: Define the max number of supported HID PID axes. (cherry picked from commit e986b80a53a4a7252bfbbf0080d3aa2db48afe17) CW-Bug-Id: #25366 --- dlls/dinput/joystick_hid.c | 18 +++++++++--------- dlls/winebus.sys/hid.c | 2 +- dlls/winebus.sys/unix_private.h | 7 ++++--- include/wine/hid.h | 2 ++ 4 files changed, 16 insertions(+), 13 deletions(-) diff --git a/dlls/dinput/joystick_hid.c b/dlls/dinput/joystick_hid.c index 97a333b8c31d..f225e7578ca0 100644 --- a/dlls/dinput/joystick_hid.c +++ b/dlls/dinput/joystick_hid.c @@ -72,8 +72,8 @@ struct pid_effect_update UINT axis_count; UINT direction_coll; UINT direction_count; - struct hid_value_caps *axis_caps[6]; - struct hid_value_caps *direction_caps[6]; + struct hid_value_caps *axis_caps[MAX_PID_AXES]; + struct hid_value_caps *direction_caps[MAX_PID_AXES]; struct hid_value_caps *duration_caps; struct hid_value_caps *gain_caps; struct hid_value_caps *sample_period_caps; @@ -225,11 +225,11 @@ struct hid_joystick_effect struct list entry; struct hid_joystick *joystick; - DWORD axes[6]; - LONG directions[6]; + DWORD axes[MAX_PID_AXES]; + LONG directions[MAX_PID_AXES]; DICONSTANTFORCE constant_force; DIRAMPFORCE ramp_force; - DICONDITION condition[6]; + DICONDITION condition[MAX_PID_AXES]; DIENVELOPE envelope; DIPERIODIC periodic; DIEFFECT params; @@ -1973,7 +1973,7 @@ static BOOL init_pid_caps( struct dinput_device *device, UINT index, struct hid_ if (instance->wCollectionNumber == effect_update->axes_coll) { SET_REPORT_ID( effect_update ); - if (effect_update->axis_count >= 6) FIXME( "more than 6 PID axes detected\n" ); + if (effect_update->axis_count >= MAX_PID_AXES) FIXME( "more than %d PID axes detected\n", MAX_PID_AXES ); else effect_update->axis_caps[effect_update->axis_count] = caps; effect_update->axis_count++; } @@ -1982,7 +1982,7 @@ static BOOL init_pid_caps( struct dinput_device *device, UINT index, struct hid_ SET_REPORT_ID( effect_update ); caps->physical_min = 0; caps->physical_max = 35900; - if (effect_update->direction_count >= 6) FIXME( "more than 6 PID directions detected\n" ); + if (effect_update->direction_count >= MAX_PID_AXES) FIXME( "more than %d PID directions detected\n", MAX_PID_AXES ); else effect_update->direction_caps[effect_update->direction_count] = caps; effect_update->direction_count++; } @@ -2503,7 +2503,7 @@ static void convert_directions_from_spherical( const DIEFFECT *in, DIEFFECT *out static void convert_directions( const DIEFFECT *in, DIEFFECT *out ) { DWORD direction_flags = DIEFF_CARTESIAN | DIEFF_POLAR | DIEFF_SPHERICAL; - LONG directions[6] = {0}; + LONG directions[MAX_PID_AXES] = {0}; DIEFFECT spherical = {.rglDirection = directions}; switch (in->dwFlags & direction_flags) @@ -3023,7 +3023,7 @@ static HRESULT WINAPI hid_joystick_effect_Download( IDirectInputEffect *iface ) ULONG report_len = impl->joystick->caps.OutputReportByteLength; HANDLE device = impl->joystick->device; struct hid_value_caps *caps; - LONG directions[4] = {0}; + LONG directions[MAX_PID_AXES] = {0}; DWORD i, tmp, count; DIEFFECT spherical; NTSTATUS status; diff --git a/dlls/winebus.sys/hid.c b/dlls/winebus.sys/hid.c index 78efe6116ceb..0e101c2f70ae 100644 --- a/dlls/winebus.sys/hid.c +++ b/dlls/winebus.sys/hid.c @@ -503,7 +503,7 @@ struct pid_effect_update BYTE gain_percent; BYTE trigger_button; BYTE enable_bits; - UINT16 direction[2]; + UINT16 direction[MAX_PID_AXES]; }; struct pid_set_periodic diff --git a/dlls/winebus.sys/unix_private.h b/dlls/winebus.sys/unix_private.h index 797ec7de7404..b8c0a0cb8957 100644 --- a/dlls/winebus.sys/unix_private.h +++ b/dlls/winebus.sys/unix_private.h @@ -29,6 +29,7 @@ #include "unixlib.h" #include "wine/list.h" +#include "wine/hid.h" struct effect_periodic { @@ -75,9 +76,9 @@ struct effect_params UINT16 sample_period; UINT16 start_delay; BYTE trigger_button; - BOOL axis_enabled[2]; + BOOL axis_enabled[MAX_PID_AXES]; BOOL direction_enabled; - UINT16 direction[2]; + UINT16 direction[MAX_PID_AXES]; BYTE gain_percent; BYTE condition_count; /* only for periodic, constant or ramp forces */ @@ -85,7 +86,7 @@ struct effect_params union { struct effect_periodic periodic; - struct effect_condition condition[2]; + struct effect_condition condition[MAX_PID_AXES]; struct effect_constant_force constant_force; struct effect_ramp_force ramp_force; }; diff --git a/include/wine/hid.h b/include/wine/hid.h index 8200ca9beecc..cb31bd6e5a5b 100644 --- a/include/wine/hid.h +++ b/include/wine/hid.h @@ -239,6 +239,8 @@ struct hid_preparsed_data #define PID_USAGE_CREATE_NEW_EFFECT_REPORT ((USAGE) 0xab) #define PID_USAGE_RAM_POOL_AVAILABLE ((USAGE) 0xac) +#define MAX_PID_AXES 6 + #define IOCTL_HID_GET_WINE_RAWINPUT_HANDLE HID_BUFFER_CTL_CODE(300) #endif /* __WINE_PARSE_H */ From fc4ea98ed675c9c2b28b2f0d438832c8f51d8adb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Paku=C5=82a?= Date: Tue, 25 Feb 2025 10:56:58 +0100 Subject: [PATCH 037/454] winebus: Support creation of dynamic number of PID axes. Allow the PID descriptor building function to create up to MAX_PID_AXES FFB-enabled axes. This will allow better effect handling and support for devices with more than two FFB axis. Devices with only one axis, can be actually presented as such. (cherry picked from commit 613997b50ab9dfa4e650ea7c05eeba5b5a62bc0d) CW-Bug-Id: #25366 --- dlls/winebus.sys/bus_sdl.c | 2 +- dlls/winebus.sys/bus_udev.c | 2 +- dlls/winebus.sys/hid.c | 139 ++++++++++++++++++++++---------- dlls/winebus.sys/unix_private.h | 3 +- 4 files changed, 101 insertions(+), 45 deletions(-) diff --git a/dlls/winebus.sys/bus_sdl.c b/dlls/winebus.sys/bus_sdl.c index aa28d2fccfd7..746ef305661e 100644 --- a/dlls/winebus.sys/bus_sdl.c +++ b/dlls/winebus.sys/bus_sdl.c @@ -240,7 +240,7 @@ static BOOL descriptor_add_haptic(struct sdl_device *impl, BOOL force) if (force || (impl->effect_support & SDL_HAPTIC_CONSTANT)) usages[count++] = PID_USAGE_ET_CONSTANT_FORCE; if (force || (impl->effect_support & SDL_HAPTIC_RAMP)) usages[count++] = PID_USAGE_ET_RAMP; - if (!hid_device_add_physical(&impl->unix_device, usages, count)) + if (!hid_device_add_physical(&impl->unix_device, usages, count, 2)) return FALSE; } diff --git a/dlls/winebus.sys/bus_udev.c b/dlls/winebus.sys/bus_udev.c index 25bae061e040..aa44be7c24b5 100644 --- a/dlls/winebus.sys/bus_udev.c +++ b/dlls/winebus.sys/bus_udev.c @@ -990,7 +990,7 @@ static NTSTATUS build_report_descriptor(struct unix_device *iface, struct udev_d if (test_bit(ffbits, FF_CONSTANT)) usages[count++] = PID_USAGE_ET_CONSTANT_FORCE; if (test_bit(ffbits, FF_RAMP)) usages[count++] = PID_USAGE_ET_RAMP; - if (!hid_device_add_physical(iface, usages, count)) + if (!hid_device_add_physical(iface, usages, count, 2)) return STATUS_NO_MEMORY; } diff --git a/dlls/winebus.sys/hid.c b/dlls/winebus.sys/hid.c index 0e101c2f70ae..7bb6617501d4 100644 --- a/dlls/winebus.sys/hid.c +++ b/dlls/winebus.sys/hid.c @@ -556,6 +556,80 @@ struct pid_effect_state }; #include "poppack.h" +static BOOL hid_descriptor_add_axes_enable(struct unix_device *iface, USHORT axes_count) +{ + struct hid_report_descriptor *desc = &iface->hid_report_descriptor; + const BYTE header[] = + { + USAGE(1, PID_USAGE_AXES_ENABLE), + COLLECTION(1, Logical), + }; + const BYTE footer[] = + { + LOGICAL_MINIMUM(1, 0), + LOGICAL_MAXIMUM(1, 1), + REPORT_SIZE(1, 1), + REPORT_COUNT(1, axes_count), + OUTPUT(1, Data|Var|Abs), + END_COLLECTION, + USAGE(1, PID_USAGE_DIRECTION_ENABLE), + REPORT_COUNT(1, 1), + OUTPUT(1, Data|Var|Abs), + REPORT_COUNT(1, (7 - axes_count) % 8), /* byte pad */ + OUTPUT(1, Cnst|Var|Abs), + }; + UINT i; + + if (!hid_report_descriptor_append(desc, header, sizeof(header))) + return FALSE; + + for (i = 0; i < axes_count; i++) + { + USAGE_AND_PAGE usage = iface->hid_device_state.abs_axis_usages[i]; + const BYTE template[] = { USAGE(4, ((UINT)usage.UsagePage << 16) | usage.Usage) }; + if (!hid_report_descriptor_append(desc, template, sizeof(template))) + return FALSE; + } + + return hid_report_descriptor_append(desc, footer, sizeof(footer)); +} + +static BOOL hid_descriptor_add_directions(struct unix_device *iface, USHORT axes_count) +{ + struct hid_report_descriptor *desc = &iface->hid_report_descriptor; + const BYTE header[] = + { + USAGE(1, PID_USAGE_DIRECTION), + COLLECTION(1, Logical), + }; + const BYTE footer[] = + { + UNIT(1, 0x14), /* Eng Rot:Angular Pos */ + UNIT_EXPONENT(1, -2), + LOGICAL_MINIMUM(1, 0), + LOGICAL_MAXIMUM(4, 35900), + REPORT_SIZE(1, 16), + REPORT_COUNT(1, axes_count), + OUTPUT(1, Data|Var|Abs), + END_COLLECTION, + UNIT_EXPONENT(1, 0), + UNIT(1, 0), /* None */ + }; + UINT i; + + if (!hid_report_descriptor_append(desc, header, sizeof(header))) + return FALSE; + + for (i = 0; i < axes_count; ++i) + { + const BYTE template[] = { USAGE(4, (HID_USAGE_PAGE_ORDINAL << 16) | (i + 1)) }; + if (!hid_report_descriptor_append(desc, template, sizeof(template))) + return FALSE; + } + + return hid_report_descriptor_append(desc, footer, sizeof(footer)); +} + static BOOL hid_descriptor_add_set_periodic(struct unix_device *iface) { struct hid_report_descriptor *desc = &iface->hid_report_descriptor; @@ -672,7 +746,7 @@ static BOOL hid_descriptor_add_set_envelope(struct unix_device *iface) return hid_report_descriptor_append(desc, template, sizeof(template)); } -static BOOL hid_descriptor_add_set_condition(struct unix_device *iface) +static BOOL hid_descriptor_add_set_condition(struct unix_device *iface, USHORT axes_count) { struct hid_report_descriptor *desc = &iface->hid_report_descriptor; const BYTE report_id = ++desc->next_report_id[HidP_Output]; @@ -692,7 +766,7 @@ static BOOL hid_descriptor_add_set_condition(struct unix_device *iface) USAGE(1, PID_USAGE_PARAMETER_BLOCK_OFFSET), LOGICAL_MINIMUM(1, 0x00), - LOGICAL_MAXIMUM(1, 0x01), + LOGICAL_MAXIMUM(1, axes_count - 1), REPORT_SIZE(1, 8), REPORT_COUNT(1, 1), OUTPUT(1, Data|Var|Abs), @@ -800,7 +874,7 @@ static BOOL hid_descriptor_add_set_ramp_force(struct unix_device *iface) return hid_report_descriptor_append(desc, template, sizeof(template)); } -BOOL hid_device_add_physical(struct unix_device *iface, USAGE *usages, USHORT count) +BOOL hid_device_add_physical(struct unix_device *iface, USAGE *usages, USHORT count, USHORT axes_count) { struct hid_report_descriptor *desc = &iface->hid_report_descriptor; const BYTE device_control_report = ++desc->next_report_id[HidP_Output]; @@ -901,7 +975,7 @@ BOOL hid_device_add_physical(struct unix_device *iface, USAGE *usages, USHORT co USAGE(1, PID_USAGE_EFFECT_TYPE), COLLECTION(1, Logical), }; - const BYTE effect_update_footer[] = + const BYTE effect_update_template[] = { LOGICAL_MINIMUM(1, 1), LOGICAL_MAXIMUM(1, count), @@ -936,37 +1010,9 @@ BOOL hid_device_add_physical(struct unix_device *iface, USAGE *usages, USHORT co REPORT_SIZE(1, 8), REPORT_COUNT(1, 1), OUTPUT(1, Data|Var|Abs|Null), - - USAGE(1, PID_USAGE_AXES_ENABLE), - COLLECTION(1, Logical), - USAGE(4, (state->abs_axis_usages[0].UsagePage<<16)|state->abs_axis_usages[0].Usage), - USAGE(4, (state->abs_axis_usages[1].UsagePage<<16)|state->abs_axis_usages[1].Usage), - LOGICAL_MINIMUM(1, 0), - LOGICAL_MAXIMUM(1, 1), - REPORT_SIZE(1, 1), - REPORT_COUNT(1, 2), - OUTPUT(1, Data|Var|Abs), - END_COLLECTION, - USAGE(1, PID_USAGE_DIRECTION_ENABLE), - REPORT_COUNT(1, 1), - OUTPUT(1, Data|Var|Abs), - REPORT_COUNT(1, 5), - OUTPUT(1, Cnst|Var|Abs), /* 5-bit pad */ - - USAGE(1, PID_USAGE_DIRECTION), - COLLECTION(1, Logical), - USAGE(4, (HID_USAGE_PAGE_ORDINAL<<16)|1), - USAGE(4, (HID_USAGE_PAGE_ORDINAL<<16)|2), - UNIT(1, 0x14), /* Eng Rot:Angular Pos */ - UNIT_EXPONENT(1, -2), - LOGICAL_MINIMUM(1, 0), - LOGICAL_MAXIMUM(4, 35900), - REPORT_SIZE(1, 16), - REPORT_COUNT(1, 2), - OUTPUT(1, Data|Var|Abs), - END_COLLECTION, - UNIT_EXPONENT(1, 0), - UNIT(1, 0), /* None */ + }; + const BYTE effect_update_footer[] = + { END_COLLECTION, }; @@ -1003,6 +1049,8 @@ BOOL hid_device_add_physical(struct unix_device *iface, USAGE *usages, USHORT co BOOL ramp_force = FALSE; ULONG i; + if (axes_count > MAX_PID_AXES) axes_count = MAX_PID_AXES; + if (!hid_report_descriptor_append(desc, device_control_header, sizeof(device_control_header))) return FALSE; for (i = 1; i < ARRAY_SIZE(pid_device_control_usages); ++i) @@ -1033,6 +1081,12 @@ BOOL hid_device_add_physical(struct unix_device *iface, USAGE *usages, USHORT co if (!hid_report_descriptor_append_usage(desc, usages[i])) return FALSE; } + if (!hid_report_descriptor_append(desc, effect_update_template, sizeof(effect_update_template))) + return FALSE; + if (!hid_descriptor_add_axes_enable(iface, axes_count)) + return FALSE; + if (!hid_descriptor_add_directions(iface, axes_count)) + return FALSE; if (!hid_report_descriptor_append(desc, effect_update_footer, sizeof(effect_update_footer))) return FALSE; @@ -1059,7 +1113,7 @@ BOOL hid_device_add_physical(struct unix_device *iface, USAGE *usages, USHORT co return FALSE; if (envelope && !hid_descriptor_add_set_envelope(iface)) return FALSE; - if (condition && !hid_descriptor_add_set_condition(iface)) + if (condition && !hid_descriptor_add_set_condition(iface, axes_count)) return FALSE; if (constant_force && !hid_descriptor_add_set_constant_force(iface)) return FALSE; @@ -1076,6 +1130,7 @@ BOOL hid_device_add_physical(struct unix_device *iface, USAGE *usages, USHORT co iface->hid_physical.device_gain_report = device_gain_report; iface->hid_physical.effect_control_report = effect_control_report; iface->hid_physical.effect_update_report = effect_update_report; + iface->hid_physical.axes_count = axes_count; effect_state->id = effect_state_report; effect_state->report_len = sizeof(struct pid_effect_state) + 1; @@ -1187,8 +1242,9 @@ static void hid_device_set_output_report(struct unix_device *iface, HID_XFER_PAC struct pid_effect_update *report = (struct pid_effect_update *)(packet->reportBuffer + 1); struct effect_params *params = iface->hid_physical.effect_params + report->index; USAGE effect_type; + ULONG i; - io->Information = sizeof(*report) + 1; + io->Information = offsetof(struct pid_effect_update, direction[physical->axes_count]) + 1; if (packet->reportBufferLen < io->Information) io->Status = STATUS_BUFFER_TOO_SMALL; else if (report->type_index >= ARRAY_SIZE(iface->hid_physical.effect_types)) @@ -1204,11 +1260,10 @@ static void hid_device_set_output_report(struct unix_device *iface, HID_XFER_PAC params->start_delay = report->start_delay; params->gain_percent = report->gain_percent; params->trigger_button = report->trigger_button == 0xff ? 0 : report->trigger_button; - params->axis_enabled[0] = (report->enable_bits & 1) != 0; - params->axis_enabled[1] = (report->enable_bits & 2) != 0; - params->direction_enabled = (report->enable_bits & 4) != 0; - params->direction[0] = report->direction[0]; - params->direction[1] = report->direction[1]; + + for (i = 0; i < physical->axes_count; ++i) params->direction[i] = report->direction[i]; + for (i = 0; i < physical->axes_count; ++i) params->axis_enabled[i] = !!(report->enable_bits & (1 << i)); + params->direction_enabled = (report->enable_bits & (1 << physical->axes_count)) != 0; io->Status = iface->hid_vtbl->physical_effect_update(iface, report->index, params); } diff --git a/dlls/winebus.sys/unix_private.h b/dlls/winebus.sys/unix_private.h index b8c0a0cb8957..34db3a7a02ee 100644 --- a/dlls/winebus.sys/unix_private.h +++ b/dlls/winebus.sys/unix_private.h @@ -184,6 +184,7 @@ struct hid_physical BYTE set_ramp_force_report; struct hid_effect_state effect_state; + USHORT axes_count; }; struct hid_device_state @@ -253,7 +254,7 @@ extern BOOL hid_device_add_axes(struct unix_device *iface, BYTE count, USAGE usa const USAGE *usages, BOOL rel, LONG min, LONG max); extern BOOL hid_device_add_haptics(struct unix_device *iface); -extern BOOL hid_device_add_physical(struct unix_device *iface, USAGE *usages, USHORT count); +extern BOOL hid_device_add_physical(struct unix_device *iface, USAGE *usages, USHORT count, USHORT axes_count); extern BOOL hid_device_set_abs_axis(struct unix_device *iface, ULONG index, LONG value); extern BOOL hid_device_set_rel_axis(struct unix_device *iface, ULONG index, LONG value); From 6d2ba04fca4c39db0d8283b74a8cf63f393a3ff5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Paku=C5=82a?= Date: Tue, 25 Feb 2025 11:27:29 +0100 Subject: [PATCH 038/454] winebus: Get the number of haptic axes from SDL. Wine will now create PID axes in the virtual descriptor based on the haptic axes found in the SDL joystick. With the recent SDL hint SDL_JOYSTICK_HAPTIC_AXES, it's possible to limit the exposed FFB axes to just one, even if device presents more. This, in turn fixes force feedback in Richard Burns Rally which includes all ffb axes on effect creation, but then updates this effec with only the steering axis. Changing cAxes and rgdwAxes after effect creation is forbidden and renders force feedback broken. This issue is not wine-specific as the same thing happens on Windows for all steering wheels which define more than one FFB axis. On windows, this can be worked around with registry hacks which remove DIDFT_FFACTUATOR and DIDOI_FFACTUATOR from second axis dwType and dwFlags. SDL_HapticNumAxes() was available since SDL 2.0 (cherry picked from commit 8e3acf8ea77eb36ebc053d059c14d7a0a177cbab) CW-Bug-Id: #25366 --- dlls/winebus.sys/bus_sdl.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/dlls/winebus.sys/bus_sdl.c b/dlls/winebus.sys/bus_sdl.c index 746ef305661e..b8e01331c5e1 100644 --- a/dlls/winebus.sys/bus_sdl.c +++ b/dlls/winebus.sys/bus_sdl.c @@ -96,6 +96,7 @@ MAKE_FUNCPTR(SDL_HapticClose); MAKE_FUNCPTR(SDL_HapticDestroyEffect); MAKE_FUNCPTR(SDL_HapticGetEffectStatus); MAKE_FUNCPTR(SDL_HapticNewEffect); +MAKE_FUNCPTR(SDL_HapticNumAxes); MAKE_FUNCPTR(SDL_HapticOpenFromJoystick); MAKE_FUNCPTR(SDL_HapticPause); MAKE_FUNCPTR(SDL_HapticQuery); @@ -228,6 +229,8 @@ static BOOL descriptor_add_haptic(struct sdl_device *impl, BOOL force) if ((impl->effect_support & EFFECT_SUPPORT_PHYSICAL)) { + int axes_count; + /* SDL_HAPTIC_SQUARE doesn't exist */ if (force || (impl->effect_support & SDL_HAPTIC_SINE)) usages[count++] = PID_USAGE_ET_SINE; if (force || (impl->effect_support & SDL_HAPTIC_TRIANGLE)) usages[count++] = PID_USAGE_ET_TRIANGLE; @@ -240,7 +243,8 @@ static BOOL descriptor_add_haptic(struct sdl_device *impl, BOOL force) if (force || (impl->effect_support & SDL_HAPTIC_CONSTANT)) usages[count++] = PID_USAGE_ET_CONSTANT_FORCE; if (force || (impl->effect_support & SDL_HAPTIC_RAMP)) usages[count++] = PID_USAGE_ET_RAMP; - if (!hid_device_add_physical(&impl->unix_device, usages, count, 2)) + if ((axes_count = pSDL_HapticNumAxes(impl->sdl_haptic)) < 0) axes_count = 2; + if (!hid_device_add_physical(&impl->unix_device, usages, count, axes_count)) return FALSE; } @@ -1177,6 +1181,7 @@ NTSTATUS sdl_bus_init(void *args) LOAD_FUNCPTR(SDL_HapticDestroyEffect); LOAD_FUNCPTR(SDL_HapticGetEffectStatus); LOAD_FUNCPTR(SDL_HapticNewEffect); + LOAD_FUNCPTR(SDL_HapticNumAxes); LOAD_FUNCPTR(SDL_HapticOpenFromJoystick); LOAD_FUNCPTR(SDL_HapticPause); LOAD_FUNCPTR(SDL_HapticQuery); From cde08477e78daf716dac497edace3d52f4ab3ff6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 16 May 2025 16:39:04 +0200 Subject: [PATCH 039/454] winebus: Skip device stop if it wasn't started. (cherry picked from commit 7d2b2052bba89ac148b975461ab7fc6dd0058754) CW-Bug-Id: #25366 --- dlls/winebus.sys/bus_udev.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/dlls/winebus.sys/bus_udev.c b/dlls/winebus.sys/bus_udev.c index aa44be7c24b5..7372d4fd6ab2 100644 --- a/dlls/winebus.sys/bus_udev.c +++ b/dlls/winebus.sys/bus_udev.c @@ -106,6 +106,7 @@ struct base_device { struct unix_device unix_device; void (*read_report)(struct unix_device *iface); + BOOL started; struct udev_device *udev_device; char devnode[MAX_PATH]; @@ -217,6 +218,8 @@ static void stop_polling_device(struct unix_device *iface) int i; if (impl->device_fd == -1) return; /* already removed */ + if (!impl->started) return; /* not started */ + impl->started = FALSE; for (i = 2; i < poll_count; ++i) if (poll_fds[i].fd == impl->device_fd) break; @@ -248,6 +251,7 @@ static void start_polling_device(struct unix_device *iface) poll_count++; write(deviceloop_control[1], "u", 1); + impl->started = TRUE; } } From 14edff4274528c0a94ac119aa90d40e9172a781d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 31 Jul 2025 11:45:44 +0200 Subject: [PATCH 040/454] winebus.sys: Prefer hidraw for all Virpil (VID 3344) devices. Original patch from Ty (Chaos). (cherry picked from commit c6d42ce04609139b0b7a1e11dadd87ebfaf78ca9) CW-Bug-Id: #25366 --- dlls/winebus.sys/main.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/dlls/winebus.sys/main.c b/dlls/winebus.sys/main.c index 2fda3e12cafb..6f9a73ce1515 100644 --- a/dlls/winebus.sys/main.c +++ b/dlls/winebus.sys/main.c @@ -558,13 +558,10 @@ static BOOL is_hidraw_enabled(WORD vid, WORD pid, const USAGE_AND_PAGE *usages, if (pid == 0x0127) prefer_hidraw = TRUE; /* VKB-Sim Space Gunfighter L */ break; case 0x3344: - /* comes with 31 buttons in the default configuration, or 128 max */ - if ((buttons == 31) || (buttons == 128)) prefer_hidraw = TRUE; - /* users may have configured button limits, usually 32/50/64 */ - if ((buttons == 32) || (buttons == 50) || (buttons == 64)) prefer_hidraw = TRUE; - /* if customized, arbitrary amount of buttons may be shown, decide by PID */ - if (pid == 0x412f) prefer_hidraw = TRUE; /* Virpil Constellation ALPHA-R */ - if (pid == 0x812c) prefer_hidraw = TRUE; /* Virpil Constellation ALPHA-L */ + /* all VPC devices require hidraw, have variable numbers of axis/buttons, & in many cases + * have functionally random PID. due to this, the only safe way to grab all VPC devices is + * a catch-all on VID and exclude any hypothetical future device that wants hidraw=false */ + prefer_hidraw = TRUE; break; case 0x03eb: /* users may have configured button limits, usually 32/50/64 */ From 38af2d59db743a2d59b7a4722b7918becf73fd95 Mon Sep 17 00:00:00 2001 From: Tim Clem Date: Mon, 4 Aug 2025 09:12:42 -0700 Subject: [PATCH 041/454] winebus: Quiet a log message about ignored HID devices. (cherry picked from commit cbb4e29b0a022847bd5fb8e9b981af46dc05e6c1) CW-Bug-Id: #25366 --- dlls/winebus.sys/bus_iohid.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/winebus.sys/bus_iohid.c b/dlls/winebus.sys/bus_iohid.c index 67276a26c489..648d55dadb86 100644 --- a/dlls/winebus.sys/bus_iohid.c +++ b/dlls/winebus.sys/bus_iohid.c @@ -297,7 +297,7 @@ static void handle_DeviceMatchingCallback(void *context, IOReturn result, void * * opening keyboards, mice, or the Touch Bar on older MacBooks triggers * a permissions dialog for input monitoring. */ - ERR("Ignoring HID device %p (vid %04x, pid %04x): not a joystick or gamepad\n", IOHIDDevice, desc.vid, desc.pid); + WARN("Ignoring HID device %p (vid %04x, pid %04x): not a joystick or gamepad\n", IOHIDDevice, desc.vid, desc.pid); return; } From 513c6eaffe6e9442251f22882bd3fa1fcbbaa983 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 10 Jun 2025 19:19:24 +0200 Subject: [PATCH 042/454] winebus: Support per-device/vendor hidraw registry option. (cherry picked from commit a19c57fe973d51ac19253190a72116bcdeb173d3) CW-Bug-Id: #25366 --- dlls/winebus.sys/main.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dlls/winebus.sys/main.c b/dlls/winebus.sys/main.c index 6f9a73ce1515..2e3d35e59473 100644 --- a/dlls/winebus.sys/main.c +++ b/dlls/winebus.sys/main.c @@ -436,8 +436,8 @@ static DWORD check_bus_option(const WCHAR *option, DWORD default_value) UNICODE_STRING str; DWORD size; + /* @@ Wine registry key: HKLM\System\CurrentControlSet\Services\WineBus */ RtlInitUnicodeString(&str, option); - if (NtQueryValueKey(driver_key, &str, KeyValuePartialInformation, info, sizeof(buffer), &size) == STATUS_SUCCESS) { if (info->Type == REG_DWORD) return *(DWORD *)info->Data; @@ -1118,6 +1118,7 @@ static void load_device_options(void) HANDLE key, subkey; NTSTATUS status; + /* @@ Wine registry key: HKLM\System\CurrentControlSet\Services\WineBus\Devices */ InitializeObjectAttributes(&attr, &path, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, driver_key, NULL); status = NtOpenKey(&key, KEY_ALL_ACCESS, &attr); if (status) return; @@ -1145,6 +1146,7 @@ static void load_device_options(void) if (status == STATUS_NO_MORE_ENTRIES) break; idx++; + /* @@ Wine registry key: HKLM\System\CurrentControlSet\Services\WineBus\Devices\ */ name_str.Buffer = name->Name; name_str.Length = name->NameLength; InitializeObjectAttributes(&attr, &name_str, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, 0, NULL); From b246b503c91a669a3c654d3b02510a0cf54b7b82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Paku=C5=82a?= Date: Sun, 20 Jul 2025 18:56:24 +0200 Subject: [PATCH 043/454] winebus: Do not touch autocenter on device init and device reset. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit FFB Autocenter introduced in https://gitlab.winehq.org/wine/wine/-/merge_requests/4911 had one major misunderstanding. The USB PID standard doesn't actually define any explicit way to autocenter a device. One could of course use the spring effect with a deadzone of 0 and dead band of 0. This is what I'm actually working on for the Linux PID driver (spring + friction/damper). Some devices implement autocenter in firmware when they receive the DC Disable Actuators command. Very few, if not just one, implement this weird autocenter effect on slot 1. This is, from what I can gather, only implemented on the MS SideWinder joystick(s) and the Windows' USB PID driver is created around these devices. Windows PID driver is a bit out of spec, is quite permissive when it comes to fields missing in the descriptor (basically, only effect types and their effect type blocks are optional). Another thing it does is handling of this out-of-spec autocentering for their joysticks. Funnliy enough, the creator of the Linux PID driver based the initial code on testing with MS Sidewinder so it's autocentering is supported. This is where the autocentering mentioned in the MR comes from. It's not the directinput api that does it but the Windows PID driver. As such, autocentering on reset should be left to the drivers, not handeled by Wine. SDL lacks full reset support and Linux is even more barebones, whre it's not even possible to query the device state, effects etc (something I'm working on slowly). As such, when games send out RESET to prepare the device, the device starts autocentering for no good reason and the effect is not removed once other effect are uploaded and played which would be the case for MS sidewinder. tl;dr Set autocentering to 0 instead of max value when DISFFC_RESET is reveived to remove the unwanted autocenter behavior. Signed-off-by: Tomasz PakuÅ‚a (cherry picked from commit b0db6d50530023a19846f08f1c62f14803448f59) CW-Bug-Id: #25366 --- dlls/winebus.sys/bus_sdl.c | 2 -- dlls/winebus.sys/bus_udev.c | 20 -------------------- 2 files changed, 22 deletions(-) diff --git a/dlls/winebus.sys/bus_sdl.c b/dlls/winebus.sys/bus_sdl.c index b8e01331c5e1..ea92a000c380 100644 --- a/dlls/winebus.sys/bus_sdl.c +++ b/dlls/winebus.sys/bus_sdl.c @@ -574,7 +574,6 @@ static NTSTATUS sdl_device_physical_device_control(struct unix_device *iface, US return STATUS_SUCCESS; case PID_USAGE_DC_STOP_ALL_EFFECTS: pSDL_HapticStopAll(impl->sdl_haptic); - pSDL_HapticSetAutocenter(impl->sdl_haptic, 0); return STATUS_SUCCESS; case PID_USAGE_DC_DEVICE_RESET: pSDL_HapticStopAll(impl->sdl_haptic); @@ -584,7 +583,6 @@ static NTSTATUS sdl_device_physical_device_control(struct unix_device *iface, US pSDL_HapticDestroyEffect(impl->sdl_haptic, impl->effect_ids[i]); impl->effect_ids[i] = -1; } - pSDL_HapticSetAutocenter(impl->sdl_haptic, 100); return STATUS_SUCCESS; case PID_USAGE_DC_DEVICE_PAUSE: pSDL_HapticPause(impl->sdl_haptic); diff --git a/dlls/winebus.sys/bus_udev.c b/dlls/winebus.sys/bus_udev.c index 7372d4fd6ab2..01974067537e 100644 --- a/dlls/winebus.sys/bus_udev.c +++ b/dlls/winebus.sys/bus_udev.c @@ -1214,24 +1214,6 @@ static NTSTATUS lnxev_device_physical_effect_run(struct lnxev_device *impl, BYTE return STATUS_SUCCESS; } -static NTSTATUS lnxev_device_physical_device_set_autocenter(struct unix_device *iface, BYTE percent) -{ - struct lnxev_device *impl = lnxev_impl_from_unix_device(iface); - struct input_event ie = - { - .type = EV_FF, - .code = FF_AUTOCENTER, - .value = 0xffff * percent / 100, - }; - - TRACE("iface %p, percent %#x.\n", iface, percent); - - if (write(impl->base.device_fd, &ie, sizeof(ie)) == -1) - WARN("write failed %d %s\n", errno, strerror(errno)); - - return STATUS_SUCCESS; -} - static NTSTATUS lnxev_device_physical_device_control(struct unix_device *iface, USAGE control) { struct lnxev_device *impl = lnxev_impl_from_unix_device(iface); @@ -1275,7 +1257,6 @@ static NTSTATUS lnxev_device_physical_device_control(struct unix_device *iface, if (impl->effect_ids[i] < 0) continue; lnxev_device_physical_effect_run(impl, i, 0); } - lnxev_device_physical_device_set_autocenter(iface, 0); return STATUS_SUCCESS; case PID_USAGE_DC_DEVICE_RESET: for (i = 0; i < ARRAY_SIZE(impl->effect_ids); ++i) @@ -1285,7 +1266,6 @@ static NTSTATUS lnxev_device_physical_device_control(struct unix_device *iface, WARN("couldn't free effect, EVIOCRMFF ioctl failed: %d %s\n", errno, strerror(errno)); impl->effect_ids[i] = -1; } - lnxev_device_physical_device_set_autocenter(iface, 100); return STATUS_SUCCESS; case PID_USAGE_DC_DEVICE_PAUSE: WARN("device pause not supported\n"); From 05c0af8a2bc882b40decadaef392ced400b0e99d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 5 Aug 2025 11:04:00 +0200 Subject: [PATCH 044/454] winebus: Better separate hidraw from evdev in udev_add_device. (cherry picked from commit c8801c8fd8c4e8a5b0b41178247afdd9c2801e0f) CW-Bug-Id: #25366 CW-Bug-Id: #25325 --- dlls/winebus.sys/bus_udev.c | 216 ++++++++++++++++++------------------ 1 file changed, 105 insertions(+), 111 deletions(-) diff --git a/dlls/winebus.sys/bus_udev.c b/dlls/winebus.sys/bus_udev.c index 01974067537e..de29c199ec70 100644 --- a/dlls/winebus.sys/bus_udev.c +++ b/dlls/winebus.sys/bus_udev.c @@ -1547,15 +1547,110 @@ static void get_device_subsystem_info(struct udev_device *dev, const char *subsy } } -static void udev_add_device(struct udev_device *dev, int fd) +static NTSTATUS hidraw_device_create(struct udev_device *dev, int fd, const char *devnode, struct device_desc desc) { - struct device_desc desc = - { - .input = -1, - }; +#ifdef HAVE_LINUX_HIDRAW_H + static const WCHAR hidraw[] = {'h','i','d','r','a','w',0}; + static const WCHAR zeros[] = {'0','0','0','0',0}; struct base_device *impl; - const char *subsystem; - const char *devnode; + char buffer[MAX_PATH]; + + desc.is_hidraw = TRUE; + if (!desc.product[0] && ioctl(fd, HIDIOCGRAWNAME(sizeof(buffer) - 1), buffer) >= 0) + ntdll_umbstowcs(buffer, strlen(buffer) + 1, desc.product, ARRAY_SIZE(desc.product)); + + if (!desc.manufacturer[0]) memcpy(desc.manufacturer, hidraw, sizeof(hidraw)); + if (!desc.serialnumber[0]) memcpy(desc.serialnumber, zeros, sizeof(zeros)); + + if (!(impl = raw_device_create(&hidraw_device_vtbl, sizeof(struct hidraw_device)))) + return STATUS_NO_MEMORY; + list_add_tail(&device_list, &impl->unix_device.entry); + impl->read_report = hidraw_device_read_report; + impl->udev_device = udev_device_ref(dev); + strcpy(impl->devnode, devnode); + impl->device_fd = fd; + + TRACE("dev %p, node %s, desc %s.\n", dev, debugstr_a(devnode), debugstr_device_desc(&desc)); + bus_event_queue_device_created(&event_queue, &impl->unix_device, &desc); + return STATUS_SUCCESS; +#else + return STATUS_NOT_SUPPORTED; +#endif +} + +static NTSTATUS lnxev_device_create(struct udev_device *dev, int fd, const char *devnode, struct device_desc desc) +{ +#ifdef HAS_PROPER_INPUT_HEADER + static const WCHAR evdev[] = {'e','v','d','e','v',0}; + static const WCHAR zeros[] = {'0','0','0','0',0}; + struct input_id device_id = {0}; + struct lnxev_device *impl; + char buffer[MAX_PATH]; + + if (ioctl(fd, EVIOCGID, &device_id) < 0) + WARN("ioctl(EVIOCGID) failed: %d %s\n", errno, strerror(errno)); + else + { + desc.vid = device_id.vendor; + desc.pid = device_id.product; + desc.version = device_id.version; + } + + if (!desc.product[0] && ioctl(fd, EVIOCGNAME(sizeof(buffer) - 1), buffer) > 0) + { + if (sscanf(buffer, "Microsoft X-Box 360 pad %u", &desc.input) != 1) desc.input = -1; + ntdll_umbstowcs(buffer, strlen(buffer) + 1, desc.product, ARRAY_SIZE(desc.product)); + } + if (!desc.serialnumber[0] && ioctl(fd, EVIOCGUNIQ(sizeof(buffer)), buffer) >= 0) + ntdll_umbstowcs(buffer, strlen(buffer) + 1, desc.serialnumber, ARRAY_SIZE(desc.serialnumber)); + + if (!desc.manufacturer[0]) memcpy(desc.manufacturer, evdev, sizeof(evdev)); + if (!desc.serialnumber[0]) memcpy(desc.serialnumber, zeros, sizeof(zeros)); + + if (desc.vid == 0x28de && desc.pid == 0x11ff) + { + desc.is_gamepad = TRUE; + desc.version = 0; /* keep version fixed as 0 so we can hardcode it in ntdll rawinput pipe redirection */ + } + else if (is_xbox_gamepad(desc.vid, desc.pid)) + desc.is_gamepad = TRUE; + else + { + int axes=0, buttons=0; + axes = count_abs_axis(fd); + buttons = count_buttons(fd, NULL); + desc.is_gamepad = (axes == 6 && buttons >= 14); + } + + if (!(impl = hid_device_create(&lnxev_device_vtbl, sizeof(struct lnxev_device)))) + return STATUS_NO_MEMORY; + list_add_tail(&device_list, &impl->base.unix_device.entry); + impl->base.read_report = lnxev_device_read_report; + impl->base.udev_device = udev_device_ref(dev); + strcpy(impl->base.devnode, devnode); + impl->base.device_fd = fd; + + if (build_report_descriptor(&impl->base.unix_device, impl->base.udev_device)) + { + list_remove(&impl->base.unix_device.entry); + impl->base.unix_device.vtbl->destroy(&impl->base.unix_device); + } + else + { + TRACE("dev %p, node %s, desc %s.\n", dev, debugstr_a(devnode), debugstr_device_desc(&desc)); + bus_event_queue_device_created(&event_queue, &impl->base.unix_device, &desc); + } + + return STATUS_SUCCESS; +#else + return STATUS_NOT_SUPPORTED; +#endif +} + +static void udev_add_device(struct udev_device *dev, int fd) +{ + struct device_desc desc = { .input = -1 }; + const char *subsystem, *devnode; int bus = 0; if (!(devnode = udev_device_get_devnode(dev))) @@ -1584,62 +1679,8 @@ static void udev_add_device(struct udev_device *dev, int fd) return; } - if (!strcmp(subsystem, "hidraw")) - { - static const WCHAR hidraw[] = {'h','i','d','r','a','w',0}; -#ifdef HAVE_LINUX_HIDRAW_H - char product[MAX_PATH]; -#endif - - if (!desc.manufacturer[0]) memcpy(desc.manufacturer, hidraw, sizeof(hidraw)); - desc.is_hidraw = TRUE; - -#ifdef HAVE_LINUX_HIDRAW_H - if (!desc.product[0] && ioctl(fd, HIDIOCGRAWNAME(sizeof(product) - 1), product) >= 0) - ntdll_umbstowcs(product, strlen(product) + 1, desc.product, ARRAY_SIZE(desc.product)); -#endif - } -#ifdef HAS_PROPER_INPUT_HEADER - else if (!strcmp(subsystem, "input")) - { - static const WCHAR evdev[] = {'e','v','d','e','v',0}; - struct input_id device_id = {0}; - char buffer[MAX_PATH]; - - if (ioctl(fd, EVIOCGID, &device_id) < 0) - WARN("ioctl(EVIOCGID) failed: %d %s\n", errno, strerror(errno)); - else - { - desc.vid = device_id.vendor; - desc.pid = device_id.product; - desc.version = device_id.version; - } - - if (!desc.manufacturer[0]) memcpy(desc.manufacturer, evdev, sizeof(evdev)); - - if (!desc.product[0] && ioctl(fd, EVIOCGNAME(sizeof(buffer) - 1), buffer) > 0) - { - if (sscanf(buffer, "Microsoft X-Box 360 pad %u", &desc.input) != 1) desc.input = -1; - ntdll_umbstowcs(buffer, strlen(buffer) + 1, desc.product, ARRAY_SIZE(desc.product)); - } - - if (!desc.serialnumber[0] && ioctl(fd, EVIOCGUNIQ(sizeof(buffer)), buffer) >= 0) - ntdll_umbstowcs(buffer, strlen(buffer) + 1, desc.serialnumber, ARRAY_SIZE(desc.serialnumber)); - } -#endif - - if (!desc.serialnumber[0]) - { - static const WCHAR zeros[] = {'0','0','0','0',0}; - memcpy(desc.serialnumber, zeros, sizeof(zeros)); - } - if (desc.vid == 0x28de && desc.pid == 0x11ff && !strcmp(subsystem, "input")) - { TRACE("evdev %s: detected steam input virtual controller\n", debugstr_a(devnode)); - desc.is_gamepad = TRUE; - desc.version = 0; /* keep version fixed as 0 so we can hardcode it in ntdll rawinput pipe redirection */ - } else if (is_sdl_ignored_device(desc.vid, desc.pid)) { TRACE("evdev %s: ignoring %s, in SDL ignore list\n", debugstr_a(devnode), debugstr_device_desc(&desc)); @@ -1652,57 +1693,10 @@ static void udev_add_device(struct udev_device *dev, int fd) close(fd); return; } - else if (is_xbox_gamepad(desc.vid, desc.pid)) - { - desc.is_gamepad = TRUE; - } -#ifdef HAS_PROPER_INPUT_HEADER - else if (!strcmp(subsystem, "input")) - { - int axes=0, buttons=0; - axes = count_abs_axis(fd); - buttons = count_buttons(fd, NULL); - desc.is_gamepad = (axes == 6 && buttons >= 14); - } -#endif - - TRACE("dev %p, node %s, desc %s.\n", dev, debugstr_a(devnode), debugstr_device_desc(&desc)); - - if (strcmp(subsystem, "hidraw") == 0) - { - if (!(impl = raw_device_create(&hidraw_device_vtbl, sizeof(struct hidraw_device)))) return; - list_add_tail(&device_list, &impl->unix_device.entry); - impl->read_report = hidraw_device_read_report; - impl->udev_device = udev_device_ref(dev); - strcpy(impl->devnode, devnode); - impl->device_fd = fd; - - bus_event_queue_device_created(&event_queue, &impl->unix_device, &desc); - } -#ifdef HAS_PROPER_INPUT_HEADER - else if (strcmp(subsystem, "input") == 0) - { - if (!(impl = hid_device_create(&lnxev_device_vtbl, sizeof(struct lnxev_device)))) return; - list_add_tail(&device_list, &impl->unix_device.entry); - impl->read_report = lnxev_device_read_report; - impl->udev_device = udev_device_ref(dev); - strcpy(impl->devnode, devnode); - impl->device_fd = fd; - - if (build_report_descriptor(&impl->unix_device, impl->udev_device)) - { - list_remove(&impl->unix_device.entry); - impl->unix_device.vtbl->destroy(&impl->unix_device); - return; - } - bus_event_queue_device_created(&event_queue, &impl->unix_device, &desc); - } -#endif - else - { - close(fd); - } + if ((desc.is_hidraw = !strcmp(subsystem, "hidraw")) && !hidraw_device_create(dev, fd, devnode, desc)) return; + if (!strcmp(subsystem, "input") && !lnxev_device_create(dev, fd, devnode, desc)) return; + close(fd); } #ifdef HAVE_SYS_INOTIFY_H From 383637875fbd25ba7868e2426441dac020e8034c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 18 Aug 2025 17:42:45 +0200 Subject: [PATCH 045/454] winebus: Read evdev device info and feature bits on creation. (cherry picked from commit 28797e7515e6edb022d917662e0334d79282505c) CW-Bug-Id: #25366 CW-Bug-Id: #25325 --- dlls/winebus.sys/bus_udev.c | 166 ++++++++++++++++-------------------- 1 file changed, 75 insertions(+), 91 deletions(-) diff --git a/dlls/winebus.sys/bus_udev.c b/dlls/winebus.sys/bus_udev.c index de29c199ec70..72f887fc778d 100644 --- a/dlls/winebus.sys/bus_udev.c +++ b/dlls/winebus.sys/bus_udev.c @@ -130,7 +130,7 @@ static inline struct hidraw_device *hidraw_impl_from_unix_device(struct unix_dev #ifdef HAS_PROPER_INPUT_HEADER -static const USAGE_AND_PAGE absolute_usages[] = +static const USAGE_AND_PAGE absolute_usages[ABS_CNT] = { {.UsagePage = HID_USAGE_PAGE_GENERIC, .Usage = HID_USAGE_GENERIC_X}, /* ABS_X */ {.UsagePage = HID_USAGE_PAGE_GENERIC, .Usage = HID_USAGE_GENERIC_Y}, /* ABS_Y */ @@ -167,7 +167,7 @@ static const USAGE_AND_PAGE absolute_usages[] = {.UsagePage = HID_USAGE_PAGE_CONSUMER, .Usage = HID_USAGE_CONSUMER_VOLUME}, /* ABS_VOLUME */ }; -static const USAGE_AND_PAGE relative_usages[] = +static const USAGE_AND_PAGE relative_usages[REL_CNT] = { {.UsagePage = HID_USAGE_PAGE_GENERIC, .Usage = HID_USAGE_GENERIC_X}, /* REL_X */ {.UsagePage = HID_USAGE_PAGE_GENERIC, .Usage = HID_USAGE_GENERIC_Y}, /* REL_Y */ @@ -181,14 +181,25 @@ static const USAGE_AND_PAGE relative_usages[] = {0}, /* REL_MISC */ }; +struct lnxev_info +{ + struct input_id id; + char name[MAX_PATH]; + char uniq[MAX_PATH]; + BYTE abs[(ABS_CNT + 7) / 8]; + BYTE rel[(REL_CNT + 7) / 8]; + BYTE key[(KEY_CNT + 7) / 8]; + BYTE ff[(FF_CNT + 7) / 8]; +}; + struct lnxev_device { struct base_device base; - BYTE abs_map[ARRAY_SIZE(absolute_usages)]; - BYTE rel_map[ARRAY_SIZE(relative_usages)]; + BYTE abs_map[ABS_CNT]; + BYTE rel_map[REL_CNT]; BYTE hat_map[8]; - BYTE button_map[KEY_MAX]; + BYTE button_map[KEY_CNT]; int haptics_state; pthread_cond_t haptics_cond; @@ -825,7 +836,7 @@ static const USAGE_AND_PAGE *what_am_I(struct udev_device *dev, int fd) return &Unknown; } -static INT count_buttons(int device_fd, BYTE *map) +static INT count_buttons(const struct lnxev_info *info, BYTE *map) { static const UINT gamepad_buttons[] = { @@ -847,17 +858,10 @@ static INT count_buttons(int device_fd, BYTE *map) }; int i; int button_count = 0; - BYTE keybits[(KEY_MAX+7)/8]; - - if (ioctl(device_fd, EVIOCGBIT(EV_KEY, sizeof(keybits)), keybits) == -1) - { - WARN("ioctl(EVIOCGBIT, EV_KEY) failed: %d %s\n", errno, strerror(errno)); - return FALSE; - } for (i = 0; i < ARRAY_SIZE(gamepad_buttons); i++) { - if (test_bit(keybits, gamepad_buttons[i])) + if (test_bit(info->key, gamepad_buttons[i])) { if (map) map[gamepad_buttons[i]] = button_count; button_count++; @@ -866,7 +870,7 @@ static INT count_buttons(int device_fd, BYTE *map) for (i = BTN_DIGI; i < KEY_MAX; i++) { - if (test_bit(keybits, i)) + if (test_bit(info->key, i)) { if (map) map[i] = button_count; button_count++; @@ -875,52 +879,31 @@ static INT count_buttons(int device_fd, BYTE *map) return button_count; } -static INT count_abs_axis(int device_fd) +static INT count_abs_axis(const struct lnxev_info *info) { - BYTE absbits[(ABS_MAX+7)/8]; int abs_count = 0; int i; - if (ioctl(device_fd, EVIOCGBIT(EV_ABS, sizeof(absbits)), absbits) == -1) + for (i = 0; i < ABS_CNT; i++) { - WARN("ioctl(EVIOCGBIT, EV_ABS) failed: %d %s\n", errno, strerror(errno)); - return 0; + USAGE_AND_PAGE usage = absolute_usages[i]; + if (!usage.UsagePage || !usage.Usage) continue; + if (!test_bit(info->abs, i)) continue; + abs_count++; } - for (i = 0; i < ARRAY_SIZE(absolute_usages); i++) - if (test_bit(absbits, i)) abs_count++; return abs_count; } -static NTSTATUS build_report_descriptor(struct unix_device *iface, struct udev_device *dev) +static NTSTATUS build_report_descriptor(struct unix_device *iface, struct udev_device *dev, struct lnxev_info *info) { - struct input_absinfo abs_info[ARRAY_SIZE(absolute_usages)]; - BYTE absbits[(ABS_MAX+7)/8]; - BYTE relbits[(REL_MAX+7)/8]; - BYTE ffbits[(FF_MAX+7)/8]; - USAGE_AND_PAGE usage; + struct input_absinfo abs_info[ABS_CNT]; USHORT count = 0; USAGE usages[16]; INT i, button_count, abs_count, rel_count, hat_count; struct lnxev_device *impl = lnxev_impl_from_unix_device(iface); const USAGE_AND_PAGE device_usage = *what_am_I(dev, impl->base.device_fd); - if (ioctl(impl->base.device_fd, EVIOCGBIT(EV_REL, sizeof(relbits)), relbits) == -1) - { - WARN("ioctl(EVIOCGBIT, EV_REL) failed: %d %s\n", errno, strerror(errno)); - memset(relbits, 0, sizeof(relbits)); - } - if (ioctl(impl->base.device_fd, EVIOCGBIT(EV_ABS, sizeof(absbits)), absbits) == -1) - { - WARN("ioctl(EVIOCGBIT, EV_ABS) failed: %d %s\n", errno, strerror(errno)); - memset(absbits, 0, sizeof(absbits)); - } - if (ioctl(impl->base.device_fd, EVIOCGBIT(EV_FF, sizeof(ffbits)), ffbits) == -1) - { - WARN("ioctl(EVIOCGBIT, EV_FF) failed: %d %s\n", errno, strerror(errno)); - memset(ffbits, 0, sizeof(ffbits)); - } - if (!hid_device_begin_report_descriptor(iface, &device_usage)) return STATUS_NO_MEMORY; @@ -928,12 +911,13 @@ static NTSTATUS build_report_descriptor(struct unix_device *iface, struct udev_d return STATUS_NO_MEMORY; abs_count = 0; - for (i = 0; i < ARRAY_SIZE(absolute_usages); i++) + for (i = 0; i < ABS_CNT; i++) { - usage = absolute_usages[i]; - if (!test_bit(absbits, i)) continue; - ioctl(impl->base.device_fd, EVIOCGABS(i), abs_info + i); + USAGE_AND_PAGE usage = absolute_usages[i]; if (!usage.UsagePage || !usage.Usage) continue; + if (!test_bit(info->abs, i)) continue; + + ioctl(impl->base.device_fd, EVIOCGABS(i), abs_info + i); if (!hid_device_add_axes(iface, 1, usage.UsagePage, &usage.Usage, FALSE, LE_DWORD(abs_info[i].minimum), LE_DWORD(abs_info[i].maximum))) return STATUS_NO_MEMORY; @@ -942,11 +926,12 @@ static NTSTATUS build_report_descriptor(struct unix_device *iface, struct udev_d } rel_count = 0; - for (i = 0; i < ARRAY_SIZE(relative_usages); i++) + for (i = 0; i < REL_CNT; i++) { - usage = relative_usages[i]; - if (!test_bit(relbits, i)) continue; + USAGE_AND_PAGE usage = relative_usages[i]; if (!usage.UsagePage || !usage.Usage) continue; + if (!test_bit(info->rel, i)) continue; + if (!hid_device_add_axes(iface, 1, usage.UsagePage, &usage.Usage, TRUE, INT32_MIN, INT32_MAX)) return STATUS_NO_MEMORY; @@ -957,7 +942,7 @@ static NTSTATUS build_report_descriptor(struct unix_device *iface, struct udev_d hat_count = 0; for (i = ABS_HAT0X; i <= ABS_HAT3X; i += 2) { - if (!test_bit(absbits, i)) continue; + if (!test_bit(info->abs, i)) continue; impl->hat_map[i - ABS_HAT0X] = hat_count; impl->hat_map[i - ABS_HAT0X + 1] = hat_count++; } @@ -966,7 +951,7 @@ static NTSTATUS build_report_descriptor(struct unix_device *iface, struct udev_d return STATUS_NO_MEMORY; /* For now lump all buttons just into incremental usages, Ignore Keys */ - button_count = count_buttons(impl->base.device_fd, impl->button_map); + button_count = count_buttons(info, impl->button_map); if (button_count && !hid_device_add_buttons(iface, HID_USAGE_PAGE_BUTTON, 1, button_count)) return STATUS_NO_MEMORY; @@ -976,23 +961,23 @@ static NTSTATUS build_report_descriptor(struct unix_device *iface, struct udev_d impl->haptics.id = -1; for (i = 0; i < ARRAY_SIZE(impl->effect_ids); ++i) impl->effect_ids[i] = -1; - if (test_bit(ffbits, FF_RUMBLE) && !hid_device_add_haptics(iface)) + if (test_bit(info->ff, FF_RUMBLE) && !hid_device_add_haptics(iface)) return STATUS_NO_MEMORY; - for (i = 0; i < FF_MAX; ++i) if (test_bit(ffbits, i)) break; + for (i = 0; i < FF_MAX; ++i) if (test_bit(info->ff, i)) break; if (i != FF_MAX) { - if (test_bit(ffbits, FF_SINE)) usages[count++] = PID_USAGE_ET_SINE; - if (test_bit(ffbits, FF_SQUARE)) usages[count++] = PID_USAGE_ET_SQUARE; - if (test_bit(ffbits, FF_TRIANGLE)) usages[count++] = PID_USAGE_ET_TRIANGLE; - if (test_bit(ffbits, FF_SAW_UP)) usages[count++] = PID_USAGE_ET_SAWTOOTH_UP; - if (test_bit(ffbits, FF_SAW_DOWN)) usages[count++] = PID_USAGE_ET_SAWTOOTH_DOWN; - if (test_bit(ffbits, FF_SPRING)) usages[count++] = PID_USAGE_ET_SPRING; - if (test_bit(ffbits, FF_DAMPER)) usages[count++] = PID_USAGE_ET_DAMPER; - if (test_bit(ffbits, FF_INERTIA)) usages[count++] = PID_USAGE_ET_INERTIA; - if (test_bit(ffbits, FF_FRICTION)) usages[count++] = PID_USAGE_ET_FRICTION; - if (test_bit(ffbits, FF_CONSTANT)) usages[count++] = PID_USAGE_ET_CONSTANT_FORCE; - if (test_bit(ffbits, FF_RAMP)) usages[count++] = PID_USAGE_ET_RAMP; + if (test_bit(info->ff, FF_SINE)) usages[count++] = PID_USAGE_ET_SINE; + if (test_bit(info->ff, FF_SQUARE)) usages[count++] = PID_USAGE_ET_SQUARE; + if (test_bit(info->ff, FF_TRIANGLE)) usages[count++] = PID_USAGE_ET_TRIANGLE; + if (test_bit(info->ff, FF_SAW_UP)) usages[count++] = PID_USAGE_ET_SAWTOOTH_UP; + if (test_bit(info->ff, FF_SAW_DOWN)) usages[count++] = PID_USAGE_ET_SAWTOOTH_DOWN; + if (test_bit(info->ff, FF_SPRING)) usages[count++] = PID_USAGE_ET_SPRING; + if (test_bit(info->ff, FF_DAMPER)) usages[count++] = PID_USAGE_ET_DAMPER; + if (test_bit(info->ff, FF_INERTIA)) usages[count++] = PID_USAGE_ET_INERTIA; + if (test_bit(info->ff, FF_FRICTION)) usages[count++] = PID_USAGE_ET_FRICTION; + if (test_bit(info->ff, FF_CONSTANT)) usages[count++] = PID_USAGE_ET_CONSTANT_FORCE; + if (test_bit(info->ff, FF_RAMP)) usages[count++] = PID_USAGE_ET_RAMP; if (!hid_device_add_physical(iface, usages, count, 2)) return STATUS_NO_MEMORY; @@ -1002,9 +987,12 @@ static NTSTATUS build_report_descriptor(struct unix_device *iface, struct udev_d return STATUS_NO_MEMORY; /* Initialize axis in the report */ - for (i = 0; i < ARRAY_SIZE(absolute_usages); i++) + for (i = 0; i < ABS_CNT; i++) { - if (!test_bit(absbits, i)) continue; + USAGE_AND_PAGE usage = absolute_usages[i]; + if (!usage.UsagePage || !usage.Usage) continue; + if (!test_bit(info->abs, i)) continue; + if (i < ABS_HAT0X || i > ABS_HAT3Y) hid_device_set_abs_axis(iface, impl->abs_map[i], abs_info[i].value); else if ((i - ABS_HAT0X) % 2) @@ -1583,30 +1571,26 @@ static NTSTATUS lnxev_device_create(struct udev_device *dev, int fd, const char #ifdef HAS_PROPER_INPUT_HEADER static const WCHAR evdev[] = {'e','v','d','e','v',0}; static const WCHAR zeros[] = {'0','0','0','0',0}; - struct input_id device_id = {0}; + struct lnxev_info info = {0}; struct lnxev_device *impl; - char buffer[MAX_PATH]; - - if (ioctl(fd, EVIOCGID, &device_id) < 0) - WARN("ioctl(EVIOCGID) failed: %d %s\n", errno, strerror(errno)); - else - { - desc.vid = device_id.vendor; - desc.pid = device_id.product; - desc.version = device_id.version; - } - - if (!desc.product[0] && ioctl(fd, EVIOCGNAME(sizeof(buffer) - 1), buffer) > 0) - { - if (sscanf(buffer, "Microsoft X-Box 360 pad %u", &desc.input) != 1) desc.input = -1; - ntdll_umbstowcs(buffer, strlen(buffer) + 1, desc.product, ARRAY_SIZE(desc.product)); - } - if (!desc.serialnumber[0] && ioctl(fd, EVIOCGUNIQ(sizeof(buffer)), buffer) >= 0) - ntdll_umbstowcs(buffer, strlen(buffer) + 1, desc.serialnumber, ARRAY_SIZE(desc.serialnumber)); + if (ioctl(fd, EVIOCGID, &info.id) == -1) memset(&info.id, 0, sizeof(info.id)); + if (ioctl(fd, EVIOCGNAME(sizeof(info.name) - 1), info.name) == -1) memset(info.name, 0, sizeof(info.name)); + if (ioctl(fd, EVIOCGUNIQ(sizeof(info.uniq) - 1), info.uniq) == -1) memset(info.uniq, 0, sizeof(info.uniq)); + if (ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(info.abs)), info.abs) == -1) memset(info.abs, 0, sizeof(info.abs)); + if (ioctl(fd, EVIOCGBIT(EV_REL, sizeof(info.rel)), info.rel) == -1) memset(info.rel, 0, sizeof(info.rel)); + if (ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(info.key)), info.key) == -1) memset(info.key, 0, sizeof(info.key)); + if (ioctl(fd, EVIOCGBIT(EV_FF, sizeof(info.ff)), info.ff) == -1) memset(info.ff, 0, sizeof(info.ff)); + + if (!desc.vid) desc.vid = info.id.vendor; + if (!desc.pid) desc.pid = info.id.product; + if (!desc.version) desc.version = info.id.version; if (!desc.manufacturer[0]) memcpy(desc.manufacturer, evdev, sizeof(evdev)); + if (!desc.product[0]) ntdll_umbstowcs(info.name, strlen(info.name) + 1, desc.product, ARRAY_SIZE(desc.product)); + if (!desc.serialnumber[0]) ntdll_umbstowcs(info.uniq, strlen(info.uniq) + 1, desc.serialnumber, ARRAY_SIZE(desc.serialnumber)); if (!desc.serialnumber[0]) memcpy(desc.serialnumber, zeros, sizeof(zeros)); + if (sscanf(info.name, "Microsoft X-Box 360 pad %u", &desc.input) != 1) desc.input = -1; if (desc.vid == 0x28de && desc.pid == 0x11ff) { desc.is_gamepad = TRUE; @@ -1616,9 +1600,9 @@ static NTSTATUS lnxev_device_create(struct udev_device *dev, int fd, const char desc.is_gamepad = TRUE; else { - int axes=0, buttons=0; - axes = count_abs_axis(fd); - buttons = count_buttons(fd, NULL); + int axes = 0, buttons = 0; + axes = count_abs_axis(&info); + buttons = count_buttons(&info, NULL); desc.is_gamepad = (axes == 6 && buttons >= 14); } @@ -1630,7 +1614,7 @@ static NTSTATUS lnxev_device_create(struct udev_device *dev, int fd, const char strcpy(impl->base.devnode, devnode); impl->base.device_fd = fd; - if (build_report_descriptor(&impl->base.unix_device, impl->base.udev_device)) + if (build_report_descriptor(&impl->base.unix_device, impl->base.udev_device, &info)) { list_remove(&impl->base.unix_device.entry); impl->base.unix_device.vtbl->destroy(&impl->base.unix_device); From 53ebdb022ef1d54711be8ccc60f3df673263169a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 18 Aug 2025 17:43:53 +0200 Subject: [PATCH 046/454] winebus: Fill device mapping before report descriptor creation. (cherry picked from commit 238a0cb35c21423bd40cb7c4103934b78193b61f) CW-Bug-Id: #25366 CW-Bug-Id: #25325 --- dlls/winebus.sys/bus_udev.c | 184 ++++++++++++++---------------------- 1 file changed, 73 insertions(+), 111 deletions(-) diff --git a/dlls/winebus.sys/bus_udev.c b/dlls/winebus.sys/bus_udev.c index 72f887fc778d..6fffaceaa2af 100644 --- a/dlls/winebus.sys/bus_udev.c +++ b/dlls/winebus.sys/bus_udev.c @@ -200,6 +200,8 @@ struct lnxev_device BYTE rel_map[REL_CNT]; BYTE hat_map[8]; BYTE button_map[KEY_CNT]; + int hat_count; + int button_count; int haptics_state; pthread_cond_t haptics_cond; @@ -836,71 +838,12 @@ static const USAGE_AND_PAGE *what_am_I(struct udev_device *dev, int fd) return &Unknown; } -static INT count_buttons(const struct lnxev_info *info, BYTE *map) -{ - static const UINT gamepad_buttons[] = - { - BTN_A, - BTN_B, - BTN_X, - BTN_Y, - BTN_TL, - BTN_TR, - BTN_SELECT, - BTN_START, - BTN_THUMBL, - BTN_THUMBR, - BTN_MODE, - BTN_C, - BTN_Z, - BTN_TL2, - BTN_TR2, - }; - int i; - int button_count = 0; - - for (i = 0; i < ARRAY_SIZE(gamepad_buttons); i++) - { - if (test_bit(info->key, gamepad_buttons[i])) - { - if (map) map[gamepad_buttons[i]] = button_count; - button_count++; - } - } - - for (i = BTN_DIGI; i < KEY_MAX; i++) - { - if (test_bit(info->key, i)) - { - if (map) map[i] = button_count; - button_count++; - } - } - return button_count; -} - -static INT count_abs_axis(const struct lnxev_info *info) -{ - int abs_count = 0; - int i; - - for (i = 0; i < ABS_CNT; i++) - { - USAGE_AND_PAGE usage = absolute_usages[i]; - if (!usage.UsagePage || !usage.Usage) continue; - if (!test_bit(info->abs, i)) continue; - abs_count++; - } - - return abs_count; -} - static NTSTATUS build_report_descriptor(struct unix_device *iface, struct udev_device *dev, struct lnxev_info *info) { struct input_absinfo abs_info[ABS_CNT]; USHORT count = 0; USAGE usages[16]; - INT i, button_count, abs_count, rel_count, hat_count; + INT i, axis; struct lnxev_device *impl = lnxev_impl_from_unix_device(iface); const USAGE_AND_PAGE device_usage = *what_am_I(dev, impl->base.device_fd); @@ -910,50 +853,27 @@ static NTSTATUS build_report_descriptor(struct unix_device *iface, struct udev_d if (!hid_device_begin_input_report(iface, &device_usage)) return STATUS_NO_MEMORY; - abs_count = 0; for (i = 0; i < ABS_CNT; i++) { USAGE_AND_PAGE usage = absolute_usages[i]; - if (!usage.UsagePage || !usage.Usage) continue; - if (!test_bit(info->abs, i)) continue; - + if (!impl->abs_map[i]) continue; ioctl(impl->base.device_fd, EVIOCGABS(i), abs_info + i); if (!hid_device_add_axes(iface, 1, usage.UsagePage, &usage.Usage, FALSE, LE_DWORD(abs_info[i].minimum), LE_DWORD(abs_info[i].maximum))) return STATUS_NO_MEMORY; - - impl->abs_map[i] = abs_count++; } - rel_count = 0; for (i = 0; i < REL_CNT; i++) { USAGE_AND_PAGE usage = relative_usages[i]; - if (!usage.UsagePage || !usage.Usage) continue; - if (!test_bit(info->rel, i)) continue; - + if (!impl->rel_map[i]) continue; if (!hid_device_add_axes(iface, 1, usage.UsagePage, &usage.Usage, TRUE, INT32_MIN, INT32_MAX)) return STATUS_NO_MEMORY; - - impl->rel_map[i] = rel_count++; - } - - hat_count = 0; - for (i = ABS_HAT0X; i <= ABS_HAT3X; i += 2) - { - if (!test_bit(info->abs, i)) continue; - impl->hat_map[i - ABS_HAT0X] = hat_count; - impl->hat_map[i - ABS_HAT0X + 1] = hat_count++; } - if (hat_count && !hid_device_add_hatswitch(iface, hat_count)) - return STATUS_NO_MEMORY; - - /* For now lump all buttons just into incremental usages, Ignore Keys */ - button_count = count_buttons(info, impl->button_map); - if (button_count && !hid_device_add_buttons(iface, HID_USAGE_PAGE_BUTTON, 1, button_count)) - return STATUS_NO_MEMORY; + if (impl->hat_count && !hid_device_add_hatswitch(iface, impl->hat_count)) return STATUS_NO_MEMORY; + if (impl->button_count && !hid_device_add_buttons(iface, HID_USAGE_PAGE_BUTTON, 1, impl->button_count)) return STATUS_NO_MEMORY; if (!hid_device_end_input_report(iface)) return STATUS_NO_MEMORY; @@ -994,11 +914,20 @@ static NTSTATUS build_report_descriptor(struct unix_device *iface, struct udev_d if (!test_bit(info->abs, i)) continue; if (i < ABS_HAT0X || i > ABS_HAT3Y) - hid_device_set_abs_axis(iface, impl->abs_map[i], abs_info[i].value); + { + if (!(axis = impl->abs_map[i])) continue; + hid_device_set_abs_axis(iface, axis - 1, abs_info[i].value); + } else if ((i - ABS_HAT0X) % 2) - hid_device_set_hatswitch_y(iface, impl->hat_map[i - ABS_HAT0X], abs_info[i].value); + { + if (!(axis = impl->hat_map[i - ABS_HAT0X])) continue; + hid_device_set_hatswitch_y(iface, axis - 1, abs_info[i].value); + } else - hid_device_set_hatswitch_x(iface, impl->hat_map[i - ABS_HAT0X], abs_info[i].value); + { + if (!(axis = impl->hat_map[i - ABS_HAT0X])) continue; + hid_device_set_hatswitch_x(iface, axis - 1, abs_info[i].value); + } } return STATUS_SUCCESS; @@ -1009,7 +938,7 @@ static BOOL set_report_from_event(struct unix_device *iface, struct input_event struct hid_effect_state *effect_state = &iface->hid_physical.effect_state; struct lnxev_device *impl = lnxev_impl_from_unix_device(iface); ULONG effect_flags = InterlockedOr(&impl->effect_flags, 0); - unsigned int i; + unsigned int i, axis, button; switch (ie->type) { @@ -1027,15 +956,25 @@ static BOOL set_report_from_event(struct unix_device *iface, struct input_event return FALSE; #endif case EV_KEY: - hid_device_set_button(iface, impl->button_map[ie->code], ie->value); + if (!(button = impl->button_map[ie->code])) return FALSE; + hid_device_set_button(iface, button - 1, ie->value); return FALSE; case EV_ABS: if (ie->code < ABS_HAT0X || ie->code > ABS_HAT3Y) - hid_device_set_abs_axis(iface, impl->abs_map[ie->code], ie->value); + { + if (!(axis = impl->abs_map[ie->code])) return FALSE; + hid_device_set_abs_axis(iface, axis - 1, ie->value); + } else if ((ie->code - ABS_HAT0X) % 2) - hid_device_set_hatswitch_y(iface, impl->hat_map[ie->code - ABS_HAT0X], ie->value); + { + if (!(axis = impl->hat_map[ie->code - ABS_HAT0X])) return FALSE; + hid_device_set_hatswitch_y(iface, axis - 1, ie->value); + } else - hid_device_set_hatswitch_x(iface, impl->hat_map[ie->code - ABS_HAT0X], ie->value); + { + if (!(axis = impl->hat_map[ie->code - ABS_HAT0X])) return FALSE; + hid_device_set_hatswitch_x(iface, axis - 1, ie->value); + } return FALSE; case EV_REL: hid_device_set_rel_axis(iface, impl->rel_map[ie->code], ie->value); @@ -1571,6 +1510,7 @@ static NTSTATUS lnxev_device_create(struct udev_device *dev, int fd, const char #ifdef HAS_PROPER_INPUT_HEADER static const WCHAR evdev[] = {'e','v','d','e','v',0}; static const WCHAR zeros[] = {'0','0','0','0',0}; + int axis_count = 0, button_count = 0; struct lnxev_info info = {0}; struct lnxev_device *impl; @@ -1590,22 +1530,6 @@ static NTSTATUS lnxev_device_create(struct udev_device *dev, int fd, const char if (!desc.serialnumber[0]) ntdll_umbstowcs(info.uniq, strlen(info.uniq) + 1, desc.serialnumber, ARRAY_SIZE(desc.serialnumber)); if (!desc.serialnumber[0]) memcpy(desc.serialnumber, zeros, sizeof(zeros)); - if (sscanf(info.name, "Microsoft X-Box 360 pad %u", &desc.input) != 1) desc.input = -1; - if (desc.vid == 0x28de && desc.pid == 0x11ff) - { - desc.is_gamepad = TRUE; - desc.version = 0; /* keep version fixed as 0 so we can hardcode it in ntdll rawinput pipe redirection */ - } - else if (is_xbox_gamepad(desc.vid, desc.pid)) - desc.is_gamepad = TRUE; - else - { - int axes = 0, buttons = 0; - axes = count_abs_axis(&info); - buttons = count_buttons(&info, NULL); - desc.is_gamepad = (axes == 6 && buttons >= 14); - } - if (!(impl = hid_device_create(&lnxev_device_vtbl, sizeof(struct lnxev_device)))) return STATUS_NO_MEMORY; list_add_tail(&device_list, &impl->base.unix_device.entry); @@ -1614,6 +1538,44 @@ static NTSTATUS lnxev_device_create(struct udev_device *dev, int fd, const char strcpy(impl->base.devnode, devnode); impl->base.device_fd = fd; + for (int i = 0; i < ABS_CNT; i++) + { + USAGE_AND_PAGE usage = absolute_usages[i]; + if (!usage.UsagePage || !usage.Usage) continue; + if (!test_bit(info.abs, i)) continue; + impl->abs_map[i] = ++axis_count; + } + + for (int i = 0, count = 0; i < REL_CNT; i++) + { + USAGE_AND_PAGE usage = relative_usages[i]; + if (!usage.UsagePage || !usage.Usage) continue; + if (!test_bit(info.rel, i)) continue; + impl->rel_map[i] = ++count; + } + + for (int i = ABS_HAT0X; i <= ABS_HAT3X; i += 2) + { + if (!test_bit(info.abs, i)) continue; + impl->hat_map[i - ABS_HAT0X] = ++impl->hat_count; + impl->hat_map[i - ABS_HAT0X + 1] = impl->hat_count; + } + + for (int i = BTN_MISC; i < KEY_MAX; i++) + { + if (!test_bit(info.key, i)) continue; + impl->button_map[i] = ++impl->button_count; + } + + if (sscanf(info.name, "Microsoft X-Box 360 pad %u", &desc.input) != 1) desc.input = -1; + if (desc.vid == 0x28de && desc.pid == 0x11ff) + { + desc.is_gamepad = TRUE; + desc.version = 0; /* keep version fixed as 0 so we can hardcode it in ntdll rawinput pipe redirection */ + } + else if (is_xbox_gamepad(desc.vid, desc.pid)) desc.is_gamepad = TRUE; + else if (axis_count == 6 && button_count >= 14) desc.is_gamepad = TRUE; + if (build_report_descriptor(&impl->base.unix_device, impl->base.udev_device, &info)) { list_remove(&impl->base.unix_device.entry); From 9edaf6ef704f9e7694dc053ef2a45afb3fe500ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 5 Aug 2025 12:24:54 +0200 Subject: [PATCH 047/454] winebus: Force the ordering of some common evdev gamepad buttons. (cherry picked from commit 6f06f4d50c2042a8d68bdb97c59faba0a0b5e15a) CW-Bug-Id: #25366 CW-Bug-Id: #25325 --- dlls/winebus.sys/bus_udev.c | 41 +++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/dlls/winebus.sys/bus_udev.c b/dlls/winebus.sys/bus_udev.c index 6fffaceaa2af..d77dd9f4c42d 100644 --- a/dlls/winebus.sys/bus_udev.c +++ b/dlls/winebus.sys/bus_udev.c @@ -1576,6 +1576,47 @@ static NTSTATUS lnxev_device_create(struct udev_device *dev, int fd, const char else if (is_xbox_gamepad(desc.vid, desc.pid)) desc.is_gamepad = TRUE; else if (axis_count == 6 && button_count >= 14) desc.is_gamepad = TRUE; + if (desc.is_gamepad) + { + static const UINT gamepad_buttons[] = + { + BTN_A, + BTN_B, + BTN_X, + BTN_Y, + BTN_TL, + BTN_TR, + BTN_SELECT, + BTN_START, + BTN_THUMBL, + BTN_THUMBR, + BTN_MODE, + BTN_C, + BTN_Z, + BTN_TL2, + BTN_TR2, + }; + + memset(impl->button_map, 0, sizeof(impl->button_map)); + impl->button_count = 0; + + for (int i = 0; i < ARRAY_SIZE(gamepad_buttons); i++) + { + int button = gamepad_buttons[i]; + if (!test_bit(info.key, button)) continue; + if (impl->button_count > 14) break; + impl->button_map[button] = ++impl->button_count; + } + + for (int i = BTN_MISC; i < KEY_MAX; i++) + { + if (i >= BTN_GAMEPAD && i < BTN_DIGI) continue; + if (impl->button_count > 14) break; + if (!test_bit(info.key, i)) continue; + impl->button_map[i] = ++impl->button_count; + } + } + if (build_report_descriptor(&impl->base.unix_device, impl->base.udev_device, &info)) { list_remove(&impl->base.unix_device.entry); From 80ce921c7ba7f1702e8dd9222255680119719291 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 7 Aug 2025 15:11:37 +0200 Subject: [PATCH 048/454] winebus: Return error status if SDL is disabled. Otherwise we'll disable evdev backend too. Fixes: 3394379fa7bfdc5010a8963c566086254488c996 (cherry picked from commit 5206181b067e06a6bba1cd6d10234b4572d90be2) CW-Bug-Id: #25366 --- dlls/winebus.sys/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/winebus.sys/main.c b/dlls/winebus.sys/main.c index 2e3d35e59473..2ff76355b98e 100644 --- a/dlls/winebus.sys/main.c +++ b/dlls/winebus.sys/main.c @@ -1249,7 +1249,7 @@ static NTSTATUS sdl_driver_init(void) .init_code = sdl_init, .wait_code = sdl_wait, }; - if (options.disable_sdl) return STATUS_SUCCESS; + if (options.disable_sdl) return STATUS_NOT_SUPPORTED; return bus_main_thread_start(&bus); } From 116ac3d69cb342ab00730d3c35f0a22a1d6d67c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 5 Aug 2025 12:34:41 +0200 Subject: [PATCH 049/454] winebus: Introduce a new set_abs_axis_value helper. (cherry picked from commit 66616b51d342d05e8298ed20e84b577e08757681) CW-Bug-Id: #25366 CW-Bug-Id: #25325 --- dlls/winebus.sys/bus_udev.c | 58 ++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 33 deletions(-) diff --git a/dlls/winebus.sys/bus_udev.c b/dlls/winebus.sys/bus_udev.c index d77dd9f4c42d..edda296f25a2 100644 --- a/dlls/winebus.sys/bus_udev.c +++ b/dlls/winebus.sys/bus_udev.c @@ -838,12 +838,33 @@ static const USAGE_AND_PAGE *what_am_I(struct udev_device *dev, int fd) return &Unknown; } +static void set_abs_axis_value(struct unix_device *iface, int code, int value) +{ + struct lnxev_device *impl = lnxev_impl_from_unix_device(iface); + + if (code < ABS_HAT0X || code > ABS_HAT3Y) + { + if (!(code = impl->abs_map[code])) return; + hid_device_set_abs_axis(iface, code - 1, value); + } + else if ((code - ABS_HAT0X) % 2) + { + if (!(code = impl->hat_map[code - ABS_HAT0X])) return; + hid_device_set_hatswitch_y(iface, code - 1, value); + } + else + { + if (!(code = impl->hat_map[code - ABS_HAT0X])) return; + hid_device_set_hatswitch_x(iface, code - 1, value); + } +} + static NTSTATUS build_report_descriptor(struct unix_device *iface, struct udev_device *dev, struct lnxev_info *info) { struct input_absinfo abs_info[ABS_CNT]; USHORT count = 0; USAGE usages[16]; - INT i, axis; + int i; struct lnxev_device *impl = lnxev_impl_from_unix_device(iface); const USAGE_AND_PAGE device_usage = *what_am_I(dev, impl->base.device_fd); @@ -912,22 +933,7 @@ static NTSTATUS build_report_descriptor(struct unix_device *iface, struct udev_d USAGE_AND_PAGE usage = absolute_usages[i]; if (!usage.UsagePage || !usage.Usage) continue; if (!test_bit(info->abs, i)) continue; - - if (i < ABS_HAT0X || i > ABS_HAT3Y) - { - if (!(axis = impl->abs_map[i])) continue; - hid_device_set_abs_axis(iface, axis - 1, abs_info[i].value); - } - else if ((i - ABS_HAT0X) % 2) - { - if (!(axis = impl->hat_map[i - ABS_HAT0X])) continue; - hid_device_set_hatswitch_y(iface, axis - 1, abs_info[i].value); - } - else - { - if (!(axis = impl->hat_map[i - ABS_HAT0X])) continue; - hid_device_set_hatswitch_x(iface, axis - 1, abs_info[i].value); - } + set_abs_axis_value(iface, i, abs_info[i].value); } return STATUS_SUCCESS; @@ -938,7 +944,7 @@ static BOOL set_report_from_event(struct unix_device *iface, struct input_event struct hid_effect_state *effect_state = &iface->hid_physical.effect_state; struct lnxev_device *impl = lnxev_impl_from_unix_device(iface); ULONG effect_flags = InterlockedOr(&impl->effect_flags, 0); - unsigned int i, axis, button; + unsigned int i, button; switch (ie->type) { @@ -960,21 +966,7 @@ static BOOL set_report_from_event(struct unix_device *iface, struct input_event hid_device_set_button(iface, button - 1, ie->value); return FALSE; case EV_ABS: - if (ie->code < ABS_HAT0X || ie->code > ABS_HAT3Y) - { - if (!(axis = impl->abs_map[ie->code])) return FALSE; - hid_device_set_abs_axis(iface, axis - 1, ie->value); - } - else if ((ie->code - ABS_HAT0X) % 2) - { - if (!(axis = impl->hat_map[ie->code - ABS_HAT0X])) return FALSE; - hid_device_set_hatswitch_y(iface, axis - 1, ie->value); - } - else - { - if (!(axis = impl->hat_map[ie->code - ABS_HAT0X])) return FALSE; - hid_device_set_hatswitch_x(iface, axis - 1, ie->value); - } + set_abs_axis_value(iface, ie->code, ie->value); return FALSE; case EV_REL: hid_device_set_rel_axis(iface, impl->rel_map[ie->code], ie->value); From 63398134fbc088e905d7a493b9e79d4fecb51d9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 5 Aug 2025 12:34:41 +0200 Subject: [PATCH 050/454] winebus: Emulate some gamepad buttons in the evdev backend. (cherry picked from commit a580e9eb3433000d1a6a195f74914d0b8f92d553) CW-Bug-Id: #25366 CW-Bug-Id: #25325 --- dlls/winebus.sys/bus_udev.c | 38 +++++++++++++++++++++++++++++++------ 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/dlls/winebus.sys/bus_udev.c b/dlls/winebus.sys/bus_udev.c index edda296f25a2..e335483ff2c1 100644 --- a/dlls/winebus.sys/bus_udev.c +++ b/dlls/winebus.sys/bus_udev.c @@ -202,6 +202,7 @@ struct lnxev_device BYTE button_map[KEY_CNT]; int hat_count; int button_count; + BOOL is_gamepad; int haptics_state; pthread_cond_t haptics_cond; @@ -850,11 +851,21 @@ static void set_abs_axis_value(struct unix_device *iface, int code, int value) else if ((code - ABS_HAT0X) % 2) { if (!(code = impl->hat_map[code - ABS_HAT0X])) return; + if (impl->is_gamepad) + { + hid_device_set_button(iface, 11, value < 0); + hid_device_set_button(iface, 12, value > 0); + } hid_device_set_hatswitch_y(iface, code - 1, value); } else { if (!(code = impl->hat_map[code - ABS_HAT0X])) return; + if (impl->is_gamepad) + { + hid_device_set_button(iface, 13, value < 0); + hid_device_set_button(iface, 14, value > 0); + } hid_device_set_hatswitch_x(iface, code - 1, value); } } @@ -893,8 +904,16 @@ static NTSTATUS build_report_descriptor(struct unix_device *iface, struct udev_d return STATUS_NO_MEMORY; } - if (impl->hat_count && !hid_device_add_hatswitch(iface, impl->hat_count)) return STATUS_NO_MEMORY; - if (impl->button_count && !hid_device_add_buttons(iface, HID_USAGE_PAGE_BUTTON, 1, impl->button_count)) return STATUS_NO_MEMORY; + if (impl->is_gamepad) + { + if (!hid_device_add_hatswitch(iface, 1)) return STATUS_NO_MEMORY; + if (!hid_device_add_buttons(iface, HID_USAGE_PAGE_BUTTON, 1, 15)) return STATUS_NO_MEMORY; + } + else + { + if (impl->hat_count && !hid_device_add_hatswitch(iface, impl->hat_count)) return STATUS_NO_MEMORY; + if (impl->button_count && !hid_device_add_buttons(iface, HID_USAGE_PAGE_BUTTON, 1, impl->button_count)) return STATUS_NO_MEMORY; + } if (!hid_device_end_input_report(iface)) return STATUS_NO_MEMORY; @@ -963,6 +982,13 @@ static BOOL set_report_from_event(struct unix_device *iface, struct input_event #endif case EV_KEY: if (!(button = impl->button_map[ie->code])) return FALSE; + if (impl->is_gamepad && !impl->hat_count) + { + if (button == 12) hid_device_set_hatswitch_y(iface, 0, -1); + if (button == 13) hid_device_set_hatswitch_y(iface, 0, +1); + if (button == 14) hid_device_set_hatswitch_x(iface, 0, -1); + if (button == 15) hid_device_set_hatswitch_x(iface, 0, +1); + } hid_device_set_button(iface, button - 1, ie->value); return FALSE; case EV_ABS: @@ -1566,9 +1592,9 @@ static NTSTATUS lnxev_device_create(struct udev_device *dev, int fd, const char desc.version = 0; /* keep version fixed as 0 so we can hardcode it in ntdll rawinput pipe redirection */ } else if (is_xbox_gamepad(desc.vid, desc.pid)) desc.is_gamepad = TRUE; - else if (axis_count == 6 && button_count >= 14) desc.is_gamepad = TRUE; + else if (axis_count == 6 && button_count >= (impl->hat_count ? 10 : 14)) desc.is_gamepad = TRUE; - if (desc.is_gamepad) + if ((impl->is_gamepad = desc.is_gamepad)) { static const UINT gamepad_buttons[] = { @@ -1596,14 +1622,14 @@ static NTSTATUS lnxev_device_create(struct udev_device *dev, int fd, const char { int button = gamepad_buttons[i]; if (!test_bit(info.key, button)) continue; - if (impl->button_count > 14) break; + if (impl->button_count > (impl->hat_count ? 10 : 14)) break; impl->button_map[button] = ++impl->button_count; } for (int i = BTN_MISC; i < KEY_MAX; i++) { if (i >= BTN_GAMEPAD && i < BTN_DIGI) continue; - if (impl->button_count > 14) break; + if (impl->button_count > (impl->hat_count ? 10 : 14)) break; if (!test_bit(info.key, i)) continue; impl->button_map[i] = ++impl->button_count; } From 5b0a67fd1751bc30846feb629a3a54e5a7f70f8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 5 Aug 2025 13:45:43 +0200 Subject: [PATCH 051/454] winebus: Introduce a new hid_device_add_gamepad helper. (cherry picked from commit 244cb4450807a64622761ef2a7f634f096a0adcf) CW-Bug-Id: #25366 CW-Bug-Id: #25325 --- dlls/winebus.sys/bus_sdl.c | 42 ++++----------------------------- dlls/winebus.sys/hid.c | 18 ++++++++++++++ dlls/winebus.sys/unix_private.h | 1 + 3 files changed, 24 insertions(+), 37 deletions(-) diff --git a/dlls/winebus.sys/bus_sdl.c b/dlls/winebus.sys/bus_sdl.c index ea92a000c380..5f13f6daa8ab 100644 --- a/dlls/winebus.sys/bus_sdl.c +++ b/dlls/winebus.sys/bus_sdl.c @@ -406,50 +406,18 @@ static NTSTATUS build_joystick_report_descriptor(struct unix_device *iface) static NTSTATUS build_controller_report_descriptor(struct unix_device *iface) { const USAGE_AND_PAGE device_usage = {.UsagePage = HID_USAGE_PAGE_GENERIC, .Usage = HID_USAGE_GENERIC_GAMEPAD}; - static const USAGE left_axis_usages[] = {HID_USAGE_GENERIC_X, HID_USAGE_GENERIC_Y}; - static const USAGE right_axis_usages[] = {HID_USAGE_GENERIC_RX, HID_USAGE_GENERIC_RY}; - static const USAGE trigger_axis_usages[] = {HID_USAGE_GENERIC_Z, HID_USAGE_GENERIC_RZ}; struct sdl_device *impl = impl_from_unix_device(iface); - ULONG i, button_count = SDL_CONTROLLER_BUTTON_MAX - 1; BOOL state; C_ASSERT(SDL_CONTROLLER_AXIS_MAX == 6); - if (!hid_device_begin_report_descriptor(iface, &device_usage)) - return STATUS_NO_MEMORY; - - if (!hid_device_begin_input_report(iface, &device_usage)) - return STATUS_NO_MEMORY; - - if (!hid_device_add_axes(iface, 2, HID_USAGE_PAGE_GENERIC, left_axis_usages, - FALSE, -32768, 32767)) - return STATUS_NO_MEMORY; - - if (!hid_device_add_axes(iface, 2, HID_USAGE_PAGE_GENERIC, right_axis_usages, - FALSE, -32768, 32767)) - return STATUS_NO_MEMORY; - - if (!hid_device_add_axes(iface, 2, HID_USAGE_PAGE_GENERIC, trigger_axis_usages, - FALSE, 0, 32767)) - return STATUS_NO_MEMORY; - - if (!hid_device_add_hatswitch(iface, 1)) - return STATUS_NO_MEMORY; - - if (!hid_device_add_buttons(iface, HID_USAGE_PAGE_BUTTON, 1, button_count)) - return STATUS_NO_MEMORY; - - if (!hid_device_end_input_report(iface)) - return STATUS_NO_MEMORY; - - if (!descriptor_add_haptic(impl, FALSE)) - return STATUS_NO_MEMORY; - - if (!hid_device_end_report_descriptor(iface)) - return STATUS_NO_MEMORY; + if (!hid_device_begin_report_descriptor(iface, &device_usage)) return STATUS_NO_MEMORY; + if (!hid_device_add_gamepad(iface)) return STATUS_NO_MEMORY; + if (!descriptor_add_haptic(impl, FALSE)) return STATUS_NO_MEMORY; + if (!hid_device_end_report_descriptor(iface)) return STATUS_NO_MEMORY; /* Initialize axis in the report */ - for (i = SDL_CONTROLLER_AXIS_LEFTX; i < SDL_CONTROLLER_AXIS_MAX; i++) + for (int i = SDL_CONTROLLER_AXIS_LEFTX; i < SDL_CONTROLLER_AXIS_MAX; i++) hid_device_set_abs_axis(iface, i, pSDL_GameControllerGetAxis(impl->sdl_controller, i)); state = pSDL_GameControllerGetButton(impl->sdl_controller, SDL_CONTROLLER_BUTTON_DPAD_UP); diff --git a/dlls/winebus.sys/hid.c b/dlls/winebus.sys/hid.c index 7bb6617501d4..693697fa8c00 100644 --- a/dlls/winebus.sys/hid.c +++ b/dlls/winebus.sys/hid.c @@ -343,6 +343,24 @@ BOOL hid_device_add_axes(struct unix_device *iface, BYTE count, USAGE usage_page return TRUE; } +BOOL hid_device_add_gamepad(struct unix_device *iface) +{ + static const USAGE_AND_PAGE device_usage = {.UsagePage = HID_USAGE_PAGE_GENERIC, .Usage = HID_USAGE_GENERIC_GAMEPAD}; + static const USAGE left[] = {HID_USAGE_GENERIC_X, HID_USAGE_GENERIC_Y}; + static const USAGE right[] = {HID_USAGE_GENERIC_RX, HID_USAGE_GENERIC_RY}; + static const USAGE trigger[] = {HID_USAGE_GENERIC_Z, HID_USAGE_GENERIC_RZ}; + + if (!hid_device_begin_input_report(iface, &device_usage)) return FALSE; + if (!hid_device_add_axes(iface, 2, HID_USAGE_PAGE_GENERIC, left, FALSE, -32768, 32767)) return FALSE; + if (!hid_device_add_axes(iface, 2, HID_USAGE_PAGE_GENERIC, right, FALSE, -32768, 32767)) return FALSE; + if (!hid_device_add_axes(iface, 2, HID_USAGE_PAGE_GENERIC, trigger, FALSE, 0, 32767)) return FALSE; + if (!hid_device_add_hatswitch(iface, 1)) return FALSE; + if (!hid_device_add_buttons(iface, HID_USAGE_PAGE_BUTTON, 1, 15)) return FALSE; + if (!hid_device_end_input_report(iface)) return FALSE; + + return TRUE; +} + #include "pshpack1.h" struct hid_haptics_intensity { diff --git a/dlls/winebus.sys/unix_private.h b/dlls/winebus.sys/unix_private.h index 34db3a7a02ee..52f50ef19e8a 100644 --- a/dlls/winebus.sys/unix_private.h +++ b/dlls/winebus.sys/unix_private.h @@ -253,6 +253,7 @@ extern BOOL hid_device_add_hatswitch(struct unix_device *iface, INT count); extern BOOL hid_device_add_axes(struct unix_device *iface, BYTE count, USAGE usage_page, const USAGE *usages, BOOL rel, LONG min, LONG max); +extern BOOL hid_device_add_gamepad(struct unix_device *iface); extern BOOL hid_device_add_haptics(struct unix_device *iface); extern BOOL hid_device_add_physical(struct unix_device *iface, USAGE *usages, USHORT count, USHORT axes_count); From ce55844c0802f5b0a3d48d69070266866140bbf0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 5 Aug 2025 13:45:43 +0200 Subject: [PATCH 052/454] winebus: Use hid_device_add_gamepad in the evdev backend. (cherry picked from commit de42e78141582186eb2f53cdc5e1d0135b59a040) CW-Bug-Id: #25366 CW-Bug-Id: #25325 --- dlls/winebus.sys/bus_udev.c | 83 +++++++++++++++++++++++-------------- 1 file changed, 51 insertions(+), 32 deletions(-) diff --git a/dlls/winebus.sys/bus_udev.c b/dlls/winebus.sys/bus_udev.c index e335483ff2c1..478a0ed54e7f 100644 --- a/dlls/winebus.sys/bus_udev.c +++ b/dlls/winebus.sys/bus_udev.c @@ -81,12 +81,6 @@ # include "hidusage.h" #endif -#ifdef WORDS_BIGENDIAN -#define LE_DWORD(x) RtlUlongByteSwap(x) -#else -#define LE_DWORD(x) (x) -#endif - #include "unix_private.h" WINE_DEFAULT_DEBUG_CHANNEL(hid); @@ -196,6 +190,8 @@ struct lnxev_device { struct base_device base; + LONG abs_min[ABS_CNT]; + LONG abs_max[ABS_CNT]; BYTE abs_map[ABS_CNT]; BYTE rel_map[REL_CNT]; BYTE hat_map[8]; @@ -845,7 +841,15 @@ static void set_abs_axis_value(struct unix_device *iface, int code, int value) if (code < ABS_HAT0X || code > ABS_HAT3Y) { + LONG min = impl->abs_min[code], range = impl->abs_max[code] - impl->abs_min[code]; if (!(code = impl->abs_map[code])) return; + + if (impl->is_gamepad) + { + double scale = (code == 5 || code == 6 ? 32767.0 : 65535.0) / range; + value = (value - min) * scale - (code == 5 || code == 6 ? 0 : 32768); + } + hid_device_set_abs_axis(iface, code - 1, value); } else if ((code - ABS_HAT0X) % 2) @@ -882,41 +886,42 @@ static NTSTATUS build_report_descriptor(struct unix_device *iface, struct udev_d if (!hid_device_begin_report_descriptor(iface, &device_usage)) return STATUS_NO_MEMORY; - if (!hid_device_begin_input_report(iface, &device_usage)) - return STATUS_NO_MEMORY; - - for (i = 0; i < ABS_CNT; i++) + if (impl->is_gamepad) { - USAGE_AND_PAGE usage = absolute_usages[i]; - if (!impl->abs_map[i]) continue; - ioctl(impl->base.device_fd, EVIOCGABS(i), abs_info + i); - if (!hid_device_add_axes(iface, 1, usage.UsagePage, &usage.Usage, FALSE, - LE_DWORD(abs_info[i].minimum), LE_DWORD(abs_info[i].maximum))) + if (!hid_device_add_gamepad(iface)) return STATUS_NO_MEMORY; } - - for (i = 0; i < REL_CNT; i++) + else { - USAGE_AND_PAGE usage = relative_usages[i]; - if (!impl->rel_map[i]) continue; - if (!hid_device_add_axes(iface, 1, usage.UsagePage, &usage.Usage, TRUE, - INT32_MIN, INT32_MAX)) + if (!hid_device_begin_input_report(iface, &device_usage)) return STATUS_NO_MEMORY; - } - if (impl->is_gamepad) - { - if (!hid_device_add_hatswitch(iface, 1)) return STATUS_NO_MEMORY; - if (!hid_device_add_buttons(iface, HID_USAGE_PAGE_BUTTON, 1, 15)) return STATUS_NO_MEMORY; - } - else - { + for (i = 0; i < ABS_CNT; i++) + { + LONG min = impl->abs_min[i], max = impl->abs_max[i]; + USAGE_AND_PAGE usage = absolute_usages[i]; + if (!impl->abs_map[i]) continue; + ioctl(impl->base.device_fd, EVIOCGABS(i), abs_info + i); + if (!hid_device_add_axes(iface, 1, usage.UsagePage, &usage.Usage, FALSE, + min, max)) + return STATUS_NO_MEMORY; + } + + for (i = 0; i < REL_CNT; i++) + { + USAGE_AND_PAGE usage = relative_usages[i]; + if (!impl->rel_map[i]) continue; + if (!hid_device_add_axes(iface, 1, usage.UsagePage, &usage.Usage, TRUE, + INT32_MIN, INT32_MAX)) + return STATUS_NO_MEMORY; + } + if (impl->hat_count && !hid_device_add_hatswitch(iface, impl->hat_count)) return STATUS_NO_MEMORY; if (impl->button_count && !hid_device_add_buttons(iface, HID_USAGE_PAGE_BUTTON, 1, impl->button_count)) return STATUS_NO_MEMORY; - } - if (!hid_device_end_input_report(iface)) - return STATUS_NO_MEMORY; + if (!hid_device_end_input_report(iface)) + return STATUS_NO_MEMORY; + } impl->haptics.id = -1; for (i = 0; i < ARRAY_SIZE(impl->effect_ids); ++i) impl->effect_ids[i] = -1; @@ -1559,9 +1564,13 @@ static NTSTATUS lnxev_device_create(struct udev_device *dev, int fd, const char for (int i = 0; i < ABS_CNT; i++) { USAGE_AND_PAGE usage = absolute_usages[i]; + struct input_absinfo abs; if (!usage.UsagePage || !usage.Usage) continue; if (!test_bit(info.abs, i)) continue; + ioctl(fd, EVIOCGABS(i), &abs); impl->abs_map[i] = ++axis_count; + impl->abs_min[i] = abs.minimum; + impl->abs_max[i] = abs.maximum; } for (int i = 0, count = 0; i < REL_CNT; i++) @@ -1596,6 +1605,7 @@ static NTSTATUS lnxev_device_create(struct udev_device *dev, int fd, const char if ((impl->is_gamepad = desc.is_gamepad)) { + static const int gamepad_axes[] = {1, 2, 5, 3, 4, 6}; static const UINT gamepad_buttons[] = { BTN_A, @@ -1615,9 +1625,18 @@ static NTSTATUS lnxev_device_create(struct udev_device *dev, int fd, const char BTN_TR2, }; + memset(impl->abs_map, 0, sizeof(impl->abs_map)); memset(impl->button_map, 0, sizeof(impl->button_map)); impl->button_count = 0; + for (int i = 0, count = 0; i < ABS_CNT; i++) + { + USAGE_AND_PAGE usage = absolute_usages[i]; + if (!usage.UsagePage || !usage.Usage) continue; + if (!test_bit(info.abs, i)) continue; + impl->abs_map[i] = gamepad_axes[count++]; + } + for (int i = 0; i < ARRAY_SIZE(gamepad_buttons); i++) { int button = gamepad_buttons[i]; From 1a05413e06b2635990b26aed27f627aa42c09751 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 22 Nov 2022 09:05:04 +0100 Subject: [PATCH 053/454] windows.gaming.input: Only create Gamepad instances for XInput devices. (cherry picked from commit 19a640ecd37acdfe3185daf358d2c10141e0897f) CW-Bug-Id: #25366 CW-Bug-Id: #25325 --- dlls/dinput/tests/joystick8.c | 1 - 1 file changed, 1 deletion(-) diff --git a/dlls/dinput/tests/joystick8.c b/dlls/dinput/tests/joystick8.c index 20c959cabb28..afc24e49c8fd 100644 --- a/dlls/dinput/tests/joystick8.c +++ b/dlls/dinput/tests/joystick8.c @@ -5173,7 +5173,6 @@ static void test_windows_gaming_input(void) ok( hr == S_OK, "get_Gamepads returned %#lx\n", hr ); hr = IVectorView_Gamepad_get_Size( gamepads_view, &size ); ok( hr == S_OK, "get_Size returned %#lx\n", hr ); - todo_wine /* but Wine currently intentionally does */ ok( size == 0, "got size %u\n", size ); IVectorView_Gamepad_Release( gamepads_view ); IGamepadStatics_Release( gamepad_statics ); From 4a6bdbee5e85031b9f056654841c35a976dc5928 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 4 Aug 2025 16:38:04 +0200 Subject: [PATCH 054/454] winebus: Improve gamepad report compatibility with XUSB / GIP. Inverting left/right trigger usages and Y values, so that WGI doesn't need to mess with axes order. (cherry picked from commit 6d08d32ef96cd71b452af6c3f3654037086b83e9) CW-Bug-Id: #25366 CW-Bug-Id: #25325 --- dlls/windows.gaming.input/gamepad.c | 4 ++-- dlls/winebus.sys/bus_sdl.c | 11 +++++++++-- dlls/winebus.sys/bus_udev.c | 1 + dlls/winebus.sys/hid.c | 6 ++++-- dlls/winexinput.sys/main.c | 4 ++-- dlls/xinput1_3/main.c | 4 ++-- 6 files changed, 20 insertions(+), 10 deletions(-) diff --git a/dlls/windows.gaming.input/gamepad.c b/dlls/windows.gaming.input/gamepad.c index def2c9dcf5ea..ba0944cd6dae 100644 --- a/dlls/windows.gaming.input/gamepad.c +++ b/dlls/windows.gaming.input/gamepad.c @@ -322,10 +322,10 @@ static HRESULT WINAPI gamepad_GetCurrentReading( IGamepad *iface, struct Gamepad } value->LeftThumbstickX = 2. * state.axes[0] - 1.; - value->LeftThumbstickY = 1. - 2. * state.axes[1]; + value->LeftThumbstickY = 2. * state.axes[1] - 1.; value->LeftTrigger = state.axes[2]; value->RightThumbstickX = 2. * state.axes[3] - 1.; - value->RightThumbstickY = 1. - 2. * state.axes[4]; + value->RightThumbstickY = 2. * state.axes[4] - 1.; value->RightTrigger = state.axes[5]; value->Timestamp = state.timestamp; diff --git a/dlls/winebus.sys/bus_sdl.c b/dlls/winebus.sys/bus_sdl.c index 5f13f6daa8ab..8b3afd48893d 100644 --- a/dlls/winebus.sys/bus_sdl.c +++ b/dlls/winebus.sys/bus_sdl.c @@ -416,9 +416,13 @@ static NTSTATUS build_controller_report_descriptor(struct unix_device *iface) if (!descriptor_add_haptic(impl, FALSE)) return STATUS_NO_MEMORY; if (!hid_device_end_report_descriptor(iface)) return STATUS_NO_MEMORY; - /* Initialize axis in the report */ for (int i = SDL_CONTROLLER_AXIS_LEFTX; i < SDL_CONTROLLER_AXIS_MAX; i++) - hid_device_set_abs_axis(iface, i, pSDL_GameControllerGetAxis(impl->sdl_controller, i)); + { + int value = pSDL_GameControllerGetAxis(impl->sdl_controller, i); + if (i == SDL_CONTROLLER_AXIS_LEFTY || i == SDL_CONTROLLER_AXIS_RIGHTY) + value = -value - 1; /* match XUSB / GIP protocol */ + hid_device_set_abs_axis(iface, i, value); + } state = pSDL_GameControllerGetButton(impl->sdl_controller, SDL_CONTROLLER_BUTTON_DPAD_UP); hid_device_move_hatswitch(iface, 0, 0, state ? -1 : +1); @@ -907,6 +911,9 @@ static BOOL set_report_from_controller_event(struct sdl_device *impl, SDL_Event { SDL_ControllerAxisEvent *ie = &event->caxis; + if (ie->axis == SDL_CONTROLLER_AXIS_LEFTY || ie->axis == SDL_CONTROLLER_AXIS_RIGHTY) + ie->value = -ie->value - 1; /* match XUSB / GIP protocol */ + hid_device_set_abs_axis(iface, ie->axis, ie->value); bus_event_queue_input_report(&event_queue, iface, state->report_buf, state->report_len); break; diff --git a/dlls/winebus.sys/bus_udev.c b/dlls/winebus.sys/bus_udev.c index 478a0ed54e7f..2bf9bb20946b 100644 --- a/dlls/winebus.sys/bus_udev.c +++ b/dlls/winebus.sys/bus_udev.c @@ -848,6 +848,7 @@ static void set_abs_axis_value(struct unix_device *iface, int code, int value) { double scale = (code == 5 || code == 6 ? 32767.0 : 65535.0) / range; value = (value - min) * scale - (code == 5 || code == 6 ? 0 : 32768); + if (code == 2 || code == 4) value = -value - 1; /* match XUSB / GIP protocol */ } hid_device_set_abs_axis(iface, code - 1, value); diff --git a/dlls/winebus.sys/hid.c b/dlls/winebus.sys/hid.c index 693697fa8c00..bb1ddbdabdb9 100644 --- a/dlls/winebus.sys/hid.c +++ b/dlls/winebus.sys/hid.c @@ -348,12 +348,14 @@ BOOL hid_device_add_gamepad(struct unix_device *iface) static const USAGE_AND_PAGE device_usage = {.UsagePage = HID_USAGE_PAGE_GENERIC, .Usage = HID_USAGE_GENERIC_GAMEPAD}; static const USAGE left[] = {HID_USAGE_GENERIC_X, HID_USAGE_GENERIC_Y}; static const USAGE right[] = {HID_USAGE_GENERIC_RX, HID_USAGE_GENERIC_RY}; - static const USAGE trigger[] = {HID_USAGE_GENERIC_Z, HID_USAGE_GENERIC_RZ}; + static const USAGE lt = HID_USAGE_GENERIC_Z; + static const USAGE rt = HID_USAGE_GENERIC_RZ; if (!hid_device_begin_input_report(iface, &device_usage)) return FALSE; if (!hid_device_add_axes(iface, 2, HID_USAGE_PAGE_GENERIC, left, FALSE, -32768, 32767)) return FALSE; if (!hid_device_add_axes(iface, 2, HID_USAGE_PAGE_GENERIC, right, FALSE, -32768, 32767)) return FALSE; - if (!hid_device_add_axes(iface, 2, HID_USAGE_PAGE_GENERIC, trigger, FALSE, 0, 32767)) return FALSE; + if (!hid_device_add_axes(iface, 1, HID_USAGE_PAGE_GENERIC, <, FALSE, 0, 32767)) return FALSE; + if (!hid_device_add_axes(iface, 1, HID_USAGE_PAGE_GENERIC, &rt, FALSE, 0, 32767)) return FALSE; if (!hid_device_add_hatswitch(iface, 1)) return FALSE; if (!hid_device_add_buttons(iface, HID_USAGE_PAGE_BUTTON, 1, 15)) return FALSE; if (!hid_device_end_input_report(iface)) return FALSE; diff --git a/dlls/winexinput.sys/main.c b/dlls/winexinput.sys/main.c index 45c6835bfe7a..48743d6adf67 100644 --- a/dlls/winexinput.sys/main.c +++ b/dlls/winexinput.sys/main.c @@ -236,9 +236,9 @@ static void translate_report_to_xinput_state(struct func_device *fdo) fdo->xinput_state.buttons |= (1 << (usages[i] - 1)); } fdo->xinput_state.lx_axis = scale_value(lx, &fdo->lx_caps, 0, 65535); - fdo->xinput_state.ly_axis = scale_value(ly, &fdo->ly_caps, 0, 65535); + fdo->xinput_state.ly_axis = scale_value(-ly - 1, &fdo->ly_caps, 0, 65535); fdo->xinput_state.rx_axis = scale_value(rx, &fdo->rx_caps, 0, 65535); - fdo->xinput_state.ry_axis = scale_value(ry, &fdo->ry_caps, 0, 65535); + fdo->xinput_state.ry_axis = scale_value(-ry - 1, &fdo->ry_caps, 0, 65535); rt = scale_value(rt, &fdo->rt_caps, 0, 255); lt = scale_value(lt, &fdo->lt_caps, 0, 255); fdo->xinput_state.trigger = 0x8000 + (lt - rt) * 128; diff --git a/dlls/xinput1_3/main.c b/dlls/xinput1_3/main.c index 92cd439f6b9f..24e0eef941c1 100644 --- a/dlls/xinput1_3/main.c +++ b/dlls/xinput1_3/main.c @@ -646,7 +646,7 @@ static void read_controller_state(struct xinput_controller *controller) status = HidP_GetUsageValue(HidP_Input, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_Y, &value, controller->hid.preparsed, report_buf, report_len); if (status != HIDP_STATUS_SUCCESS) WARN("HidP_GetUsageValue HID_USAGE_PAGE_GENERIC / HID_USAGE_GENERIC_Y returned %#lx\n", status); - else state.Gamepad.sThumbLY = -scale_value(value, &controller->hid.ly_caps, -32768, 32767) - 1; + else state.Gamepad.sThumbLY = scale_value(value, &controller->hid.ly_caps, -32768, 32767); status = HidP_GetUsageValue(HidP_Input, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_RX, &value, controller->hid.preparsed, report_buf, report_len); if (status != HIDP_STATUS_SUCCESS) WARN("HidP_GetUsageValue HID_USAGE_PAGE_GENERIC / HID_USAGE_GENERIC_RX returned %#lx\n", status); @@ -654,7 +654,7 @@ static void read_controller_state(struct xinput_controller *controller) status = HidP_GetUsageValue(HidP_Input, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_RY, &value, controller->hid.preparsed, report_buf, report_len); if (status != HIDP_STATUS_SUCCESS) WARN("HidP_GetUsageValue HID_USAGE_PAGE_GENERIC / HID_USAGE_GENERIC_RY returned %#lx\n", status); - else state.Gamepad.sThumbRY = -scale_value(value, &controller->hid.ry_caps, -32768, 32767) - 1; + else state.Gamepad.sThumbRY = scale_value(value, &controller->hid.ry_caps, -32768, 32767); status = HidP_GetUsageValue(HidP_Input, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_RZ, &value, controller->hid.preparsed, report_buf, report_len); if (status != HIDP_STATUS_SUCCESS) WARN("HidP_GetUsageValue HID_USAGE_PAGE_GENERIC / HID_USAGE_GENERIC_RZ returned %#lx\n", status); From 341970f3adc23bd2ec718042f1a369c8e0c81277 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 4 Aug 2025 16:37:35 +0200 Subject: [PATCH 055/454] windows.gaming.input: Use a generic dinput device data format. To avoid the builtin axis mapping that c_dfDIJoystick2 does, we need IRawGameController to expose the axes / povs / buttons in their raw order. (cherry picked from commit 15c02e64e284d83f11b10894592e0337df62cfdc) CW-Bug-Id: #25366 CW-Bug-Id: #25325 --- dlls/windows.gaming.input/gamepad.c | 8 +- dlls/windows.gaming.input/provider.c | 156 ++++++++++++++++++++++++++- 2 files changed, 159 insertions(+), 5 deletions(-) diff --git a/dlls/windows.gaming.input/gamepad.c b/dlls/windows.gaming.input/gamepad.c index ba0944cd6dae..ecac632ec3bc 100644 --- a/dlls/windows.gaming.input/gamepad.c +++ b/dlls/windows.gaming.input/gamepad.c @@ -321,11 +321,11 @@ static HRESULT WINAPI gamepad_GetCurrentReading( IGamepad *iface, struct Gamepad break; } - value->LeftThumbstickX = 2. * state.axes[0] - 1.; - value->LeftThumbstickY = 2. * state.axes[1] - 1.; - value->LeftTrigger = state.axes[2]; + value->LeftThumbstickX = 2. * state.axes[1] - 1.; + value->LeftThumbstickY = 2. * state.axes[0] - 1.; + value->LeftTrigger = state.axes[4]; value->RightThumbstickX = 2. * state.axes[3] - 1.; - value->RightThumbstickY = 2. * state.axes[4] - 1.; + value->RightThumbstickY = 2. * state.axes[2] - 1.; value->RightTrigger = state.axes[5]; value->Timestamp = state.timestamp; diff --git a/dlls/windows.gaming.input/provider.c b/dlls/windows.gaming.input/provider.c index 7c8dea0df8b7..d12785dee4c8 100644 --- a/dlls/windows.gaming.input/provider.c +++ b/dlls/windows.gaming.input/provider.c @@ -618,6 +618,160 @@ static void open_haptics_device( struct provider *provider ) CloseHandle( device ); } +static const DIOBJECTDATAFORMAT data_format_objs[] = +{ + {NULL,DIJOFS_X,DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_Y,DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_Z,DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_RX,DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_RY,DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_RZ,DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_SLIDER(0),DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_SLIDER(1),DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_POV(0),DIDFT_OPTIONAL|DIDFT_POV|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_POV(1),DIDFT_OPTIONAL|DIDFT_POV|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_POV(2),DIDFT_OPTIONAL|DIDFT_POV|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_POV(3),DIDFT_OPTIONAL|DIDFT_POV|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(0),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(1),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(2),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(3),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(4),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(5),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(6),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(7),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(8),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(9),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(10),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(11),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(12),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(13),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(14),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(15),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(16),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(17),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(18),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(19),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(20),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(21),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(22),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(23),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(24),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(25),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(26),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(27),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(28),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(29),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(30),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(31),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(32),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(33),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(34),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(35),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(36),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(37),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(38),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(39),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(40),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(41),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(42),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(43),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(44),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(45),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(46),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(47),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(48),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(49),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(50),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(51),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(52),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(53),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(54),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(55),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(56),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(57),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(58),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(59),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(60),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(61),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(62),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(63),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(64),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(65),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(66),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(67),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(68),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(69),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(70),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(71),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(72),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(73),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(74),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(75),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(76),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(77),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(78),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(79),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(80),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(81),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(82),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(83),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(84),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(85),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(86),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(87),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(88),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(89),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(90),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(91),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(92),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(93),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(94),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(95),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(96),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(97),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(98),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(99),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(100),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(101),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(102),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(103),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(104),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(105),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(106),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(107),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(108),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(109),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(110),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(111),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(112),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(113),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(114),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(115),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(116),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(117),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(118),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(119),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(120),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(121),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(122),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(123),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(124),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(125),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(126),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(127),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, +}; + +static const DIDATAFORMAT data_format = +{ + sizeof(DIDATAFORMAT), + sizeof(DIOBJECTDATAFORMAT), + DIDF_ABSAXIS, + sizeof(DIJOYSTATE2), + ARRAY_SIZE(data_format_objs), + (LPDIOBJECTDATAFORMAT)data_format_objs +}; + void provider_create( const WCHAR *device_path ) { IDirectInputDevice8W *dinput_device; @@ -642,7 +796,7 @@ void provider_create( const WCHAR *device_path ) if (FAILED(hr)) return; if (FAILED(hr = IDirectInputDevice8_SetCooperativeLevel( dinput_device, 0, DISCL_BACKGROUND | DISCL_NONEXCLUSIVE ))) goto done; - if (FAILED(hr = IDirectInputDevice8_SetDataFormat( dinput_device, &c_dfDIJoystick2 ))) goto done; + if (FAILED(hr = IDirectInputDevice8_SetDataFormat( dinput_device, &data_format ))) goto done; if (FAILED(hr = IDirectInputDevice8_Acquire( dinput_device ))) goto done; if (!(impl = calloc( 1, sizeof(*impl) ))) goto done; From eba30c5bd4a4cd0ca0e5882945327451cc6aaef1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 4 Aug 2025 16:38:04 +0200 Subject: [PATCH 056/454] winebus: Use a vendor specific usage for gamepad guide buttons. To avoid exposing it with the other buttons, the XUSB / GIP frontends don't expose it to applications (for instance in Windows.Gaming.Input). (cherry picked from commit 9206673e9d4a60511ef22b6890695b053a60f7cb) CW-Bug-Id: #25366 CW-Bug-Id: #25325 --- dlls/dinput/joystick_hid.c | 2 +- dlls/winebus.sys/bus_sdl.c | 30 ++++++++++++++++-------------- dlls/winebus.sys/bus_udev.c | 18 ++++++++++-------- dlls/winebus.sys/hid.c | 3 ++- dlls/xinput1_3/main.c | 6 +++++- 5 files changed, 34 insertions(+), 25 deletions(-) diff --git a/dlls/dinput/joystick_hid.c b/dlls/dinput/joystick_hid.c index f225e7578ca0..2f321fd471b5 100644 --- a/dlls/dinput/joystick_hid.c +++ b/dlls/dinput/joystick_hid.c @@ -1404,7 +1404,7 @@ static HRESULT hid_joystick_read( IDirectInputDevice8W *iface ) { usages = impl->usages_buf + count; if (usages->UsagePage != HID_USAGE_PAGE_BUTTON) - FIXME( "unimplemented usage page %x.\n", usages->UsagePage ); + WARN( "unimplemented usage page %x.\n", usages->UsagePage ); else if (usages->Usage >= 128) FIXME( "ignoring extraneous button %d.\n", usages->Usage ); else diff --git a/dlls/winebus.sys/bus_sdl.c b/dlls/winebus.sys/bus_sdl.c index 8b3afd48893d..63c9c5663bcb 100644 --- a/dlls/winebus.sys/bus_sdl.c +++ b/dlls/winebus.sys/bus_sdl.c @@ -880,29 +880,31 @@ static BOOL set_report_from_controller_event(struct sdl_device *impl, SDL_Event SDL_ControllerButtonEvent *ie = &event->cbutton; int button; - switch ((button = ie->button)) + switch (ie->button) { - case SDL_CONTROLLER_BUTTON_DPAD_UP: - hid_device_move_hatswitch(iface, 0, 0, ie->state ? -1 : +1); - break; - case SDL_CONTROLLER_BUTTON_DPAD_DOWN: - hid_device_move_hatswitch(iface, 0, 0, ie->state ? +1 : -1); - break; - case SDL_CONTROLLER_BUTTON_DPAD_LEFT: - hid_device_move_hatswitch(iface, 0, ie->state ? -1 : +1, 0); - break; - case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: - hid_device_move_hatswitch(iface, 0, ie->state ? +1 : -1, 0); - break; + case SDL_CONTROLLER_BUTTON_A: button = 0; break; + case SDL_CONTROLLER_BUTTON_B: button = 1; break; + case SDL_CONTROLLER_BUTTON_X: button = 2; break; + case SDL_CONTROLLER_BUTTON_Y: button = 3; break; case SDL_CONTROLLER_BUTTON_LEFTSHOULDER: button = 4; break; case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER: button = 5; break; case SDL_CONTROLLER_BUTTON_BACK: button = 6; break; case SDL_CONTROLLER_BUTTON_START: button = 7; break; case SDL_CONTROLLER_BUTTON_LEFTSTICK: button = 8; break; case SDL_CONTROLLER_BUTTON_RIGHTSTICK: button = 9; break; - case SDL_CONTROLLER_BUTTON_GUIDE: button = 10; break; + case SDL_CONTROLLER_BUTTON_DPAD_UP: button = 10; break; + case SDL_CONTROLLER_BUTTON_DPAD_DOWN: button = 11; break; + case SDL_CONTROLLER_BUTTON_DPAD_LEFT: button = 12; break; + case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: button = 13; break; + case SDL_CONTROLLER_BUTTON_GUIDE: button = 16; break; + default: button = -1; break; } + if (button == -1) break; + if (button == 10) hid_device_move_hatswitch(iface, 0, 0, ie->state ? -1 : +1); + if (button == 11) hid_device_move_hatswitch(iface, 0, 0, ie->state ? +1 : -1); + if (button == 12) hid_device_move_hatswitch(iface, 0, ie->state ? -1 : +1, 0); + if (button == 13) hid_device_move_hatswitch(iface, 0, ie->state ? +1 : -1, 0); hid_device_set_button(iface, button, ie->state); bus_event_queue_input_report(&event_queue, iface, state->report_buf, state->report_len); break; diff --git a/dlls/winebus.sys/bus_udev.c b/dlls/winebus.sys/bus_udev.c index 2bf9bb20946b..378d20223b3a 100644 --- a/dlls/winebus.sys/bus_udev.c +++ b/dlls/winebus.sys/bus_udev.c @@ -858,8 +858,8 @@ static void set_abs_axis_value(struct unix_device *iface, int code, int value) if (!(code = impl->hat_map[code - ABS_HAT0X])) return; if (impl->is_gamepad) { - hid_device_set_button(iface, 11, value < 0); - hid_device_set_button(iface, 12, value > 0); + hid_device_set_button(iface, 10, value < 0); + hid_device_set_button(iface, 11, value > 0); } hid_device_set_hatswitch_y(iface, code - 1, value); } @@ -868,8 +868,8 @@ static void set_abs_axis_value(struct unix_device *iface, int code, int value) if (!(code = impl->hat_map[code - ABS_HAT0X])) return; if (impl->is_gamepad) { - hid_device_set_button(iface, 13, value < 0); - hid_device_set_button(iface, 14, value > 0); + hid_device_set_button(iface, 12, value < 0); + hid_device_set_button(iface, 13, value > 0); } hid_device_set_hatswitch_x(iface, code - 1, value); } @@ -990,10 +990,10 @@ static BOOL set_report_from_event(struct unix_device *iface, struct input_event if (!(button = impl->button_map[ie->code])) return FALSE; if (impl->is_gamepad && !impl->hat_count) { - if (button == 12) hid_device_set_hatswitch_y(iface, 0, -1); - if (button == 13) hid_device_set_hatswitch_y(iface, 0, +1); - if (button == 14) hid_device_set_hatswitch_x(iface, 0, -1); - if (button == 15) hid_device_set_hatswitch_x(iface, 0, +1); + if (button == 11) hid_device_set_hatswitch_y(iface, 0, -1); + if (button == 12) hid_device_set_hatswitch_y(iface, 0, +1); + if (button == 13) hid_device_set_hatswitch_x(iface, 0, -1); + if (button == 14) hid_device_set_hatswitch_x(iface, 0, +1); } hid_device_set_button(iface, button - 1, ie->value); return FALSE; @@ -1644,6 +1644,7 @@ static NTSTATUS lnxev_device_create(struct udev_device *dev, int fd, const char if (!test_bit(info.key, button)) continue; if (impl->button_count > (impl->hat_count ? 10 : 14)) break; impl->button_map[button] = ++impl->button_count; + if (impl->hat_count && impl->button_count == 11) impl->button_map[button] = 17; } for (int i = BTN_MISC; i < KEY_MAX; i++) @@ -1652,6 +1653,7 @@ static NTSTATUS lnxev_device_create(struct udev_device *dev, int fd, const char if (impl->button_count > (impl->hat_count ? 10 : 14)) break; if (!test_bit(info.key, i)) continue; impl->button_map[i] = ++impl->button_count; + if (impl->hat_count && impl->button_count == 11) impl->button_map[i] = 17; } } diff --git a/dlls/winebus.sys/hid.c b/dlls/winebus.sys/hid.c index bb1ddbdabdb9..ee1c9a9de079 100644 --- a/dlls/winebus.sys/hid.c +++ b/dlls/winebus.sys/hid.c @@ -357,7 +357,8 @@ BOOL hid_device_add_gamepad(struct unix_device *iface) if (!hid_device_add_axes(iface, 1, HID_USAGE_PAGE_GENERIC, <, FALSE, 0, 32767)) return FALSE; if (!hid_device_add_axes(iface, 1, HID_USAGE_PAGE_GENERIC, &rt, FALSE, 0, 32767)) return FALSE; if (!hid_device_add_hatswitch(iface, 1)) return FALSE; - if (!hid_device_add_buttons(iface, HID_USAGE_PAGE_BUTTON, 1, 15)) return FALSE; + if (!hid_device_add_buttons(iface, HID_USAGE_PAGE_BUTTON, 1, 14)) return FALSE; + if (!hid_device_add_buttons(iface, HID_USAGE_PAGE_VENDOR_DEFINED_BEGIN, 1, 8)) return FALSE; if (!hid_device_end_input_report(iface)) return FALSE; return TRUE; diff --git a/dlls/xinput1_3/main.c b/dlls/xinput1_3/main.c index 24e0eef941c1..ecd9a2bde720 100644 --- a/dlls/xinput1_3/main.c +++ b/dlls/xinput1_3/main.c @@ -618,10 +618,14 @@ static void read_controller_state(struct xinput_controller *controller) case 8: state.Gamepad.wButtons |= XINPUT_GAMEPAD_START; break; case 9: state.Gamepad.wButtons |= XINPUT_GAMEPAD_LEFT_THUMB; break; case 10: state.Gamepad.wButtons |= XINPUT_GAMEPAD_RIGHT_THUMB; break; - case 11: state.Gamepad.wButtons |= XINPUT_GAMEPAD_GUIDE; break; } } + button_length = ARRAY_SIZE(buttons); + status = HidP_GetUsages(HidP_Input, HID_USAGE_PAGE_VENDOR_DEFINED_BEGIN, 0, buttons, &button_length, controller->hid.preparsed, report_buf, report_len); + if (status != HIDP_STATUS_SUCCESS) WARN("HidP_GetUsages HID_USAGE_PAGE_VENDOR_DEFINED_BEGIN returned %#lx\n", status); + if (button_length) state.Gamepad.wButtons |= XINPUT_GAMEPAD_GUIDE; + status = HidP_GetUsageValue(HidP_Input, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_HATSWITCH, &value, controller->hid.preparsed, report_buf, report_len); if (status != HIDP_STATUS_SUCCESS) WARN("HidP_GetUsageValue HID_USAGE_PAGE_GENERIC / HID_USAGE_GENERIC_HATSWITCH returned %#lx\n", status); else switch (value) From 18a51c4c2645df0906173b99411b0d546ff59fa5 Mon Sep 17 00:00:00 2001 From: Arkadiusz Hiler Date: Sun, 17 Nov 2024 11:35:14 +0100 Subject: [PATCH 057/454] winebus: Add Logitech G920 mapping to the SDL backend. The HID input report format is different between Linux (hid_logitech_hidpp) and Windows when it comes to axis HID usages. To correct that we can use the device through SDL and craft our own HID mapping. (cherry picked from commit 6a60075eff1577c1d56823e3c71f497784e70ec9) CW-Bug-Id: #25366 --- dlls/winebus.sys/bus_sdl.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/dlls/winebus.sys/bus_sdl.c b/dlls/winebus.sys/bus_sdl.c index 63c9c5663bcb..9278f1c41ef6 100644 --- a/dlls/winebus.sys/bus_sdl.c +++ b/dlls/winebus.sys/bus_sdl.c @@ -284,9 +284,9 @@ static const USAGE_AND_PAGE relative_axis_usages[] = {.UsagePage = HID_USAGE_PAGE_GENERIC, .Usage = HID_USAGE_GENERIC_WHEEL}, }; -static int get_absolute_usages(struct sdl_device *impl, const USAGE_AND_PAGE **absolute_usages) +static int get_absolute_usages(const struct device_desc *desc, const USAGE_AND_PAGE **absolute_usages) { - if (pSDL_JoystickGetVendor(impl->sdl_joystick) == 0x046D && pSDL_JoystickGetProduct(impl->sdl_joystick) == 0xC262) + if (desc->vid == 0x046d && desc->pid == 0xc262) { *absolute_usages = g920_absolute_usages; return ARRAY_SIZE(g920_absolute_usages); @@ -296,7 +296,7 @@ static int get_absolute_usages(struct sdl_device *impl, const USAGE_AND_PAGE **a return ARRAY_SIZE(absolute_axis_usages); } -static NTSTATUS build_joystick_report_descriptor(struct unix_device *iface) +static NTSTATUS build_joystick_report_descriptor(struct unix_device *iface, const struct device_desc *desc) { const USAGE_AND_PAGE device_usage = {.UsagePage = HID_USAGE_PAGE_GENERIC, .Usage = HID_USAGE_GENERIC_JOYSTICK}; struct sdl_device *impl = impl_from_unix_device(iface); @@ -304,7 +304,7 @@ static NTSTATUS build_joystick_report_descriptor(struct unix_device *iface) USAGE_AND_PAGE physical_usage; const USAGE_AND_PAGE *absolute_usages = NULL; - size_t absolute_usages_count = get_absolute_usages(impl, &absolute_usages); + size_t absolute_usages_count = get_absolute_usages(desc, &absolute_usages); axis_count = pSDL_JoystickNumAxes(impl->sdl_joystick); if (options->split_controllers) axis_count = min(6, axis_count - impl->axis_offset); @@ -1045,7 +1045,7 @@ static void sdl_add_device(unsigned int index) impl->axis_offset = axis_offset; if (impl->sdl_controller) status = build_controller_report_descriptor(&impl->unix_device); - else status = build_joystick_report_descriptor(&impl->unix_device); + else status = build_joystick_report_descriptor(&impl->unix_device, &desc); if (status) { list_remove(&impl->unix_device.entry); From b7133c200df47bc39d8c41d1d323239589a06466 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 20 Jan 2025 22:27:19 +0100 Subject: [PATCH 058/454] winebus: Create dedicated threads to write evdev haptics output reports. Some controllers block on ioctl and write calls, and this may propagate up to the frontend through various blocking calls, including when the frontend API is using async I/O because of how Wine implements async I/O for device objects. (cherry picked from commit 74fdd1ae03298a1f226792d1bada465fe7a3a3cc) CW-Bug-Id: #25366 --- dlls/winebus.sys/bus_udev.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/dlls/winebus.sys/bus_udev.c b/dlls/winebus.sys/bus_udev.c index 378d20223b3a..625a1c6d712f 100644 --- a/dlls/winebus.sys/bus_udev.c +++ b/dlls/winebus.sys/bus_udev.c @@ -200,7 +200,6 @@ struct lnxev_device int button_count; BOOL is_gamepad; - int haptics_state; pthread_cond_t haptics_cond; pthread_t haptics_thread; struct ff_effect haptics; @@ -1026,18 +1025,17 @@ static void lnxev_device_destroy(struct unix_device *iface) static void *lnxev_device_haptics_thread(void *args) { struct lnxev_device *impl = lnxev_impl_from_unix_device(args); + struct ff_effect effect = {0}; pthread_mutex_lock(&udev_cs); for (;;) { - struct ff_effect effect; - - while (impl->haptics_state == 1) pthread_cond_wait(&impl->haptics_cond, &udev_cs); - if (!impl->haptics_state) break; + while (!memcmp(&effect, &impl->haptics, sizeof(effect))) + pthread_cond_wait(&impl->haptics_cond, &udev_cs); + if (impl->haptics.type == (__u16)-1) break; effect = impl->haptics; - impl->haptics_state = 1; pthread_mutex_unlock(&udev_cs); if (effect.type && (effect.id == -1 || ioctl(impl->base.device_fd, EVIOCSFF, &effect) == -1)) @@ -1066,7 +1064,7 @@ static NTSTATUS lnxev_device_start(struct unix_device *iface) pthread_mutex_lock(&udev_cs); start_polling_device(iface); - impl->haptics_state = 1; + impl->haptics.type = 0; pthread_mutex_unlock(&udev_cs); pthread_cond_init(&impl->haptics_cond, NULL); @@ -1081,7 +1079,7 @@ static void lnxev_device_stop(struct unix_device *iface) pthread_mutex_lock(&udev_cs); stop_polling_device(iface); list_remove(&impl->base.unix_device.entry); - impl->haptics_state = 0; + impl->haptics.type = -1; pthread_mutex_unlock(&udev_cs); pthread_cond_signal(&impl->haptics_cond); @@ -1119,7 +1117,6 @@ static NTSTATUS lnxev_device_haptics_start(struct unix_device *iface, UINT durat impl->haptics.replay.length = duration_ms; impl->haptics.u.rumble.strong_magnitude = rumble_intensity; impl->haptics.u.rumble.weak_magnitude = buzz_intensity; - impl->haptics_state = 2; pthread_mutex_unlock(&udev_cs); pthread_cond_signal(&impl->haptics_cond); @@ -1137,7 +1134,6 @@ static NTSTATUS lnxev_device_haptics_stop(struct unix_device *iface) impl->haptics.replay.length = 0; impl->haptics.u.rumble.strong_magnitude = 0; impl->haptics.u.rumble.weak_magnitude = 0; - impl->haptics_state = 2; pthread_mutex_unlock(&udev_cs); pthread_cond_signal(&impl->haptics_cond); From a19c966d09009ab153677adbbabb6dcf7493703e Mon Sep 17 00:00:00 2001 From: Gerald Pfeifer Date: Fri, 15 Aug 2025 23:57:58 +0200 Subject: [PATCH 059/454] winebus.sys: Use uint16_t instead of __u16. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This addresses a regression introduced by commit e603bbf69c7 where we'd get dlls/winebus.sys/bus_udev.c: In function ‘lnxev_device_haptics_thread’: dlls/winebus.sys/bus_udev.c:711:36: error: ‘__u16’ undeclared on FreeBSD 13. (cherry picked from commit 765fff593c58569c56d924db89876febc9a0208b) --- dlls/winebus.sys/bus_udev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/winebus.sys/bus_udev.c b/dlls/winebus.sys/bus_udev.c index 625a1c6d712f..a324f49d2d74 100644 --- a/dlls/winebus.sys/bus_udev.c +++ b/dlls/winebus.sys/bus_udev.c @@ -1033,7 +1033,7 @@ static void *lnxev_device_haptics_thread(void *args) { while (!memcmp(&effect, &impl->haptics, sizeof(effect))) pthread_cond_wait(&impl->haptics_cond, &udev_cs); - if (impl->haptics.type == (__u16)-1) break; + if (impl->haptics.type == (uint16_t)-1) break; effect = impl->haptics; pthread_mutex_unlock(&udev_cs); From decd8da1073840e9eb20df0577b7b235abe5df43 Mon Sep 17 00:00:00 2001 From: Brendan Shanks Date: Tue, 29 Jul 2025 17:35:45 -0700 Subject: [PATCH 060/454] xinput1_3: Correctly handle a NULL GUID parameter in XInputGetDSoundAudioDeviceGuids(). (cherry picked from commit f42b00e96ac2b4c462507e660a48a76114244a9c) CW-Bug-Id: #25652 --- dlls/xinput1_3/main.c | 2 +- dlls/xinput1_3/tests/xinput.c | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/dlls/xinput1_3/main.c b/dlls/xinput1_3/main.c index ecd9a2bde720..c2ae05a0ce84 100644 --- a/dlls/xinput1_3/main.c +++ b/dlls/xinput1_3/main.c @@ -1131,7 +1131,7 @@ DWORD WINAPI DECLSPEC_HOTPATCH XInputGetDSoundAudioDeviceGuids(DWORD index, GUID FIXME("index %lu, render_guid %s, capture_guid %s stub!\n", index, debugstr_guid(render_guid), debugstr_guid(capture_guid)); - if (index >= XUSER_MAX_COUNT) return ERROR_BAD_ARGUMENTS; + if (index >= XUSER_MAX_COUNT || !render_guid || !capture_guid) return ERROR_BAD_ARGUMENTS; if (!controllers[index].device) return ERROR_DEVICE_NOT_CONNECTED; return ERROR_NOT_SUPPORTED; diff --git a/dlls/xinput1_3/tests/xinput.c b/dlls/xinput1_3/tests/xinput.c index 5c31f0be1fe2..71905dc5a409 100644 --- a/dlls/xinput1_3/tests/xinput.c +++ b/dlls/xinput1_3/tests/xinput.c @@ -255,6 +255,12 @@ static void test_get_dsoundaudiodevice(void) trace("Headset microphone not attached\n"); } + result = pXInputGetDSoundAudioDeviceGuids(0, NULL, &soundCapture); + ok(result == ERROR_BAD_ARGUMENTS, "XInputGetDSoundAudioDeviceGuids returned %lu\n", result); + + result = pXInputGetDSoundAudioDeviceGuids(0, &soundRender, NULL); + ok(result == ERROR_BAD_ARGUMENTS, "XInputGetDSoundAudioDeviceGuids returned %lu\n", result); + result = pXInputGetDSoundAudioDeviceGuids(XUSER_MAX_COUNT+1, &soundRender, &soundCapture); ok(result == ERROR_BAD_ARGUMENTS, "XInputGetDSoundAudioDeviceGuids returned %lu\n", result); } From bf9e63f17642caf7f269da577849e8f9a35526a8 Mon Sep 17 00:00:00 2001 From: Brendan Shanks Date: Tue, 29 Jul 2025 16:15:38 -0700 Subject: [PATCH 061/454] xinput9_1_0: Implement by dynamically loading and calling xinput1_4.dll. This matches how the native DLL is implemented, and fixes Steam Input in games that use xinput9_1_0. (Steam Input hooks xinput1_{1-4} but not 9_1_0, so relies on 9_1_0 calling into 1_4). (cherry picked from commit 870860b5d0059e2519b8ba25a9d5ef1826bfea1e) CW-Bug-Id: #25652 --- dlls/xinput9_1_0/Makefile.in | 2 - dlls/xinput9_1_0/main.c | 135 +++++++++++++++++++++++++++++++++++ 2 files changed, 135 insertions(+), 2 deletions(-) create mode 100644 dlls/xinput9_1_0/main.c diff --git a/dlls/xinput9_1_0/Makefile.in b/dlls/xinput9_1_0/Makefile.in index d5f2adfa3ff2..fe89cfc4b6e8 100644 --- a/dlls/xinput9_1_0/Makefile.in +++ b/dlls/xinput9_1_0/Makefile.in @@ -1,6 +1,4 @@ MODULE = xinput9_1_0.dll -IMPORTS = hid setupapi advapi32 user32 -PARENTSRC = ../xinput1_3 SOURCES = \ main.c \ diff --git a/dlls/xinput9_1_0/main.c b/dlls/xinput9_1_0/main.c new file mode 100644 index 000000000000..8c0259d1c609 --- /dev/null +++ b/dlls/xinput9_1_0/main.c @@ -0,0 +1,135 @@ +/* + * The Wine project - Xinput Joystick Library + * Copyright 2025 Brendan Shanks for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include +#include +#include +#include + +#include "windef.h" +#include "winbase.h" +#include "winerror.h" +#include "winuser.h" +#include "winreg.h" +#include "wingdi.h" +#include "winnls.h" +#include "winternl.h" + +#include "xinput.h" + +#include "wine/debug.h" + +#include "initguid.h" +DEFINE_GUID(GUID_NULL,0,0,0,0,0,0,0,0,0,0,0); + +WINE_DEFAULT_DEBUG_CHANNEL(xinput); + +static INIT_ONCE init_xinput1_4_once = INIT_ONCE_STATIC_INIT; +static HMODULE xinput1_4; +static DWORD (WINAPI *pXInputGetCapabilities)(DWORD,DWORD,XINPUT_CAPABILITIES*); +static DWORD (WINAPI *pXInputGetState)(DWORD, XINPUT_STATE*); +static DWORD (WINAPI *pXInputSetState)(DWORD, XINPUT_VIBRATION*); + +static BOOL WINAPI init_xinput1_4_funcs(INIT_ONCE *once, void *param, void **context) +{ + TRACE("xinput9_1_0: Loading functions from xinput1_4.dll\n"); + + xinput1_4 = LoadLibraryExW(L"xinput1_4.dll", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32); + if (xinput1_4) + { + pXInputGetCapabilities = (void*)GetProcAddress(xinput1_4, "XInputGetCapabilities"); + pXInputGetState = (void*)GetProcAddress(xinput1_4, "XInputGetState"); + pXInputSetState = (void*)GetProcAddress(xinput1_4, "XInputSetState"); + + if (!pXInputGetCapabilities) + ERR("Unable to get XInputGetCapabilities from xinput1_4\n"); + if (!pXInputGetState) + ERR("Unable to get XInputGetState from xinput1_4\n"); + if (!pXInputSetState) + ERR("Unable to get XInputSetState from xinput1_4\n"); + } + else + ERR("Unable to load xinput1_4.dll\n"); + + return TRUE; +} + +DWORD WINAPI DECLSPEC_HOTPATCH XInputGetCapabilities(DWORD index, DWORD flags, XINPUT_CAPABILITIES *capabilities) +{ + InitOnceExecuteOnce(&init_xinput1_4_once, init_xinput1_4_funcs, NULL, NULL); + + if (!pXInputGetCapabilities) return ERROR_DEVICE_NOT_CONNECTED; + return pXInputGetCapabilities(index, flags, capabilities); +} + +DWORD WINAPI DECLSPEC_HOTPATCH XInputGetDSoundAudioDeviceGuids(DWORD index, GUID *render_guid, GUID *capture_guid) +{ + XINPUT_STATE state; + DWORD ret; + + TRACE("index %lu, render_guid %s, capture_guid %s.\n", index, debugstr_guid(render_guid), + debugstr_guid(capture_guid)); + + InitOnceExecuteOnce(&init_xinput1_4_once, init_xinput1_4_funcs, NULL, NULL); + + if (index >= XUSER_MAX_COUNT || !render_guid || !capture_guid) return ERROR_BAD_ARGUMENTS; + if (!pXInputGetState) return ERROR_DEVICE_NOT_CONNECTED; + + ret = pXInputGetState(index, &state); + if (ret != ERROR_SUCCESS) return ret; + + /* Docs say this function returns no results on Win8 and later. */ + *render_guid = GUID_NULL; + *capture_guid = GUID_NULL; + + return ERROR_SUCCESS; +} + +DWORD WINAPI DECLSPEC_HOTPATCH XInputGetState(DWORD index, XINPUT_STATE *state) +{ + InitOnceExecuteOnce(&init_xinput1_4_once, init_xinput1_4_funcs, NULL, NULL); + + if (!pXInputGetState) return ERROR_DEVICE_NOT_CONNECTED; + return pXInputGetState(index, state); +} + +DWORD WINAPI DECLSPEC_HOTPATCH XInputSetState(DWORD index, XINPUT_VIBRATION *vibration) +{ + InitOnceExecuteOnce(&init_xinput1_4_once, init_xinput1_4_funcs, NULL, NULL); + + if (!pXInputSetState) return ERROR_DEVICE_NOT_CONNECTED; + return pXInputSetState(index, vibration); +} + +BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, LPVOID reserved) +{ + TRACE("inst %p, reason %lu, reserved %p.\n", inst, reason, reserved); + + switch (reason) + { + case DLL_PROCESS_ATTACH: + DisableThreadLibraryCalls(inst); + break; + case DLL_PROCESS_DETACH: + if (reserved) break; + if (xinput1_4) FreeLibrary(xinput1_4); + break; + } + return TRUE; +} From 50b7a28def6bc41641b3a905e1266024b73f53d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 19 Aug 2025 19:51:14 +0200 Subject: [PATCH 062/454] xinput: Hardcode gamepad capabilities like native report them. Some games expect the button mask to have some specific value to decide if a gamepad is an XBox compatible one. CW-Bug-Id: #25652 --- dlls/xinput1_1/Makefile.in | 1 + dlls/xinput1_2/Makefile.in | 1 + dlls/xinput1_3/Makefile.in | 1 + dlls/xinput1_3/main.c | 64 +++++++++++++++++++------------------- dlls/xinput1_4/Makefile.in | 1 + dlls/xinput9_1_0/main.c | 19 +++++++++-- 6 files changed, 53 insertions(+), 34 deletions(-) diff --git a/dlls/xinput1_1/Makefile.in b/dlls/xinput1_1/Makefile.in index b5c13cde8dfd..770937e76129 100644 --- a/dlls/xinput1_1/Makefile.in +++ b/dlls/xinput1_1/Makefile.in @@ -1,3 +1,4 @@ +EXTRADEFS = -DXINPUT_VER=1 MODULE = xinput1_1.dll IMPORTS = hid setupapi advapi32 user32 PARENTSRC = ../xinput1_3 diff --git a/dlls/xinput1_2/Makefile.in b/dlls/xinput1_2/Makefile.in index f49e8f53191f..a8c71b40ca89 100644 --- a/dlls/xinput1_2/Makefile.in +++ b/dlls/xinput1_2/Makefile.in @@ -1,3 +1,4 @@ +EXTRADEFS = -DXINPUT_VER=2 MODULE = xinput1_2.dll IMPORTS = hid setupapi advapi32 user32 PARENTSRC = ../xinput1_3 diff --git a/dlls/xinput1_3/Makefile.in b/dlls/xinput1_3/Makefile.in index 28d46d8c97f2..7dc406dd85a9 100644 --- a/dlls/xinput1_3/Makefile.in +++ b/dlls/xinput1_3/Makefile.in @@ -1,3 +1,4 @@ +EXTRADEFS = -DXINPUT_VER=3 MODULE = xinput1_3.dll IMPORTLIB = xinput IMPORTS = hid setupapi advapi32 user32 diff --git a/dlls/xinput1_3/main.c b/dlls/xinput1_3/main.c index c2ae05a0ce84..eb84f6df8301 100644 --- a/dlls/xinput1_3/main.c +++ b/dlls/xinput1_3/main.c @@ -54,7 +54,6 @@ WINE_DEFAULT_DEBUG_CHANNEL(xinput); struct xinput_controller { CRITICAL_SECTION crit; - XINPUT_CAPABILITIES caps; XINPUT_STATE state; XINPUT_GAMEPAD last_keystroke; XINPUT_VIBRATION vibration; @@ -198,7 +197,6 @@ static void check_waveform_caps(struct xinput_controller *controller, HANDLE dev static BOOL controller_check_caps(struct xinput_controller *controller, HANDLE device, PHIDP_PREPARSED_DATA preparsed) { USHORT caps_count = 0, waveform_caps_count = 0; - XINPUT_CAPABILITIES *caps = &controller->caps; HIDP_LINK_COLLECTION_NODE *collections; HIDP_VALUE_CAPS waveform_caps[8]; HIDP_BUTTON_CAPS *button_caps; @@ -207,9 +205,6 @@ static BOOL controller_check_caps(struct xinput_controller *controller, HANDLE d int i, u, button_count = 0; NTSTATUS status; - /* Count buttons */ - memset(caps, 0, sizeof(XINPUT_CAPABILITIES)); - if (!(button_caps = malloc(sizeof(*button_caps) * controller->hid.caps.NumberInputButtonCaps))) return FALSE; status = HidP_GetButtonCaps(HidP_Input, button_caps, &controller->hid.caps.NumberInputButtonCaps, preparsed); if (status != HIDP_STATUS_SUCCESS) WARN("HidP_GetButtonCaps returned %#lx\n", status); @@ -223,9 +218,7 @@ static BOOL controller_check_caps(struct xinput_controller *controller, HANDLE d button_count = max(button_count, button_caps[i].NotRange.Usage); } free(button_caps); - if (button_count < 11) - WARN("Too few buttons, continuing anyway\n"); - caps->Gamepad.wButtons = 0xffff; + if (button_count < 11) WARN("Too few buttons, continuing anyway\n"); if (!(value_caps = malloc(sizeof(*value_caps) * controller->hid.caps.NumberInputValueCaps))) return FALSE; status = HidP_GetValueCaps(HidP_Input, value_caps, &controller->hid.caps.NumberInputValueCaps, preparsed); @@ -240,20 +233,11 @@ static BOOL controller_check_caps(struct xinput_controller *controller, HANDLE d free(value_caps); if (!controller->hid.lt_caps.UsagePage) WARN("Missing axis LeftTrigger\n"); - else caps->Gamepad.bLeftTrigger = (1u << (sizeof(caps->Gamepad.bLeftTrigger) + 1)) - 1; if (!controller->hid.rt_caps.UsagePage) WARN("Missing axis RightTrigger\n"); - else caps->Gamepad.bRightTrigger = (1u << (sizeof(caps->Gamepad.bRightTrigger) + 1)) - 1; if (!controller->hid.lx_caps.UsagePage) WARN("Missing axis ThumbLX\n"); - else caps->Gamepad.sThumbLX = (1u << (sizeof(caps->Gamepad.sThumbLX) + 1)) - 1; if (!controller->hid.ly_caps.UsagePage) WARN("Missing axis ThumbLY\n"); - else caps->Gamepad.sThumbLY = (1u << (sizeof(caps->Gamepad.sThumbLY) + 1)) - 1; if (!controller->hid.rx_caps.UsagePage) WARN("Missing axis ThumbRX\n"); - else caps->Gamepad.sThumbRX = (1u << (sizeof(caps->Gamepad.sThumbRX) + 1)) - 1; if (!controller->hid.ry_caps.UsagePage) WARN("Missing axis ThumbRY\n"); - else caps->Gamepad.sThumbRY = (1u << (sizeof(caps->Gamepad.sThumbRY) + 1)) - 1; - - caps->Type = XINPUT_DEVTYPE_GAMEPAD; - caps->SubType = XINPUT_DEVSUBTYPE_GAMEPAD; collections_count = controller->hid.caps.NumberLinkCollectionNodes; if (!(collections = malloc(sizeof(*collections) * controller->hid.caps.NumberLinkCollectionNodes))) return FALSE; @@ -273,14 +257,6 @@ static BOOL controller_check_caps(struct xinput_controller *controller, HANDLE d for (i = 0; i < waveform_caps_count; ++i) check_waveform_caps(controller, device, preparsed, collections, waveform_caps + i); free(collections); - if (controller->hid.haptics_rumble_caps.UsagePage || - controller->hid.haptics_buzz_caps.UsagePage) - { - caps->Flags |= XINPUT_CAPS_FFB_SUPPORTED; - caps->Vibration.wLeftMotorSpeed = 255; - caps->Vibration.wRightMotorSpeed = 255; - } - return TRUE; } @@ -294,7 +270,7 @@ static DWORD HID_set_state(struct xinput_controller *controller, XINPUT_VIBRATIO NTSTATUS status; BYTE report_id; - if (!(controller->caps.Flags & XINPUT_CAPS_FFB_SUPPORTED)) return ERROR_SUCCESS; + if (!controller->hid.haptics_rumble_caps.UsagePage && !controller->hid.haptics_buzz_caps.UsagePage) return ERROR_SUCCESS; update_rumble = (controller->vibration.wLeftMotorSpeed != state->wLeftMotorSpeed); controller->vibration.wLeftMotorSpeed = state->wLeftMotorSpeed; @@ -330,7 +306,7 @@ static void controller_disable(struct xinput_controller *controller) XINPUT_VIBRATION state = {0}; if (!controller->enabled) return; - if (controller->caps.Flags & XINPUT_CAPS_FFB_SUPPORTED) HID_set_state(controller, &state); + HID_set_state(controller, &state); controller->enabled = FALSE; CancelIoEx(controller->device, &controller->hid.read_ovl); @@ -368,7 +344,7 @@ static void controller_enable(struct xinput_controller *controller) BOOL ret; if (controller->enabled) return; - if (controller->caps.Flags & XINPUT_CAPS_FFB_SUPPORTED) HID_set_state(controller, &state); + HID_set_state(controller, &state); controller->enabled = TRUE; memset(&controller->hid.read_ovl, 0, sizeof(controller->hid.read_ovl)); @@ -1151,6 +1127,10 @@ DWORD WINAPI DECLSPEC_HOTPATCH XInputGetBatteryInformation(DWORD index, BYTE typ DWORD WINAPI DECLSPEC_HOTPATCH XInputGetCapabilitiesEx(DWORD unk, DWORD index, DWORD flags, XINPUT_CAPABILITIES_EX *caps) { + static const UINT XINPUT_BUTTONS_ALL = XINPUT_GAMEPAD_DPAD_UP | XINPUT_GAMEPAD_DPAD_DOWN | XINPUT_GAMEPAD_DPAD_LEFT | XINPUT_GAMEPAD_DPAD_RIGHT + | XINPUT_GAMEPAD_START | XINPUT_GAMEPAD_BACK | XINPUT_GAMEPAD_LEFT_THUMB | XINPUT_GAMEPAD_RIGHT_THUMB + | XINPUT_GAMEPAD_LEFT_SHOULDER | XINPUT_GAMEPAD_RIGHT_SHOULDER + | XINPUT_GAMEPAD_A | XINPUT_GAMEPAD_B | XINPUT_GAMEPAD_X | XINPUT_GAMEPAD_Y; HIDD_ATTRIBUTES attr; DWORD ret = ERROR_SUCCESS; @@ -1162,13 +1142,33 @@ DWORD WINAPI DECLSPEC_HOTPATCH XInputGetCapabilitiesEx(DWORD unk, DWORD index, D if (!controller_lock(&controllers[index])) return ERROR_DEVICE_NOT_CONNECTED; - if (flags & XINPUT_FLAG_GAMEPAD && controllers[index].caps.SubType != XINPUT_DEVSUBTYPE_GAMEPAD) - ret = ERROR_DEVICE_NOT_CONNECTED; - else if (!HidD_GetAttributes(controllers[index].device, &attr)) + if (!HidD_GetAttributes(controllers[index].device, &attr)) ret = ERROR_DEVICE_NOT_CONNECTED; else { - caps->Capabilities = controllers[index].caps; + memset(caps, 0, sizeof(*caps)); + +#if XINPUT_VER >= 4 + caps->Capabilities.Type = XINPUT_DEVTYPE_GAMEPAD; +#endif + caps->Capabilities.SubType = XINPUT_DEVSUBTYPE_GAMEPAD; +#if XINPUT_VER >= 4 + caps->Capabilities.Flags |= XINPUT_CAPS_PMD_SUPPORTED; +#endif +#if XINPUT_VER >= 3 + caps->Capabilities.Flags |= XINPUT_CAPS_VOICE_SUPPORTED; +#endif + + caps->Capabilities.Gamepad.wButtons = XINPUT_BUTTONS_ALL; + caps->Capabilities.Gamepad.bLeftTrigger = 0xff; + caps->Capabilities.Gamepad.bRightTrigger = 0xff; + caps->Capabilities.Gamepad.sThumbLX = ~0x3f; + caps->Capabilities.Gamepad.sThumbLY = ~0x3f; + caps->Capabilities.Gamepad.sThumbRX = ~0x3f; + caps->Capabilities.Gamepad.sThumbRY = ~0x3f; + caps->Capabilities.Vibration.wLeftMotorSpeed = 0xff; + caps->Capabilities.Vibration.wRightMotorSpeed = 0xff; + caps->VendorId = attr.VendorID; caps->ProductId = attr.ProductID; caps->VersionNumber = attr.VersionNumber; diff --git a/dlls/xinput1_4/Makefile.in b/dlls/xinput1_4/Makefile.in index 69d6b9c018f6..482f75d0f7e4 100644 --- a/dlls/xinput1_4/Makefile.in +++ b/dlls/xinput1_4/Makefile.in @@ -1,3 +1,4 @@ +EXTRADEFS = -DXINPUT_VER=4 MODULE = xinput1_4.dll IMPORTS = hid setupapi advapi32 user32 PARENTSRC = ../xinput1_3 diff --git a/dlls/xinput9_1_0/main.c b/dlls/xinput9_1_0/main.c index 8c0259d1c609..dc3c54e64987 100644 --- a/dlls/xinput9_1_0/main.c +++ b/dlls/xinput9_1_0/main.c @@ -70,12 +70,27 @@ static BOOL WINAPI init_xinput1_4_funcs(INIT_ONCE *once, void *param, void **con return TRUE; } -DWORD WINAPI DECLSPEC_HOTPATCH XInputGetCapabilities(DWORD index, DWORD flags, XINPUT_CAPABILITIES *capabilities) +DWORD WINAPI DECLSPEC_HOTPATCH XInputGetCapabilities(DWORD index, DWORD flags, XINPUT_CAPABILITIES *caps) { + DWORD ret; + InitOnceExecuteOnce(&init_xinput1_4_once, init_xinput1_4_funcs, NULL, NULL); if (!pXInputGetCapabilities) return ERROR_DEVICE_NOT_CONNECTED; - return pXInputGetCapabilities(index, flags, capabilities); + if (!(ret = pXInputGetCapabilities(index, flags, caps))) + { + caps->Flags = XINPUT_CAPS_VOICE_SUPPORTED; + caps->Gamepad.bLeftTrigger = !!caps->Gamepad.bLeftTrigger; + caps->Gamepad.bRightTrigger = !!caps->Gamepad.bRightTrigger; + caps->Gamepad.sThumbLX = !!caps->Gamepad.sThumbLX; + caps->Gamepad.sThumbLY = !!caps->Gamepad.sThumbLY; + caps->Gamepad.sThumbRX = !!caps->Gamepad.sThumbRX; + caps->Gamepad.sThumbRY = !!caps->Gamepad.sThumbRY; + caps->Vibration.wLeftMotorSpeed = !!caps->Vibration.wLeftMotorSpeed; + caps->Vibration.wRightMotorSpeed = !!caps->Vibration.wRightMotorSpeed; + } + + return ret; } DWORD WINAPI DECLSPEC_HOTPATCH XInputGetDSoundAudioDeviceGuids(DWORD index, GUID *render_guid, GUID *capture_guid) From 11e351b6a12b3085756270f5ccc3005a3bb84e63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 20 Aug 2025 10:39:08 +0200 Subject: [PATCH 063/454] fixup! HACK: dinput: Emulate Steam Input native hooks. CW-Bug-Id: #25652 --- dlls/dinput/joystick_hid.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/dlls/dinput/joystick_hid.c b/dlls/dinput/joystick_hid.c index 2f321fd471b5..6efb9acd07b3 100644 --- a/dlls/dinput/joystick_hid.c +++ b/dlls/dinput/joystick_hid.c @@ -1581,6 +1581,14 @@ static HRESULT hid_joystick_device_try_open( const WCHAR *path, HANDLE *device, instance->wUsagePage = caps->UsagePage; instance->wUsage = caps->Usage; + /* CW-Bug-Id: #23185 Emulate Steam Input native hooks for native SDL */ + if (attrs->VendorID == 0x28de && attrs->ProductID == 0x11ff) + { + instance->guidProduct.Data2 = 0x28de; + instance->guidInstance = instance->guidProduct; + instance->guidInstance.Data3 = 1; + } + node_count = ARRAY_SIZE(nodes); status = HidP_GetLinkCollectionNodes( nodes, &node_count, preparsed_data ); if (status != HIDP_STATUS_SUCCESS) node_count = 0; From b29d58399f044b8e50a80e53b3646fc7f8dd8943 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 20 Aug 2025 10:40:10 +0200 Subject: [PATCH 064/454] fixup! HACK: windows.gaming.input: Emulate Steam Input native hooks. CW-Bug-Id: #25652 --- dlls/windows.gaming.input/provider.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/dlls/windows.gaming.input/provider.c b/dlls/windows.gaming.input/provider.c index d12785dee4c8..b0c54044b668 100644 --- a/dlls/windows.gaming.input/provider.c +++ b/dlls/windows.gaming.input/provider.c @@ -145,10 +145,21 @@ static HRESULT WINAPI wine_provider_get_DisplayName( IWineGameControllerProvider { struct provider *impl = impl_from_IWineGameControllerProvider( iface ); DIDEVICEINSTANCEW instance = {.dwSize = sizeof(DIDEVICEINSTANCEW)}; + UINT16 vid, pid; HRESULT hr; TRACE( "iface %p, value %p\n", iface, value ); + if (FAILED(hr = IGameControllerProvider_get_HardwareVendorId( &impl->IGameControllerProvider_iface, &vid ))) return hr; + if (FAILED(hr = IGameControllerProvider_get_HardwareProductId( &impl->IGameControllerProvider_iface, &pid ))) return hr; + + /* CW-Bug-Id: #23185 Emulate Steam Input native hooks for native SDL */ + if (vid == 0x28de && pid == 0x11ff) + { + static const WCHAR name[] = L"Xbox 360 Controller for Windows"; + return WindowsCreateString( name, wcslen( name ), value ); + } + if (FAILED(hr = IDirectInputDevice8_GetDeviceInfo( impl->dinput_device, &instance ))) return hr; return WindowsCreateString( instance.tszProductName, wcslen( instance.tszProductName ), value ); } From 3fe3e3611238c31cadc3bf45be865e81a0b502d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 20 Aug 2025 10:38:29 +0200 Subject: [PATCH 065/454] fixup! HACK: xinput: Emulate Steam Input native hooks. CW-Bug-Id: #25652 --- dlls/xinput1_3/main.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/dlls/xinput1_3/main.c b/dlls/xinput1_3/main.c index eb84f6df8301..906d25d196b6 100644 --- a/dlls/xinput1_3/main.c +++ b/dlls/xinput1_3/main.c @@ -1172,8 +1172,14 @@ DWORD WINAPI DECLSPEC_HOTPATCH XInputGetCapabilitiesEx(DWORD unk, DWORD index, D caps->VendorId = attr.VendorID; caps->ProductId = attr.ProductID; caps->VersionNumber = attr.VersionNumber; + /* CW-Bug-Id: #23185 Emulate Steam Input native hooks for native SDL */ - caps->unk2 = index; + if (attr.VendorID == 0x28de && attr.ProductID == 0x11ff) + { + caps->Capabilities.Type = XINPUT_DEVTYPE_GAMEPAD; + caps->Capabilities.Flags = XINPUT_CAPS_WIRELESS; + caps->unk2 = index; + } } controller_unlock(&controllers[index]); From 643af2d51b12e07c1c007605cf48c33a9b2b1cdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 8 Jan 2024 14:35:42 +0100 Subject: [PATCH 066/454] HACK: wbemprox: Emulate Steam Input native hooks. CW-Bug-Id: #25652 CW-Bug-Id: #23185 --- dlls/wbemprox/builtin.c | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/dlls/wbemprox/builtin.c b/dlls/wbemprox/builtin.c index 432e3ab61886..024d2cc4cb0e 100644 --- a/dlls/wbemprox/builtin.c +++ b/dlls/wbemprox/builtin.c @@ -3283,6 +3283,30 @@ static enum fill_status fill_physicalmemory( struct table *table, const struct e return status; } +static BOOL steam_input_get_vid_pid( UINT slot, UINT16 *vid, UINT16 *pid ) +{ + const char *info = getenv( "SteamVirtualGamepadInfo" ); + char buffer[256]; + UINT current; + FILE *file; + + TRACE( "reading SteamVirtualGamepadInfo %s\n", debugstr_a(info) ); + + if (!info || !(file = fopen( info, "r" ))) return FALSE; + while (fscanf( file, "%255[^\n]\n", buffer ) == 1) + { + if (sscanf( buffer, "[slot %d]", ¤t )) continue; + if (current < slot) continue; + if (current > slot) break; + if (sscanf( buffer, "VID=0x%hx", vid )) continue; + if (sscanf( buffer, "PID=0x%hx", pid )) continue; + } + + fclose( file ); + + return TRUE; +} + static enum fill_status fill_pnpentity( struct table *table, const struct expr *cond ) { struct record_pnpentity *rec; @@ -3312,6 +3336,22 @@ static enum fill_status fill_pnpentity( struct table *table, const struct expr * if (SetupDiGetDeviceInstanceIdW( device_info_set, &devinfo, device_id, ARRAY_SIZE(device_id), NULL )) { + /* CW-Bug-Id: #23185 Emulate Steam Input native hooks for native SDL */ + UINT16 vid, pid; + UINT slot; + + if (swscanf( device_id, L"HID\\VID_%04x&PID_%04x&XI_%02u", &vid, &pid, &slot ) == 3 && + vid == 0x28de && pid == 0x11ff) + { + swprintf( device_id, ARRAY_SIZE(device_id), L"#HID#VID_%04X&PID_%04X&IG_%02u", vid, pid, slot ); + } + if (swscanf( device_id, L"HID\\VID_%04x&PID_%04x&IG_%02u", &vid, &pid, &slot ) == 3 && + vid == 0x28de && pid == 0x11ff && steam_input_get_vid_pid( slot, &vid, &pid )) + { + swprintf( device_id, ARRAY_SIZE(device_id), L"HID\\VID_%04X&PID_%04X&IG_%02u", vid, pid, slot ); + device_id[27] = '\\'; + } + StringFromGUID2( &devinfo.ClassGuid, guid, ARRAY_SIZE(guid) ); rec->caption = L"Wine PnP Device"; rec->class_guid = wcsdup( wcslwr(guid) ); From b2d580e4b390041a483e2cb7186019f038a1227c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 8 Jan 2024 14:35:42 +0100 Subject: [PATCH 067/454] HACK: user32: Emulate Steam Input native hooks. CW-Bug-Id: #23185 CW-Bug-Id: #25733 --- dlls/user32/input.c | 89 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 87 insertions(+), 2 deletions(-) diff --git a/dlls/user32/input.c b/dlls/user32/input.c index db59f21f89b1..8d079a0794de 100644 --- a/dlls/user32/input.c +++ b/dlls/user32/input.c @@ -535,6 +535,91 @@ static DWORD CALLBACK devnotify_window_callbackA(HANDLE handle, DWORD flags, DEV return 0; } +static BOOL steam_input_get_vid_pid( UINT slot, UINT16 *vid, UINT16 *pid ) +{ + const char *info = getenv( "SteamVirtualGamepadInfo" ); + char buffer[256]; + UINT current; + FILE *file; + + TRACE( "reading SteamVirtualGamepadInfo %s\n", debugstr_a(info) ); + + if (!info || !(file = fopen( info, "r" ))) return FALSE; + while (fscanf( file, "%255[^\n]\n", buffer ) == 1) + { + if (sscanf( buffer, "[slot %d]", ¤t )) continue; + if (current < slot) continue; + if (current > slot) break; + if (sscanf( buffer, "VID=0x%hx", vid )) continue; + if (sscanf( buffer, "PID=0x%hx", pid )) continue; + } + + fclose( file ); + + return TRUE; +} + +/* CW-Bug-Id: #23185 Emulate Steam Input native hooks for native SDL */ +static BOOL steam_input_devnotify(HANDLE handle, DWORD flags, DEV_BROADCAST_HDR *header, BOOL ansi) +{ + char buffer[offsetof(DEV_BROADCAST_DEVICEINTERFACE_W, dbcc_name[MAX_PATH])]; + DEV_BROADCAST_DEVICEINTERFACE_W *copyW = (DEV_BROADCAST_DEVICEINTERFACE_W *)buffer; + + if (flags & 0x8000) + { + switch (header->dbch_devicetype) + { + case DBT_DEVTYP_DEVICEINTERFACE: + { + static const WCHAR steam_input_idW[] = L"\\\\?\\HID#VID_28DE&PID_11FF&IG_"; + const DEV_BROADCAST_DEVICEINTERFACE_W *ifaceW = (const DEV_BROADCAST_DEVICEINTERFACE_W *)header; + + if (!wcsnicmp( ifaceW->dbcc_name, steam_input_idW, 29 )) + { + UINT size, slot; + const WCHAR *tmpW; + UINT16 vid, pid; + + copyW->dbcc_devicetype = ifaceW->dbcc_devicetype; + copyW->dbcc_reserved = ifaceW->dbcc_reserved; + copyW->dbcc_classguid = ifaceW->dbcc_classguid; + + if (swscanf( ifaceW->dbcc_name + 29, L"%02u", &slot ) != 1) slot = 0; + if (!steam_input_get_vid_pid( slot, &vid, &pid )) + { + vid = 0x045e; + pid = 0x028e; + } + + size = swprintf( copyW->dbcc_name, MAX_PATH, L"\\\\.\\pipe\\HID#VID_045E&PID_028E&IG_00#%04X&%04X", vid, pid ); + if ((tmpW = wcschr( ifaceW->dbcc_name + 29, '&' ))) + { + do copyW->dbcc_name[size++] = *tmpW++; + while (*tmpW != '&' && size < MAX_PATH); + } + size += swprintf( copyW->dbcc_name + size, MAX_PATH - size, L"#%d#%u", slot, (UINT)GetCurrentProcessId() ); + + copyW->dbcc_size = offsetof(DEV_BROADCAST_DEVICEINTERFACE_W, dbcc_name[size + 1]); + header = (DEV_BROADCAST_HDR *)copyW; + } + } + } + } + + if (ansi) return devnotify_window_callbackA(handle, flags, header); + return devnotify_window_callbackW(handle, flags, header); +} + +static DWORD CALLBACK steam_input_callbackW(HANDLE handle, DWORD flags, DEV_BROADCAST_HDR *header) +{ + return steam_input_devnotify(handle, flags, header, FALSE); +} + +static DWORD CALLBACK steam_input_callbackA(HANDLE handle, DWORD flags, DEV_BROADCAST_HDR *header) +{ + return steam_input_devnotify(handle, flags, header, TRUE); +} + static DWORD CALLBACK devnotify_service_callback(HANDLE handle, DWORD flags, DEV_BROADCAST_HDR *header) { FIXME("Support for service handles is not yet implemented!\n"); @@ -576,9 +661,9 @@ HDEVNOTIFY WINAPI RegisterDeviceNotificationW( HANDLE handle, void *filter, DWOR if (flags & DEVICE_NOTIFY_SERVICE_HANDLE) callback = devnotify_service_callback; else if (IsWindowUnicode( handle )) - callback = devnotify_window_callbackW; + callback = steam_input_callbackW; else - callback = devnotify_window_callbackA; + callback = steam_input_callbackA; if (!header) { From 1cfd7c92e006c3ce7b2d0f92aa43f6e1b2f2a984 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20K=C3=B6lbl?= Date: Mon, 11 Aug 2025 14:50:35 +0200 Subject: [PATCH 068/454] mf: Handle start request when session is already running. CW-Bug-Id: #18552 --- dlls/mf/session.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dlls/mf/session.c b/dlls/mf/session.c index 2a6ac4f61f71..d19955953704 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -1179,6 +1179,9 @@ static void session_start(struct media_session *session, const GUID *time_format session->state = SESSION_STATE_RESTARTING_SOURCES; break; } + else if (session->state == SESSION_STATE_STARTED) + return session_command_complete_with_event(session, MESessionStarted, S_OK, NULL); + /* fallthrough; we're resuming from the current position */ case SESSION_STATE_STOPPED: /* Start request with no current topology. */ From 42f9a845c1ad0b71bf9596a4168b6af8420c2c9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 9 Sep 2025 12:33:06 +0200 Subject: [PATCH 069/454] winebus: Match match gamepad dpad buttons with XUSB / GIP. (cherry picked from commit c736282109057c59b248df1f5c514b7dba02610c) --- dlls/winebus.sys/bus_sdl.c | 12 ++++++------ dlls/winebus.sys/bus_udev.c | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/dlls/winebus.sys/bus_sdl.c b/dlls/winebus.sys/bus_sdl.c index 9278f1c41ef6..259ee419c21d 100644 --- a/dlls/winebus.sys/bus_sdl.c +++ b/dlls/winebus.sys/bus_sdl.c @@ -893,18 +893,18 @@ static BOOL set_report_from_controller_event(struct sdl_device *impl, SDL_Event case SDL_CONTROLLER_BUTTON_LEFTSTICK: button = 8; break; case SDL_CONTROLLER_BUTTON_RIGHTSTICK: button = 9; break; case SDL_CONTROLLER_BUTTON_DPAD_UP: button = 10; break; - case SDL_CONTROLLER_BUTTON_DPAD_DOWN: button = 11; break; - case SDL_CONTROLLER_BUTTON_DPAD_LEFT: button = 12; break; - case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: button = 13; break; + case SDL_CONTROLLER_BUTTON_DPAD_DOWN: button = 12; break; + case SDL_CONTROLLER_BUTTON_DPAD_LEFT: button = 13; break; + case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: button = 11; break; case SDL_CONTROLLER_BUTTON_GUIDE: button = 16; break; default: button = -1; break; } if (button == -1) break; if (button == 10) hid_device_move_hatswitch(iface, 0, 0, ie->state ? -1 : +1); - if (button == 11) hid_device_move_hatswitch(iface, 0, 0, ie->state ? +1 : -1); - if (button == 12) hid_device_move_hatswitch(iface, 0, ie->state ? -1 : +1, 0); - if (button == 13) hid_device_move_hatswitch(iface, 0, ie->state ? +1 : -1, 0); + if (button == 12) hid_device_move_hatswitch(iface, 0, 0, ie->state ? +1 : -1); + if (button == 13) hid_device_move_hatswitch(iface, 0, ie->state ? -1 : +1, 0); + if (button == 11) hid_device_move_hatswitch(iface, 0, ie->state ? +1 : -1, 0); hid_device_set_button(iface, button, ie->state); bus_event_queue_input_report(&event_queue, iface, state->report_buf, state->report_len); break; diff --git a/dlls/winebus.sys/bus_udev.c b/dlls/winebus.sys/bus_udev.c index a324f49d2d74..9079fb2cf35b 100644 --- a/dlls/winebus.sys/bus_udev.c +++ b/dlls/winebus.sys/bus_udev.c @@ -858,7 +858,7 @@ static void set_abs_axis_value(struct unix_device *iface, int code, int value) if (impl->is_gamepad) { hid_device_set_button(iface, 10, value < 0); - hid_device_set_button(iface, 11, value > 0); + hid_device_set_button(iface, 12, value > 0); } hid_device_set_hatswitch_y(iface, code - 1, value); } @@ -867,8 +867,8 @@ static void set_abs_axis_value(struct unix_device *iface, int code, int value) if (!(code = impl->hat_map[code - ABS_HAT0X])) return; if (impl->is_gamepad) { - hid_device_set_button(iface, 12, value < 0); - hid_device_set_button(iface, 13, value > 0); + hid_device_set_button(iface, 13, value < 0); + hid_device_set_button(iface, 11, value > 0); } hid_device_set_hatswitch_x(iface, code - 1, value); } @@ -990,9 +990,9 @@ static BOOL set_report_from_event(struct unix_device *iface, struct input_event if (impl->is_gamepad && !impl->hat_count) { if (button == 11) hid_device_set_hatswitch_y(iface, 0, -1); - if (button == 12) hid_device_set_hatswitch_y(iface, 0, +1); - if (button == 13) hid_device_set_hatswitch_x(iface, 0, -1); - if (button == 14) hid_device_set_hatswitch_x(iface, 0, +1); + if (button == 13) hid_device_set_hatswitch_y(iface, 0, +1); + if (button == 14) hid_device_set_hatswitch_x(iface, 0, -1); + if (button == 12) hid_device_set_hatswitch_x(iface, 0, +1); } hid_device_set_button(iface, button - 1, ie->value); return FALSE; From b143adf9dc2159a8be03bf4cf6f84e742c14c68c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 17 Sep 2025 16:52:02 +0200 Subject: [PATCH 070/454] Revert "winebus: Match match gamepad dpad buttons with XUSB / GIP." This reverts commit 22e013bc33cc637948cc891d5cad43c677b162bc. --- dlls/winebus.sys/bus_sdl.c | 12 ++++++------ dlls/winebus.sys/bus_udev.c | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/dlls/winebus.sys/bus_sdl.c b/dlls/winebus.sys/bus_sdl.c index 259ee419c21d..9278f1c41ef6 100644 --- a/dlls/winebus.sys/bus_sdl.c +++ b/dlls/winebus.sys/bus_sdl.c @@ -893,18 +893,18 @@ static BOOL set_report_from_controller_event(struct sdl_device *impl, SDL_Event case SDL_CONTROLLER_BUTTON_LEFTSTICK: button = 8; break; case SDL_CONTROLLER_BUTTON_RIGHTSTICK: button = 9; break; case SDL_CONTROLLER_BUTTON_DPAD_UP: button = 10; break; - case SDL_CONTROLLER_BUTTON_DPAD_DOWN: button = 12; break; - case SDL_CONTROLLER_BUTTON_DPAD_LEFT: button = 13; break; - case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: button = 11; break; + case SDL_CONTROLLER_BUTTON_DPAD_DOWN: button = 11; break; + case SDL_CONTROLLER_BUTTON_DPAD_LEFT: button = 12; break; + case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: button = 13; break; case SDL_CONTROLLER_BUTTON_GUIDE: button = 16; break; default: button = -1; break; } if (button == -1) break; if (button == 10) hid_device_move_hatswitch(iface, 0, 0, ie->state ? -1 : +1); - if (button == 12) hid_device_move_hatswitch(iface, 0, 0, ie->state ? +1 : -1); - if (button == 13) hid_device_move_hatswitch(iface, 0, ie->state ? -1 : +1, 0); - if (button == 11) hid_device_move_hatswitch(iface, 0, ie->state ? +1 : -1, 0); + if (button == 11) hid_device_move_hatswitch(iface, 0, 0, ie->state ? +1 : -1); + if (button == 12) hid_device_move_hatswitch(iface, 0, ie->state ? -1 : +1, 0); + if (button == 13) hid_device_move_hatswitch(iface, 0, ie->state ? +1 : -1, 0); hid_device_set_button(iface, button, ie->state); bus_event_queue_input_report(&event_queue, iface, state->report_buf, state->report_len); break; diff --git a/dlls/winebus.sys/bus_udev.c b/dlls/winebus.sys/bus_udev.c index 9079fb2cf35b..a324f49d2d74 100644 --- a/dlls/winebus.sys/bus_udev.c +++ b/dlls/winebus.sys/bus_udev.c @@ -858,7 +858,7 @@ static void set_abs_axis_value(struct unix_device *iface, int code, int value) if (impl->is_gamepad) { hid_device_set_button(iface, 10, value < 0); - hid_device_set_button(iface, 12, value > 0); + hid_device_set_button(iface, 11, value > 0); } hid_device_set_hatswitch_y(iface, code - 1, value); } @@ -867,8 +867,8 @@ static void set_abs_axis_value(struct unix_device *iface, int code, int value) if (!(code = impl->hat_map[code - ABS_HAT0X])) return; if (impl->is_gamepad) { - hid_device_set_button(iface, 13, value < 0); - hid_device_set_button(iface, 11, value > 0); + hid_device_set_button(iface, 12, value < 0); + hid_device_set_button(iface, 13, value > 0); } hid_device_set_hatswitch_x(iface, code - 1, value); } @@ -990,9 +990,9 @@ static BOOL set_report_from_event(struct unix_device *iface, struct input_event if (impl->is_gamepad && !impl->hat_count) { if (button == 11) hid_device_set_hatswitch_y(iface, 0, -1); - if (button == 13) hid_device_set_hatswitch_y(iface, 0, +1); - if (button == 14) hid_device_set_hatswitch_x(iface, 0, -1); - if (button == 12) hid_device_set_hatswitch_x(iface, 0, +1); + if (button == 12) hid_device_set_hatswitch_y(iface, 0, +1); + if (button == 13) hid_device_set_hatswitch_x(iface, 0, -1); + if (button == 14) hid_device_set_hatswitch_x(iface, 0, +1); } hid_device_set_button(iface, button - 1, ie->value); return FALSE; From f4cebf19c12b35586561f5c6121c7b36cd863574 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 17 Sep 2025 16:52:19 +0200 Subject: [PATCH 071/454] Revert "winebus: Use a vendor specific usage for gamepad guide buttons." This reverts commit 73b88be7a79a7fcf38a6678a6cdf9510398a1d22. --- dlls/dinput/joystick_hid.c | 2 +- dlls/winebus.sys/bus_sdl.c | 30 ++++++++++++++---------------- dlls/winebus.sys/bus_udev.c | 18 ++++++++---------- dlls/winebus.sys/hid.c | 3 +-- dlls/xinput1_3/main.c | 6 +----- 5 files changed, 25 insertions(+), 34 deletions(-) diff --git a/dlls/dinput/joystick_hid.c b/dlls/dinput/joystick_hid.c index 6efb9acd07b3..33f03a9e2c16 100644 --- a/dlls/dinput/joystick_hid.c +++ b/dlls/dinput/joystick_hid.c @@ -1404,7 +1404,7 @@ static HRESULT hid_joystick_read( IDirectInputDevice8W *iface ) { usages = impl->usages_buf + count; if (usages->UsagePage != HID_USAGE_PAGE_BUTTON) - WARN( "unimplemented usage page %x.\n", usages->UsagePage ); + FIXME( "unimplemented usage page %x.\n", usages->UsagePage ); else if (usages->Usage >= 128) FIXME( "ignoring extraneous button %d.\n", usages->Usage ); else diff --git a/dlls/winebus.sys/bus_sdl.c b/dlls/winebus.sys/bus_sdl.c index 9278f1c41ef6..d9420a7bf083 100644 --- a/dlls/winebus.sys/bus_sdl.c +++ b/dlls/winebus.sys/bus_sdl.c @@ -880,31 +880,29 @@ static BOOL set_report_from_controller_event(struct sdl_device *impl, SDL_Event SDL_ControllerButtonEvent *ie = &event->cbutton; int button; - switch (ie->button) + switch ((button = ie->button)) { - case SDL_CONTROLLER_BUTTON_A: button = 0; break; - case SDL_CONTROLLER_BUTTON_B: button = 1; break; - case SDL_CONTROLLER_BUTTON_X: button = 2; break; - case SDL_CONTROLLER_BUTTON_Y: button = 3; break; + case SDL_CONTROLLER_BUTTON_DPAD_UP: + hid_device_move_hatswitch(iface, 0, 0, ie->state ? -1 : +1); + break; + case SDL_CONTROLLER_BUTTON_DPAD_DOWN: + hid_device_move_hatswitch(iface, 0, 0, ie->state ? +1 : -1); + break; + case SDL_CONTROLLER_BUTTON_DPAD_LEFT: + hid_device_move_hatswitch(iface, 0, ie->state ? -1 : +1, 0); + break; + case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: + hid_device_move_hatswitch(iface, 0, ie->state ? +1 : -1, 0); + break; case SDL_CONTROLLER_BUTTON_LEFTSHOULDER: button = 4; break; case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER: button = 5; break; case SDL_CONTROLLER_BUTTON_BACK: button = 6; break; case SDL_CONTROLLER_BUTTON_START: button = 7; break; case SDL_CONTROLLER_BUTTON_LEFTSTICK: button = 8; break; case SDL_CONTROLLER_BUTTON_RIGHTSTICK: button = 9; break; - case SDL_CONTROLLER_BUTTON_DPAD_UP: button = 10; break; - case SDL_CONTROLLER_BUTTON_DPAD_DOWN: button = 11; break; - case SDL_CONTROLLER_BUTTON_DPAD_LEFT: button = 12; break; - case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: button = 13; break; - case SDL_CONTROLLER_BUTTON_GUIDE: button = 16; break; - default: button = -1; break; + case SDL_CONTROLLER_BUTTON_GUIDE: button = 10; break; } - if (button == -1) break; - if (button == 10) hid_device_move_hatswitch(iface, 0, 0, ie->state ? -1 : +1); - if (button == 11) hid_device_move_hatswitch(iface, 0, 0, ie->state ? +1 : -1); - if (button == 12) hid_device_move_hatswitch(iface, 0, ie->state ? -1 : +1, 0); - if (button == 13) hid_device_move_hatswitch(iface, 0, ie->state ? +1 : -1, 0); hid_device_set_button(iface, button, ie->state); bus_event_queue_input_report(&event_queue, iface, state->report_buf, state->report_len); break; diff --git a/dlls/winebus.sys/bus_udev.c b/dlls/winebus.sys/bus_udev.c index a324f49d2d74..2274cdfd20f8 100644 --- a/dlls/winebus.sys/bus_udev.c +++ b/dlls/winebus.sys/bus_udev.c @@ -857,8 +857,8 @@ static void set_abs_axis_value(struct unix_device *iface, int code, int value) if (!(code = impl->hat_map[code - ABS_HAT0X])) return; if (impl->is_gamepad) { - hid_device_set_button(iface, 10, value < 0); - hid_device_set_button(iface, 11, value > 0); + hid_device_set_button(iface, 11, value < 0); + hid_device_set_button(iface, 12, value > 0); } hid_device_set_hatswitch_y(iface, code - 1, value); } @@ -867,8 +867,8 @@ static void set_abs_axis_value(struct unix_device *iface, int code, int value) if (!(code = impl->hat_map[code - ABS_HAT0X])) return; if (impl->is_gamepad) { - hid_device_set_button(iface, 12, value < 0); - hid_device_set_button(iface, 13, value > 0); + hid_device_set_button(iface, 13, value < 0); + hid_device_set_button(iface, 14, value > 0); } hid_device_set_hatswitch_x(iface, code - 1, value); } @@ -989,10 +989,10 @@ static BOOL set_report_from_event(struct unix_device *iface, struct input_event if (!(button = impl->button_map[ie->code])) return FALSE; if (impl->is_gamepad && !impl->hat_count) { - if (button == 11) hid_device_set_hatswitch_y(iface, 0, -1); - if (button == 12) hid_device_set_hatswitch_y(iface, 0, +1); - if (button == 13) hid_device_set_hatswitch_x(iface, 0, -1); - if (button == 14) hid_device_set_hatswitch_x(iface, 0, +1); + if (button == 12) hid_device_set_hatswitch_y(iface, 0, -1); + if (button == 13) hid_device_set_hatswitch_y(iface, 0, +1); + if (button == 14) hid_device_set_hatswitch_x(iface, 0, -1); + if (button == 15) hid_device_set_hatswitch_x(iface, 0, +1); } hid_device_set_button(iface, button - 1, ie->value); return FALSE; @@ -1640,7 +1640,6 @@ static NTSTATUS lnxev_device_create(struct udev_device *dev, int fd, const char if (!test_bit(info.key, button)) continue; if (impl->button_count > (impl->hat_count ? 10 : 14)) break; impl->button_map[button] = ++impl->button_count; - if (impl->hat_count && impl->button_count == 11) impl->button_map[button] = 17; } for (int i = BTN_MISC; i < KEY_MAX; i++) @@ -1649,7 +1648,6 @@ static NTSTATUS lnxev_device_create(struct udev_device *dev, int fd, const char if (impl->button_count > (impl->hat_count ? 10 : 14)) break; if (!test_bit(info.key, i)) continue; impl->button_map[i] = ++impl->button_count; - if (impl->hat_count && impl->button_count == 11) impl->button_map[i] = 17; } } diff --git a/dlls/winebus.sys/hid.c b/dlls/winebus.sys/hid.c index ee1c9a9de079..bb1ddbdabdb9 100644 --- a/dlls/winebus.sys/hid.c +++ b/dlls/winebus.sys/hid.c @@ -357,8 +357,7 @@ BOOL hid_device_add_gamepad(struct unix_device *iface) if (!hid_device_add_axes(iface, 1, HID_USAGE_PAGE_GENERIC, <, FALSE, 0, 32767)) return FALSE; if (!hid_device_add_axes(iface, 1, HID_USAGE_PAGE_GENERIC, &rt, FALSE, 0, 32767)) return FALSE; if (!hid_device_add_hatswitch(iface, 1)) return FALSE; - if (!hid_device_add_buttons(iface, HID_USAGE_PAGE_BUTTON, 1, 14)) return FALSE; - if (!hid_device_add_buttons(iface, HID_USAGE_PAGE_VENDOR_DEFINED_BEGIN, 1, 8)) return FALSE; + if (!hid_device_add_buttons(iface, HID_USAGE_PAGE_BUTTON, 1, 15)) return FALSE; if (!hid_device_end_input_report(iface)) return FALSE; return TRUE; diff --git a/dlls/xinput1_3/main.c b/dlls/xinput1_3/main.c index 906d25d196b6..c45f7570bd1a 100644 --- a/dlls/xinput1_3/main.c +++ b/dlls/xinput1_3/main.c @@ -594,14 +594,10 @@ static void read_controller_state(struct xinput_controller *controller) case 8: state.Gamepad.wButtons |= XINPUT_GAMEPAD_START; break; case 9: state.Gamepad.wButtons |= XINPUT_GAMEPAD_LEFT_THUMB; break; case 10: state.Gamepad.wButtons |= XINPUT_GAMEPAD_RIGHT_THUMB; break; + case 11: state.Gamepad.wButtons |= XINPUT_GAMEPAD_GUIDE; break; } } - button_length = ARRAY_SIZE(buttons); - status = HidP_GetUsages(HidP_Input, HID_USAGE_PAGE_VENDOR_DEFINED_BEGIN, 0, buttons, &button_length, controller->hid.preparsed, report_buf, report_len); - if (status != HIDP_STATUS_SUCCESS) WARN("HidP_GetUsages HID_USAGE_PAGE_VENDOR_DEFINED_BEGIN returned %#lx\n", status); - if (button_length) state.Gamepad.wButtons |= XINPUT_GAMEPAD_GUIDE; - status = HidP_GetUsageValue(HidP_Input, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_HATSWITCH, &value, controller->hid.preparsed, report_buf, report_len); if (status != HIDP_STATUS_SUCCESS) WARN("HidP_GetUsageValue HID_USAGE_PAGE_GENERIC / HID_USAGE_GENERIC_HATSWITCH returned %#lx\n", status); else switch (value) From c2ed36043b707871bd27359ec7f404d976f1a396 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 17 Sep 2025 16:52:21 +0200 Subject: [PATCH 072/454] Revert "windows.gaming.input: Use a generic dinput device data format." This reverts commit db5c19dd3ceb1e8443eed534b2f6a085f563678d. --- dlls/windows.gaming.input/gamepad.c | 8 +- dlls/windows.gaming.input/provider.c | 156 +-------------------------- 2 files changed, 5 insertions(+), 159 deletions(-) diff --git a/dlls/windows.gaming.input/gamepad.c b/dlls/windows.gaming.input/gamepad.c index ecac632ec3bc..ba0944cd6dae 100644 --- a/dlls/windows.gaming.input/gamepad.c +++ b/dlls/windows.gaming.input/gamepad.c @@ -321,11 +321,11 @@ static HRESULT WINAPI gamepad_GetCurrentReading( IGamepad *iface, struct Gamepad break; } - value->LeftThumbstickX = 2. * state.axes[1] - 1.; - value->LeftThumbstickY = 2. * state.axes[0] - 1.; - value->LeftTrigger = state.axes[4]; + value->LeftThumbstickX = 2. * state.axes[0] - 1.; + value->LeftThumbstickY = 2. * state.axes[1] - 1.; + value->LeftTrigger = state.axes[2]; value->RightThumbstickX = 2. * state.axes[3] - 1.; - value->RightThumbstickY = 2. * state.axes[2] - 1.; + value->RightThumbstickY = 2. * state.axes[4] - 1.; value->RightTrigger = state.axes[5]; value->Timestamp = state.timestamp; diff --git a/dlls/windows.gaming.input/provider.c b/dlls/windows.gaming.input/provider.c index b0c54044b668..e5db1e81a0be 100644 --- a/dlls/windows.gaming.input/provider.c +++ b/dlls/windows.gaming.input/provider.c @@ -629,160 +629,6 @@ static void open_haptics_device( struct provider *provider ) CloseHandle( device ); } -static const DIOBJECTDATAFORMAT data_format_objs[] = -{ - {NULL,DIJOFS_X,DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_Y,DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_Z,DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_RX,DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_RY,DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_RZ,DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_SLIDER(0),DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_SLIDER(1),DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_POV(0),DIDFT_OPTIONAL|DIDFT_POV|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_POV(1),DIDFT_OPTIONAL|DIDFT_POV|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_POV(2),DIDFT_OPTIONAL|DIDFT_POV|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_POV(3),DIDFT_OPTIONAL|DIDFT_POV|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(0),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(1),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(2),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(3),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(4),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(5),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(6),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(7),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(8),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(9),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(10),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(11),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(12),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(13),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(14),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(15),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(16),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(17),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(18),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(19),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(20),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(21),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(22),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(23),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(24),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(25),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(26),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(27),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(28),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(29),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(30),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(31),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(32),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(33),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(34),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(35),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(36),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(37),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(38),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(39),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(40),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(41),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(42),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(43),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(44),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(45),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(46),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(47),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(48),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(49),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(50),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(51),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(52),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(53),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(54),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(55),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(56),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(57),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(58),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(59),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(60),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(61),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(62),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(63),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(64),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(65),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(66),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(67),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(68),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(69),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(70),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(71),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(72),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(73),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(74),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(75),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(76),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(77),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(78),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(79),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(80),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(81),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(82),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(83),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(84),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(85),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(86),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(87),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(88),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(89),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(90),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(91),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(92),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(93),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(94),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(95),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(96),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(97),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(98),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(99),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(100),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(101),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(102),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(103),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(104),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(105),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(106),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(107),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(108),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(109),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(110),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(111),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(112),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(113),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(114),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(115),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(116),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(117),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(118),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(119),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(120),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(121),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(122),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(123),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(124),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(125),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(126),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, - {NULL,DIJOFS_BUTTON(127),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, -}; - -static const DIDATAFORMAT data_format = -{ - sizeof(DIDATAFORMAT), - sizeof(DIOBJECTDATAFORMAT), - DIDF_ABSAXIS, - sizeof(DIJOYSTATE2), - ARRAY_SIZE(data_format_objs), - (LPDIOBJECTDATAFORMAT)data_format_objs -}; - void provider_create( const WCHAR *device_path ) { IDirectInputDevice8W *dinput_device; @@ -807,7 +653,7 @@ void provider_create( const WCHAR *device_path ) if (FAILED(hr)) return; if (FAILED(hr = IDirectInputDevice8_SetCooperativeLevel( dinput_device, 0, DISCL_BACKGROUND | DISCL_NONEXCLUSIVE ))) goto done; - if (FAILED(hr = IDirectInputDevice8_SetDataFormat( dinput_device, &data_format ))) goto done; + if (FAILED(hr = IDirectInputDevice8_SetDataFormat( dinput_device, &c_dfDIJoystick2 ))) goto done; if (FAILED(hr = IDirectInputDevice8_Acquire( dinput_device ))) goto done; if (!(impl = calloc( 1, sizeof(*impl) ))) goto done; From e5dd606c59e8ba6d80651457d19235a35d800c7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 17 Sep 2025 16:52:24 +0200 Subject: [PATCH 073/454] Revert "winebus: Improve gamepad report compatibility with XUSB / GIP." This reverts commit 5f6e199fcec921581e6a39f6df7aeeed6dc8549e. --- dlls/windows.gaming.input/gamepad.c | 4 ++-- dlls/winebus.sys/bus_sdl.c | 11 ++--------- dlls/winebus.sys/bus_udev.c | 1 - dlls/winebus.sys/hid.c | 6 ++---- dlls/winexinput.sys/main.c | 4 ++-- dlls/xinput1_3/main.c | 4 ++-- 6 files changed, 10 insertions(+), 20 deletions(-) diff --git a/dlls/windows.gaming.input/gamepad.c b/dlls/windows.gaming.input/gamepad.c index ba0944cd6dae..def2c9dcf5ea 100644 --- a/dlls/windows.gaming.input/gamepad.c +++ b/dlls/windows.gaming.input/gamepad.c @@ -322,10 +322,10 @@ static HRESULT WINAPI gamepad_GetCurrentReading( IGamepad *iface, struct Gamepad } value->LeftThumbstickX = 2. * state.axes[0] - 1.; - value->LeftThumbstickY = 2. * state.axes[1] - 1.; + value->LeftThumbstickY = 1. - 2. * state.axes[1]; value->LeftTrigger = state.axes[2]; value->RightThumbstickX = 2. * state.axes[3] - 1.; - value->RightThumbstickY = 2. * state.axes[4] - 1.; + value->RightThumbstickY = 1. - 2. * state.axes[4]; value->RightTrigger = state.axes[5]; value->Timestamp = state.timestamp; diff --git a/dlls/winebus.sys/bus_sdl.c b/dlls/winebus.sys/bus_sdl.c index d9420a7bf083..b40e1244ed5f 100644 --- a/dlls/winebus.sys/bus_sdl.c +++ b/dlls/winebus.sys/bus_sdl.c @@ -416,13 +416,9 @@ static NTSTATUS build_controller_report_descriptor(struct unix_device *iface) if (!descriptor_add_haptic(impl, FALSE)) return STATUS_NO_MEMORY; if (!hid_device_end_report_descriptor(iface)) return STATUS_NO_MEMORY; + /* Initialize axis in the report */ for (int i = SDL_CONTROLLER_AXIS_LEFTX; i < SDL_CONTROLLER_AXIS_MAX; i++) - { - int value = pSDL_GameControllerGetAxis(impl->sdl_controller, i); - if (i == SDL_CONTROLLER_AXIS_LEFTY || i == SDL_CONTROLLER_AXIS_RIGHTY) - value = -value - 1; /* match XUSB / GIP protocol */ - hid_device_set_abs_axis(iface, i, value); - } + hid_device_set_abs_axis(iface, i, pSDL_GameControllerGetAxis(impl->sdl_controller, i)); state = pSDL_GameControllerGetButton(impl->sdl_controller, SDL_CONTROLLER_BUTTON_DPAD_UP); hid_device_move_hatswitch(iface, 0, 0, state ? -1 : +1); @@ -911,9 +907,6 @@ static BOOL set_report_from_controller_event(struct sdl_device *impl, SDL_Event { SDL_ControllerAxisEvent *ie = &event->caxis; - if (ie->axis == SDL_CONTROLLER_AXIS_LEFTY || ie->axis == SDL_CONTROLLER_AXIS_RIGHTY) - ie->value = -ie->value - 1; /* match XUSB / GIP protocol */ - hid_device_set_abs_axis(iface, ie->axis, ie->value); bus_event_queue_input_report(&event_queue, iface, state->report_buf, state->report_len); break; diff --git a/dlls/winebus.sys/bus_udev.c b/dlls/winebus.sys/bus_udev.c index 2274cdfd20f8..dd3a596c5717 100644 --- a/dlls/winebus.sys/bus_udev.c +++ b/dlls/winebus.sys/bus_udev.c @@ -847,7 +847,6 @@ static void set_abs_axis_value(struct unix_device *iface, int code, int value) { double scale = (code == 5 || code == 6 ? 32767.0 : 65535.0) / range; value = (value - min) * scale - (code == 5 || code == 6 ? 0 : 32768); - if (code == 2 || code == 4) value = -value - 1; /* match XUSB / GIP protocol */ } hid_device_set_abs_axis(iface, code - 1, value); diff --git a/dlls/winebus.sys/hid.c b/dlls/winebus.sys/hid.c index bb1ddbdabdb9..693697fa8c00 100644 --- a/dlls/winebus.sys/hid.c +++ b/dlls/winebus.sys/hid.c @@ -348,14 +348,12 @@ BOOL hid_device_add_gamepad(struct unix_device *iface) static const USAGE_AND_PAGE device_usage = {.UsagePage = HID_USAGE_PAGE_GENERIC, .Usage = HID_USAGE_GENERIC_GAMEPAD}; static const USAGE left[] = {HID_USAGE_GENERIC_X, HID_USAGE_GENERIC_Y}; static const USAGE right[] = {HID_USAGE_GENERIC_RX, HID_USAGE_GENERIC_RY}; - static const USAGE lt = HID_USAGE_GENERIC_Z; - static const USAGE rt = HID_USAGE_GENERIC_RZ; + static const USAGE trigger[] = {HID_USAGE_GENERIC_Z, HID_USAGE_GENERIC_RZ}; if (!hid_device_begin_input_report(iface, &device_usage)) return FALSE; if (!hid_device_add_axes(iface, 2, HID_USAGE_PAGE_GENERIC, left, FALSE, -32768, 32767)) return FALSE; if (!hid_device_add_axes(iface, 2, HID_USAGE_PAGE_GENERIC, right, FALSE, -32768, 32767)) return FALSE; - if (!hid_device_add_axes(iface, 1, HID_USAGE_PAGE_GENERIC, <, FALSE, 0, 32767)) return FALSE; - if (!hid_device_add_axes(iface, 1, HID_USAGE_PAGE_GENERIC, &rt, FALSE, 0, 32767)) return FALSE; + if (!hid_device_add_axes(iface, 2, HID_USAGE_PAGE_GENERIC, trigger, FALSE, 0, 32767)) return FALSE; if (!hid_device_add_hatswitch(iface, 1)) return FALSE; if (!hid_device_add_buttons(iface, HID_USAGE_PAGE_BUTTON, 1, 15)) return FALSE; if (!hid_device_end_input_report(iface)) return FALSE; diff --git a/dlls/winexinput.sys/main.c b/dlls/winexinput.sys/main.c index 48743d6adf67..45c6835bfe7a 100644 --- a/dlls/winexinput.sys/main.c +++ b/dlls/winexinput.sys/main.c @@ -236,9 +236,9 @@ static void translate_report_to_xinput_state(struct func_device *fdo) fdo->xinput_state.buttons |= (1 << (usages[i] - 1)); } fdo->xinput_state.lx_axis = scale_value(lx, &fdo->lx_caps, 0, 65535); - fdo->xinput_state.ly_axis = scale_value(-ly - 1, &fdo->ly_caps, 0, 65535); + fdo->xinput_state.ly_axis = scale_value(ly, &fdo->ly_caps, 0, 65535); fdo->xinput_state.rx_axis = scale_value(rx, &fdo->rx_caps, 0, 65535); - fdo->xinput_state.ry_axis = scale_value(-ry - 1, &fdo->ry_caps, 0, 65535); + fdo->xinput_state.ry_axis = scale_value(ry, &fdo->ry_caps, 0, 65535); rt = scale_value(rt, &fdo->rt_caps, 0, 255); lt = scale_value(lt, &fdo->lt_caps, 0, 255); fdo->xinput_state.trigger = 0x8000 + (lt - rt) * 128; diff --git a/dlls/xinput1_3/main.c b/dlls/xinput1_3/main.c index c45f7570bd1a..dfce8312bdcf 100644 --- a/dlls/xinput1_3/main.c +++ b/dlls/xinput1_3/main.c @@ -622,7 +622,7 @@ static void read_controller_state(struct xinput_controller *controller) status = HidP_GetUsageValue(HidP_Input, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_Y, &value, controller->hid.preparsed, report_buf, report_len); if (status != HIDP_STATUS_SUCCESS) WARN("HidP_GetUsageValue HID_USAGE_PAGE_GENERIC / HID_USAGE_GENERIC_Y returned %#lx\n", status); - else state.Gamepad.sThumbLY = scale_value(value, &controller->hid.ly_caps, -32768, 32767); + else state.Gamepad.sThumbLY = -scale_value(value, &controller->hid.ly_caps, -32768, 32767) - 1; status = HidP_GetUsageValue(HidP_Input, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_RX, &value, controller->hid.preparsed, report_buf, report_len); if (status != HIDP_STATUS_SUCCESS) WARN("HidP_GetUsageValue HID_USAGE_PAGE_GENERIC / HID_USAGE_GENERIC_RX returned %#lx\n", status); @@ -630,7 +630,7 @@ static void read_controller_state(struct xinput_controller *controller) status = HidP_GetUsageValue(HidP_Input, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_RY, &value, controller->hid.preparsed, report_buf, report_len); if (status != HIDP_STATUS_SUCCESS) WARN("HidP_GetUsageValue HID_USAGE_PAGE_GENERIC / HID_USAGE_GENERIC_RY returned %#lx\n", status); - else state.Gamepad.sThumbRY = scale_value(value, &controller->hid.ry_caps, -32768, 32767); + else state.Gamepad.sThumbRY = -scale_value(value, &controller->hid.ry_caps, -32768, 32767) - 1; status = HidP_GetUsageValue(HidP_Input, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_RZ, &value, controller->hid.preparsed, report_buf, report_len); if (status != HIDP_STATUS_SUCCESS) WARN("HidP_GetUsageValue HID_USAGE_PAGE_GENERIC / HID_USAGE_GENERIC_RZ returned %#lx\n", status); From 2f01e1674310c85fecc4a4f837cee5bc519bc2bd Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Fri, 4 Jul 2025 17:05:54 -0400 Subject: [PATCH 074/454] wine.inf: Add Name and Age fields to protontts entries. CW-Bug-Id: #22894 --- loader/wine.inf.in | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/loader/wine.inf.in b/loader/wine.inf.in index 4c5edde1d42f..bcb1f5dc362c 100644 --- a/loader/wine.inf.in +++ b/loader/wine.inf.in @@ -2915,8 +2915,10 @@ HKLM,Software\Microsoft\Speech\Voices\Tokens\ProtonTTS_libritts-r_3,"CLSID",,"{5 HKLM,Software\Microsoft\Speech\Voices\Tokens\ProtonTTS_libritts-r_3,"LengthScale",,"1.4" HKLM,Software\Microsoft\Speech\Voices\Tokens\ProtonTTS_libritts-r_3,"ModelPath",,"en_US-libritts_r-medium.onnx" HKLM,Software\Microsoft\Speech\Voices\Tokens\ProtonTTS_libritts-r_3,"SpeakerID",,"3" +HKLM,Software\Microsoft\Speech\Voices\Tokens\ProtonTTS_libritts-r_3\Attributes,"Name",,"Proton Voice - libritts-r 3" HKLM,Software\Microsoft\Speech\Voices\Tokens\ProtonTTS_libritts-r_3\Attributes,"Language",,"409" HKLM,Software\Microsoft\Speech\Voices\Tokens\ProtonTTS_libritts-r_3\Attributes,"Gender",,"Male" +HKLM,Software\Microsoft\Speech\Voices\Tokens\ProtonTTS_libritts-r_3\Attributes,"Age",,"Adult" HKLM,Software\Microsoft\Speech\Voices\Tokens\ProtonTTS_libritts-r_3\Attributes,"Vendor",,"Wine" HKLM,Software\Microsoft\Speech\Voices\Tokens\ProtonTTS_libritts-r_14,,,"Proton Voice - English (United States) - libritts-r 14" HKLM,Software\Microsoft\Speech\Voices\Tokens\ProtonTTS_libritts-r_14,"409",,"Proton Voice - English (United States) - libritts-r 14" @@ -2924,8 +2926,10 @@ HKLM,Software\Microsoft\Speech\Voices\Tokens\ProtonTTS_libritts-r_14,"CLSID",,"{ HKLM,Software\Microsoft\Speech\Voices\Tokens\ProtonTTS_libritts-r_14,"LengthScale",,"1.4" HKLM,Software\Microsoft\Speech\Voices\Tokens\ProtonTTS_libritts-r_14,"ModelPath",,"en_US-libritts_r-medium.onnx" HKLM,Software\Microsoft\Speech\Voices\Tokens\ProtonTTS_libritts-r_14,"SpeakerID",,"14" +HKLM,Software\Microsoft\Speech\Voices\Tokens\ProtonTTS_libritts-r_14\Attributes,"Name",,"Proton Voice - libritts-r 14" HKLM,Software\Microsoft\Speech\Voices\Tokens\ProtonTTS_libritts-r_14\Attributes,"Language",,"409" HKLM,Software\Microsoft\Speech\Voices\Tokens\ProtonTTS_libritts-r_14\Attributes,"Gender",,"Female" +HKLM,Software\Microsoft\Speech\Voices\Tokens\ProtonTTS_libritts-r_14\Attributes,"Age",,"Adult" HKLM,Software\Microsoft\Speech\Voices\Tokens\ProtonTTS_libritts-r_14\Attributes,"Vendor",,"Wine" HKLM,Software\Microsoft\Speech_OneCore\Voices,"DefaultDefaultTokenId",,"HKEY_LOCAL_MACHINE\Software\Microsoft\Speech_OneCore\Voices\Tokens\ProtonTTS_libritts-r_3" HKLM,Software\Microsoft\Speech_OneCore\Voices\Tokens\ProtonTTS_libritts-r_3,,,"Proton Voice - English (United States) - libritts-r 3" @@ -2934,8 +2938,10 @@ HKLM,Software\Microsoft\Speech_OneCore\Voices\Tokens\ProtonTTS_libritts-r_3,"CLS HKLM,Software\Microsoft\Speech_OneCore\Voices\Tokens\ProtonTTS_libritts-r_3,"LengthScale",,"1.4" HKLM,Software\Microsoft\Speech_OneCore\Voices\Tokens\ProtonTTS_libritts-r_3,"ModelPath",,"en_US-libritts_r-medium.onnx" HKLM,Software\Microsoft\Speech_OneCore\Voices\Tokens\ProtonTTS_libritts-r_3,"SpeakerID",,"3" +HKLM,Software\Microsoft\Speech_OneCore\Voices\Tokens\ProtonTTS_libritts-r_3\Attributes,"Name",,"Proton Voice - libritts-r 3" HKLM,Software\Microsoft\Speech_OneCore\Voices\Tokens\ProtonTTS_libritts-r_3\Attributes,"Language",,"409" HKLM,Software\Microsoft\Speech_OneCore\Voices\Tokens\ProtonTTS_libritts-r_3\Attributes,"Gender",,"Male" +HKLM,Software\Microsoft\Speech_OneCore\Voices\Tokens\ProtonTTS_libritts-r_3\Attributes,"Age",,"Adult" HKLM,Software\Microsoft\Speech_OneCore\Voices\Tokens\ProtonTTS_libritts-r_3\Attributes,"Vendor",,"Wine" HKLM,Software\Microsoft\Speech_OneCore\Voices\Tokens\ProtonTTS_libritts-r_14,,,"Proton Voice - English (United States) - libritts-r 14" HKLM,Software\Microsoft\Speech_OneCore\Voices\Tokens\ProtonTTS_libritts-r_14,"409",,"Proton Voice - English (United States) - libritts-r 14" @@ -2943,8 +2949,10 @@ HKLM,Software\Microsoft\Speech_OneCore\Voices\Tokens\ProtonTTS_libritts-r_14,"CL HKLM,Software\Microsoft\Speech_OneCore\Voices\Tokens\ProtonTTS_libritts-r_14,"LengthScale",,"1.4" HKLM,Software\Microsoft\Speech_OneCore\Voices\Tokens\ProtonTTS_libritts-r_14,"ModelPath",,"en_US-libritts_r-medium.onnx" HKLM,Software\Microsoft\Speech_OneCore\Voices\Tokens\ProtonTTS_libritts-r_14,"SpeakerID",,"14" +HKLM,Software\Microsoft\Speech_OneCore\Voices\Tokens\ProtonTTS_libritts-r_14\Attributes,"Name",,"Proton Voice - libritts-r 14" HKLM,Software\Microsoft\Speech_OneCore\Voices\Tokens\ProtonTTS_libritts-r_14\Attributes,"Language",,"409" HKLM,Software\Microsoft\Speech_OneCore\Voices\Tokens\ProtonTTS_libritts-r_14\Attributes,"Gender",,"Female" +HKLM,Software\Microsoft\Speech_OneCore\Voices\Tokens\ProtonTTS_libritts-r_14\Attributes,"Age",,"Adult" HKLM,Software\Microsoft\Speech_OneCore\Voices\Tokens\ProtonTTS_libritts-r_14\Attributes,"Vendor",,"Wine" [ProtonOverrides] From b79acccb1ca9d98c475e5b5020fe2cea028a567c Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Mon, 7 Jul 2025 18:22:22 -0400 Subject: [PATCH 075/454] protontts/tts: Implement volume support. CW-Bug-Id: #22894 --- dlls/protontts/tts.c | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/dlls/protontts/tts.c b/dlls/protontts/tts.c index f0f19d6ba210..b9036a88aab5 100644 --- a/dlls/protontts/tts.c +++ b/dlls/protontts/tts.c @@ -166,6 +166,23 @@ static ULONG WINAPI ttsengine_Release(ISpTTSEngine *iface) return ref; } +static void adjust_volume(int16_t *buf, UINT32 num, ULONG volume) +{ + UINT32 i; + + if (volume >= 10000) return; + + for (i = 0; i < num; i++) + { + int x = buf[i] * (int)volume; + + if (x > 0) + buf[i] = (x + 5000) / 10000; + else + buf[i] = (x - 5000) / 10000; + } +} + static DWORD CALLBACK synthesize_thread_proc(void *params) { SetThreadDescription(GetCurrentThread(), L"protontts_synthesize"); @@ -177,6 +194,7 @@ static HRESULT WINAPI ttsengine_Speak(ISpTTSEngine *iface, DWORD flags, REFGUID ISpTTSEngineSite *site) { struct ttsengine *This = impl_from_ISpTTSEngine(iface); + USHORT global_volume = 100; HANDLE abort_event; HANDLE thread = NULL; char *text = NULL; @@ -220,20 +238,27 @@ static HRESULT WINAPI ttsengine_Speak(ISpTTSEngine *iface, DWORD flags, REFGUID for (done = false; !done;) { + DWORD actions; void *buf; UINT32 size; Sleep(50); - if (ISpTTSEngineSite_GetActions(site) & SPVES_ABORT) + actions = ISpTTSEngineSite_GetActions(site); + if (actions & SPVES_ABORT) { SetEvent(abort_event); goto done; } + if (actions & SPVES_VOLUME) + ISpTTSEngineSite_GetVolume(site, &global_volume); tts_voice_audio_lock(This->voice, &buf, &size, &done); if (buf) + { + adjust_volume(buf, size / sizeof(int16_t), global_volume * frag_list->State.Volume); hr = ISpTTSEngineSite_Write(site, buf, size, NULL); + } WINE_UNIX_CALL(unix_tts_voice_audio_release, &This->voice); if (FAILED(hr)) From 2b84e5610f13aa1b0851b3d8567fa99b0704a90e Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Sun, 12 Jan 2025 20:37:51 -0500 Subject: [PATCH 076/454] sapi: Adding missing interfaces for SpStream. (cherry picked from commit 3f8cbfb9d6e3bfe81577506c73584030e2f4775c) CW-Bug-Id: #22894 --- dlls/sapi/stream.c | 3 +++ dlls/sapi/tests/stream.c | 27 ++++++++++++++++++++++++++- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/dlls/sapi/stream.c b/dlls/sapi/stream.c index 5cd64125ade9..c3ecdf426975 100644 --- a/dlls/sapi/stream.c +++ b/dlls/sapi/stream.c @@ -52,6 +52,9 @@ static HRESULT WINAPI spstream_QueryInterface(ISpStream *iface, REFIID iid, void TRACE("(%p, %s, %p).\n", iface, debugstr_guid(iid), obj); if (IsEqualIID(iid, &IID_IUnknown) || + IsEqualIID(iid, &IID_ISequentialStream) || + IsEqualIID(iid, &IID_IStream) || + IsEqualIID(iid, &IID_ISpStreamFormat) || IsEqualIID(iid, &IID_ISpStream)) *obj = &This->ISpStream_iface; else diff --git a/dlls/sapi/tests/stream.c b/dlls/sapi/tests/stream.c index ea2608eed363..ee9a7648aa88 100644 --- a/dlls/sapi/tests/stream.c +++ b/dlls/sapi/tests/stream.c @@ -36,14 +36,17 @@ static void _expect_ref(IUnknown *obj, ULONG ref, int line) static void test_interfaces(void) { + ISpStreamFormat *stream_format; + ISequentialStream *seq_stream; ISpStream *speech_stream; IDispatch *dispatch; + IStream *stream; IUnknown *unk; HRESULT hr; hr = CoCreateInstance(&CLSID_SpStream, NULL, CLSCTX_INPROC_SERVER, &IID_ISpStream, (void **)&speech_stream); - ok(hr == S_OK, "Failed to create ISpeechVoice interface: %#lx.\n", hr); + ok(hr == S_OK, "Failed to create ISpStream interface: %#lx.\n", hr); EXPECT_REF(speech_stream, 1); hr = CoCreateInstance(&CLSID_SpStream, NULL, CLSCTX_INPROC_SERVER, @@ -58,6 +61,28 @@ static void test_interfaces(void) ok(hr == E_NOINTERFACE, "Succeeded to create IDispatch interface: %#lx.\n", hr); ok(!dispatch, "Expected NULL dispatch, got %p.", dispatch); + hr = CoCreateInstance(&CLSID_SpStream, NULL, CLSCTX_INPROC_SERVER, + &IID_ISequentialStream, (void **)&seq_stream); + ok(hr == S_OK, "Failed to create ISequentialStream interface: %#lx.\n", hr); + EXPECT_REF(seq_stream, 1); + EXPECT_REF(speech_stream, 1); + ISequentialStream_Release(seq_stream); + + hr = CoCreateInstance(&CLSID_SpStream, NULL, CLSCTX_INPROC_SERVER, + &IID_IStream, (void **)&stream); + ok(hr == S_OK, "Failed to create IStream interface: %#lx.\n", hr); + EXPECT_REF(stream, 1); + EXPECT_REF(speech_stream, 1); + IStream_Release(stream); + + hr = CoCreateInstance(&CLSID_SpStream, NULL, CLSCTX_INPROC_SERVER, + &IID_ISpStreamFormat, (void **)&stream_format); + ok(hr == S_OK, "Failed to create ISpStreamFormat interface: %#lx.\n", hr); + EXPECT_REF(stream_format, 1); + EXPECT_REF(speech_stream, 1); + ISpStreamFormat_Release(stream_format); + + ISpStream_Release(speech_stream); } From 968e630197bfab078233919ae3651a096cfeb918 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Sun, 12 Jan 2025 21:46:20 -0500 Subject: [PATCH 077/454] sapi: Implement ISpStream::Set/GetBaseStream. (cherry picked from commit 8283a595131ddb0dcd02ce7289e1c7d164788779) CW-Bug-Id: #22894 --- dlls/sapi/stream.c | 52 +++++++++++++++++++++++++++++---- dlls/sapi/tests/stream.c | 62 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 109 insertions(+), 5 deletions(-) diff --git a/dlls/sapi/stream.c b/dlls/sapi/stream.c index c3ecdf426975..ae0e8c048c92 100644 --- a/dlls/sapi/stream.c +++ b/dlls/sapi/stream.c @@ -27,6 +27,7 @@ #include "objbase.h" #include "sapiddk.h" +#include "sperror.h" #include "wine/debug.h" @@ -38,6 +39,10 @@ struct spstream { ISpStream ISpStream_iface; LONG ref; + + IStream *base_stream; + GUID format; + WAVEFORMATEX *wfx; }; static inline struct spstream *impl_from_ISpStream(ISpStream *iface) @@ -87,6 +92,8 @@ static ULONG WINAPI spstream_Release(ISpStream *iface) if (!ref) { + if (This->base_stream) IStream_Release(This->base_stream); + free(This->wfx); free(This); } @@ -182,18 +189,49 @@ static HRESULT WINAPI spstream_GetFormat(ISpStream *iface, GUID *format, WAVEFOR } static HRESULT WINAPI spstream_SetBaseStream(ISpStream *iface, IStream *stream, REFGUID format, - const WAVEFORMATEX *wave) + const WAVEFORMATEX *wfx) { - FIXME("(%p, %p, %s, %p): stub.\n", iface, stream, debugstr_guid(format), wave); + struct spstream *This = impl_from_ISpStream(iface); - return E_NOTIMPL; + TRACE("(%p, %p, %s, %p).\n", iface, stream, debugstr_guid(format), wfx); + + if (!stream || !format) + return E_INVALIDARG; + + if (This->base_stream) + return SPERR_ALREADY_INITIALIZED; + + This->format = *format; + if (IsEqualGUID(format, &SPDFID_WaveFormatEx)) + { + if (!wfx) + return E_INVALIDARG; + if (!(This->wfx = malloc(sizeof(WAVEFORMATEX) + wfx->cbSize))) + return E_OUTOFMEMORY; + memcpy(This->wfx, wfx, sizeof(WAVEFORMATEX) + wfx->cbSize); + } + + IStream_AddRef(stream); + This->base_stream = stream; + return S_OK; } static HRESULT WINAPI spstream_GetBaseStream(ISpStream *iface, IStream **stream) { - FIXME("(%p, %p): stub.\n", iface, stream); + struct spstream *This = impl_from_ISpStream(iface); - return E_NOTIMPL; + TRACE("(%p, %p).\n", iface, stream); + + if (!stream) + return E_INVALIDARG; + + if (!This->base_stream) + return SPERR_UNINITIALIZED; + + *stream = This->base_stream; + if (*stream) + IStream_AddRef(*stream); + return S_OK; } static HRESULT WINAPI spstream_BindToFile(ISpStream *iface, LPCWSTR filename, SPFILEMODE mode, @@ -245,6 +283,10 @@ HRESULT speech_stream_create(IUnknown *outer, REFIID iid, void **obj) This->ISpStream_iface.lpVtbl = &spstream_vtbl; This->ref = 1; + This->base_stream = NULL; + This->format = GUID_NULL; + This->wfx = NULL; + hr = ISpStream_QueryInterface(&This->ISpStream_iface, iid, obj); ISpStream_Release(&This->ISpStream_iface); diff --git a/dlls/sapi/tests/stream.c b/dlls/sapi/tests/stream.c index ee9a7648aa88..6cb21851ee40 100644 --- a/dlls/sapi/tests/stream.c +++ b/dlls/sapi/tests/stream.c @@ -86,9 +86,71 @@ static void test_interfaces(void) ISpStream_Release(speech_stream); } +static void test_spstream(void) +{ + ISpStream *stream; + ISpMMSysAudio *mmaudio; + IStream *base_stream, *base_stream2; + GUID fmtid; + WAVEFORMATEX *wfx = NULL; + HRESULT hr; + + hr = CoCreateInstance(&CLSID_SpMMAudioOut, NULL, CLSCTX_INPROC_SERVER, + &IID_ISpMMSysAudio, (void **)&mmaudio); + ok(hr == S_OK, "Failed to create ISpMMSysAudio interface: %#lx.\n", hr); + + hr = ISpMMSysAudio_GetFormat(mmaudio, &fmtid, &wfx); + ok(hr == S_OK, "got %#lx.\n", hr); + ok(IsEqualGUID(&fmtid, &SPDFID_WaveFormatEx), "got %s.\n", wine_dbgstr_guid(&fmtid)); + + hr = ISpMMSysAudio_QueryInterface(mmaudio, &IID_IStream, (void **)&base_stream); + ok(hr == S_OK, "Failed to get IStream interface from mmaudio: %#lx.\n", hr); + + hr = CoCreateInstance(&CLSID_SpStream, NULL, CLSCTX_INPROC_SERVER, + &IID_ISpStream, (void **)&stream); + ok(hr == S_OK, "Failed to create ISpStream interface: %#lx.\n", hr); + + hr = ISpStream_SetBaseStream(stream, NULL, NULL, NULL); + ok(hr == E_INVALIDARG, "got %#lx.\n", hr); + + hr = ISpStream_SetBaseStream(stream, base_stream, NULL, NULL); + ok(hr == E_INVALIDARG, "got %#lx.\n", hr); + + hr = ISpStream_GetBaseStream(stream, &base_stream2); + ok(hr == SPERR_UNINITIALIZED, "got %#lx.\n", hr); + + hr = ISpStream_SetBaseStream(stream, base_stream, &SPDFID_Text, NULL); + ok(hr == S_OK, "got %#lx.\n", hr); + + hr = ISpStream_SetBaseStream(stream, base_stream, &fmtid, wfx); + ok(hr == SPERR_ALREADY_INITIALIZED, "got %#lx.\n", hr); + + ISpStream_Release(stream); + + hr = CoCreateInstance(&CLSID_SpStream, NULL, CLSCTX_INPROC_SERVER, + &IID_ISpStream, (void **)&stream); + ok(hr == S_OK, "Failed to create ISpStream interface: %#lx.\n", hr); + + hr = ISpStream_SetBaseStream(stream, base_stream, &SPDFID_WaveFormatEx, NULL); + ok(hr == E_INVALIDARG, "got %#lx.\n", hr); + + hr = ISpStream_SetBaseStream(stream, base_stream, &SPDFID_WaveFormatEx, wfx); + ok(hr == S_OK, "got %#lx.\n", hr); + + hr = ISpStream_GetBaseStream(stream, &base_stream2); + ok(hr == S_OK, "got %#lx.\n", hr); + ok(base_stream2 == base_stream, "got %p.\n", base_stream2); + IStream_Release(base_stream2); + + ISpStream_Release(stream); + IStream_Release(base_stream); + ISpMMSysAudio_Release(mmaudio); +} + START_TEST(stream) { CoInitialize(NULL); test_interfaces(); + test_spstream(); CoUninitialize(); } From 5ceeb5dd65b31164ed46b5f6f2605f6cc53cc2f5 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Sun, 12 Jan 2025 21:54:48 -0500 Subject: [PATCH 078/454] sapi: Implement ISpStream::Close. (cherry picked from commit 0b08214bd705ebf671ac1b911351425820929039) CW-Bug-Id: #22894 --- dlls/sapi/stream.c | 22 ++++++++++++++++++---- dlls/sapi/tests/stream.c | 12 ++++++++++++ 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/dlls/sapi/stream.c b/dlls/sapi/stream.c index ae0e8c048c92..f0b4e2cd595f 100644 --- a/dlls/sapi/stream.c +++ b/dlls/sapi/stream.c @@ -43,6 +43,7 @@ struct spstream IStream *base_stream; GUID format; WAVEFORMATEX *wfx; + BOOL closed; }; static inline struct spstream *impl_from_ISpStream(ISpStream *iface) @@ -198,7 +199,7 @@ static HRESULT WINAPI spstream_SetBaseStream(ISpStream *iface, IStream *stream, if (!stream || !format) return E_INVALIDARG; - if (This->base_stream) + if (This->base_stream || This->closed) return SPERR_ALREADY_INITIALIZED; This->format = *format; @@ -225,7 +226,9 @@ static HRESULT WINAPI spstream_GetBaseStream(ISpStream *iface, IStream **stream) if (!stream) return E_INVALIDARG; - if (!This->base_stream) + if (This->closed) + return SPERR_STREAM_CLOSED; + else if (!This->base_stream) return SPERR_UNINITIALIZED; *stream = This->base_stream; @@ -246,9 +249,19 @@ static HRESULT WINAPI spstream_BindToFile(ISpStream *iface, LPCWSTR filename, SP static HRESULT WINAPI spstream_Close(ISpStream *iface) { - FIXME("(%p): stub.\n", iface); + struct spstream *This = impl_from_ISpStream(iface); - return E_NOTIMPL; + TRACE("(%p).\n", iface); + + if (This->closed) + return SPERR_STREAM_CLOSED; + else if (!This->base_stream) + return SPERR_UNINITIALIZED; + + IStream_Release(This->base_stream); + This->base_stream = NULL; + This->closed = TRUE; + return S_OK; } const static ISpStreamVtbl spstream_vtbl = @@ -286,6 +299,7 @@ HRESULT speech_stream_create(IUnknown *outer, REFIID iid, void **obj) This->base_stream = NULL; This->format = GUID_NULL; This->wfx = NULL; + This->closed = FALSE; hr = ISpStream_QueryInterface(&This->ISpStream_iface, iid, obj); diff --git a/dlls/sapi/tests/stream.c b/dlls/sapi/tests/stream.c index 6cb21851ee40..f7190db202b0 100644 --- a/dlls/sapi/tests/stream.c +++ b/dlls/sapi/tests/stream.c @@ -142,6 +142,18 @@ static void test_spstream(void) ok(base_stream2 == base_stream, "got %p.\n", base_stream2); IStream_Release(base_stream2); + hr = ISpStream_Close(stream); + ok(hr == S_OK, "got %#lx.\n", hr); + + hr = ISpStream_SetBaseStream(stream, base_stream, &fmtid, wfx); + ok(hr == SPERR_ALREADY_INITIALIZED, "got %#lx.\n", hr); + + hr = ISpStream_GetBaseStream(stream, &base_stream2); + ok(hr == SPERR_STREAM_CLOSED, "got %#lx.\n", hr); + + hr = ISpStream_Close(stream); + ok(hr == SPERR_STREAM_CLOSED, "got %#lx.\n", hr); + ISpStream_Release(stream); IStream_Release(base_stream); ISpMMSysAudio_Release(mmaudio); From 80eeab77f85ea8cc5dfd4676aed5e2f995777a0b Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Sun, 12 Jan 2025 22:09:34 -0500 Subject: [PATCH 079/454] sapi: Implement ISpStream::GetFormat. (cherry picked from commit 94cf8b9a42706add8edc96c3af1b3a06f923c77b) CW-Bug-Id: #22894 --- dlls/sapi/stream.c | 27 ++++++++++++++++++++++++--- dlls/sapi/tests/stream.c | 22 ++++++++++++++++++++-- 2 files changed, 44 insertions(+), 5 deletions(-) diff --git a/dlls/sapi/stream.c b/dlls/sapi/stream.c index f0b4e2cd595f..b0e5c2d5d0ad 100644 --- a/dlls/sapi/stream.c +++ b/dlls/sapi/stream.c @@ -182,11 +182,32 @@ static HRESULT WINAPI spstream_Clone(ISpStream *iface, IStream **stream) return E_NOTIMPL; } -static HRESULT WINAPI spstream_GetFormat(ISpStream *iface, GUID *format, WAVEFORMATEX **wave) +static HRESULT WINAPI spstream_GetFormat(ISpStream *iface, GUID *format, WAVEFORMATEX **wfx) { - FIXME("(%p, %p, %p): stub.\n", iface, format, wave); + struct spstream *This = impl_from_ISpStream(iface); - return E_NOTIMPL; + TRACE("(%p, %p, %p).\n", iface, format, wfx); + + if (!format) + return E_POINTER; + + if (This->closed) + return SPERR_STREAM_CLOSED; + else if (!This->base_stream) + return SPERR_UNINITIALIZED; + + if (This->wfx) + { + if (!wfx) + return E_POINTER; + if (!(*wfx = CoTaskMemAlloc(sizeof(WAVEFORMATEX) + This->wfx->cbSize))) + return E_OUTOFMEMORY; + memcpy(*wfx, This->wfx, sizeof(WAVEFORMATEX) + This->wfx->cbSize); + } + + *format = This->format; + + return S_OK; } static HRESULT WINAPI spstream_SetBaseStream(ISpStream *iface, IStream *stream, REFGUID format, diff --git a/dlls/sapi/tests/stream.c b/dlls/sapi/tests/stream.c index f7190db202b0..5d28ab13d95e 100644 --- a/dlls/sapi/tests/stream.c +++ b/dlls/sapi/tests/stream.c @@ -91,8 +91,8 @@ static void test_spstream(void) ISpStream *stream; ISpMMSysAudio *mmaudio; IStream *base_stream, *base_stream2; - GUID fmtid; - WAVEFORMATEX *wfx = NULL; + GUID fmtid, fmtid2; + WAVEFORMATEX *wfx = NULL, *wfx2 = NULL; HRESULT hr; hr = CoCreateInstance(&CLSID_SpMMAudioOut, NULL, CLSCTX_INPROC_SERVER, @@ -131,6 +131,9 @@ static void test_spstream(void) &IID_ISpStream, (void **)&stream); ok(hr == S_OK, "Failed to create ISpStream interface: %#lx.\n", hr); + hr = ISpStream_GetFormat(stream, &fmtid2, &wfx2); + ok(hr == SPERR_UNINITIALIZED, "got %#lx.\n", hr); + hr = ISpStream_SetBaseStream(stream, base_stream, &SPDFID_WaveFormatEx, NULL); ok(hr == E_INVALIDARG, "got %#lx.\n", hr); @@ -142,6 +145,18 @@ static void test_spstream(void) ok(base_stream2 == base_stream, "got %p.\n", base_stream2); IStream_Release(base_stream2); + hr = ISpStream_GetFormat(stream, NULL, NULL); + ok(hr == E_POINTER, "got %#lx.\n", hr); + + hr = ISpStream_GetFormat(stream, &fmtid2, NULL); + ok(hr == E_POINTER, "got %#lx.\n", hr); + + hr = ISpStream_GetFormat(stream, &fmtid2, &wfx2); + ok(hr == S_OK, "got %#lx.\n", hr); + ok(IsEqualGUID(&fmtid2, &SPDFID_WaveFormatEx), "got %s.\n", wine_dbgstr_guid(&fmtid2)); + ok(!memcmp(wfx, wfx2, sizeof(WAVEFORMATEX)), "wfx mismatch.\n"); + CoTaskMemFree(wfx2); + hr = ISpStream_Close(stream); ok(hr == S_OK, "got %#lx.\n", hr); @@ -151,6 +166,9 @@ static void test_spstream(void) hr = ISpStream_GetBaseStream(stream, &base_stream2); ok(hr == SPERR_STREAM_CLOSED, "got %#lx.\n", hr); + hr = ISpStream_GetFormat(stream, &fmtid2, &wfx2); + ok(hr == SPERR_STREAM_CLOSED, "got %#lx.\n", hr); + hr = ISpStream_Close(stream); ok(hr == SPERR_STREAM_CLOSED, "got %#lx.\n", hr); From 69335663ada3e9d433a7e43f32521ab25ba811d0 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Sun, 12 Jan 2025 22:26:53 -0500 Subject: [PATCH 080/454] sapi: Implement IStream methods for SpStream. (cherry picked from commit e1747b5dd7cd3a0ad36ad5e6b315b9a22cfa2c96) CW-Bug-Id: #22894 --- dlls/sapi/stream.c | 113 +++++++++++++++++++++++++++++++-------- dlls/sapi/tests/stream.c | 112 +++++++++++++++++++++++++++++++++++--- 2 files changed, 197 insertions(+), 28 deletions(-) diff --git a/dlls/sapi/stream.c b/dlls/sapi/stream.c index b0e5c2d5d0ad..41826b8c9493 100644 --- a/dlls/sapi/stream.c +++ b/dlls/sapi/stream.c @@ -103,81 +103,150 @@ static ULONG WINAPI spstream_Release(ISpStream *iface) static HRESULT WINAPI spstream_Read(ISpStream *iface, void *pv, ULONG cb, ULONG *read) { - FIXME("(%p, %p, %ld, %p): stub.\n", iface, pv, cb, read); + struct spstream *This = impl_from_ISpStream(iface); - return E_NOTIMPL; + TRACE("(%p, %p, %ld, %p).\n", iface, pv, cb, read); + + if (This->closed) + return SPERR_STREAM_CLOSED; + else if (!This->base_stream) + return SPERR_UNINITIALIZED; + + return IStream_Read(This->base_stream, pv, cb, read); } static HRESULT WINAPI spstream_Write(ISpStream *iface, const void *pv, ULONG cb, ULONG *written) { - FIXME("(%p, %p, %ld, %p): stub.\n", iface, pv, cb, written); + struct spstream *This = impl_from_ISpStream(iface); - return E_NOTIMPL; + TRACE("(%p, %p, %ld, %p).\n", iface, pv, cb, written); + + if (This->closed) + return SPERR_STREAM_CLOSED; + else if (!This->base_stream) + return SPERR_UNINITIALIZED; + + return IStream_Write(This->base_stream, pv, cb, written); } static HRESULT WINAPI spstream_Seek(ISpStream *iface, LARGE_INTEGER mode, DWORD origin, ULARGE_INTEGER *position) { - FIXME("(%p, %s, %ld, %p): stub.\n", iface, wine_dbgstr_longlong(mode.QuadPart), origin, position); + struct spstream *This = impl_from_ISpStream(iface); - return E_NOTIMPL; + TRACE("(%p, %s, %ld, %p).\n", iface, wine_dbgstr_longlong(mode.QuadPart), origin, position); + + if (This->closed) + return SPERR_STREAM_CLOSED; + else if (!This->base_stream) + return SPERR_UNINITIALIZED; + + return IStream_Seek(This->base_stream, mode, origin, position); } static HRESULT WINAPI spstream_SetSize(ISpStream *iface, ULARGE_INTEGER size) { - FIXME("(%p, %s): stub.\n", iface, wine_dbgstr_longlong(size.QuadPart)); + struct spstream *This = impl_from_ISpStream(iface); - return E_NOTIMPL; + TRACE("(%p, %s).\n", iface, wine_dbgstr_longlong(size.QuadPart)); + + if (This->closed) + return SPERR_STREAM_CLOSED; + else if (!This->base_stream) + return SPERR_UNINITIALIZED; + + return IStream_SetSize(This->base_stream, size); } static HRESULT WINAPI spstream_CopyTo(ISpStream *iface, IStream *stream, ULARGE_INTEGER cb, ULARGE_INTEGER *read, ULARGE_INTEGER *written) { - FIXME("(%p, %p, %s, %p, %p): stub.\n", iface, stream, wine_dbgstr_longlong(cb.QuadPart), - read, written); + struct spstream *This = impl_from_ISpStream(iface); - return E_NOTIMPL; + TRACE("(%p, %p, %s, %p, %p).\n", iface, stream, wine_dbgstr_longlong(cb.QuadPart), read, written); + + if (This->closed) + return SPERR_STREAM_CLOSED; + else if (!This->base_stream) + return SPERR_UNINITIALIZED; + + return IStream_CopyTo(This->base_stream, stream, cb, read, written); } static HRESULT WINAPI spstream_Commit(ISpStream *iface, DWORD flag) { - FIXME("(%p, %ld): stub.\n", iface, flag); + struct spstream *This = impl_from_ISpStream(iface); - return E_NOTIMPL; + TRACE("(%p, %ld).\n", iface, flag); + + if (This->closed) + return SPERR_STREAM_CLOSED; + else if (!This->base_stream) + return SPERR_UNINITIALIZED; + + return IStream_Commit(This->base_stream, flag); } static HRESULT WINAPI spstream_Revert(ISpStream *iface) { - FIXME("(%p): stub.\n", iface); + struct spstream *This = impl_from_ISpStream(iface); - return E_NOTIMPL; + TRACE("(%p).\n", iface); + + if (This->closed) + return SPERR_STREAM_CLOSED; + else if (!This->base_stream) + return SPERR_UNINITIALIZED; + + return IStream_Revert(This->base_stream); } static HRESULT WINAPI spstream_LockRegion(ISpStream *iface, ULARGE_INTEGER offset, ULARGE_INTEGER cb, DWORD type) { - FIXME("(%p, %s, %s, %ld): stub.\n", iface, wine_dbgstr_longlong(offset.QuadPart), + struct spstream *This = impl_from_ISpStream(iface); + + TRACE("(%p, %s, %s, %ld).\n", iface, wine_dbgstr_longlong(offset.QuadPart), wine_dbgstr_longlong(cb.QuadPart), type); - return E_NOTIMPL; + if (This->closed) + return SPERR_STREAM_CLOSED; + else if (!This->base_stream) + return SPERR_UNINITIALIZED; + + return IStream_LockRegion(This->base_stream, offset, cb, type); } static HRESULT WINAPI spstream_UnlockRegion(ISpStream *iface, ULARGE_INTEGER offset, ULARGE_INTEGER cb, DWORD type) { - FIXME("(%p, %s, %s, %ld): stub.\n", iface, wine_dbgstr_longlong(offset.QuadPart), + struct spstream *This = impl_from_ISpStream(iface); + + TRACE("(%p, %s, %s, %ld).\n", iface, wine_dbgstr_longlong(offset.QuadPart), wine_dbgstr_longlong(cb.QuadPart), type); - return E_NOTIMPL; + if (This->closed) + return SPERR_STREAM_CLOSED; + else if (!This->base_stream) + return SPERR_UNINITIALIZED; + + return IStream_UnlockRegion(This->base_stream, offset, cb, type); } static HRESULT WINAPI spstream_Stat(ISpStream *iface, STATSTG *statstg, DWORD flag) { - FIXME("(%p, %p, %ld): stub.\n", iface, statstg, flag); + struct spstream *This = impl_from_ISpStream(iface); - return E_NOTIMPL; + TRACE("(%p, %p, %ld).\n", iface, statstg, flag); + + if (This->closed) + return SPERR_STREAM_CLOSED; + else if (!This->base_stream) + return SPERR_UNINITIALIZED; + + return IStream_Stat(This->base_stream, statstg, flag); } static HRESULT WINAPI spstream_Clone(ISpStream *iface, IStream **stream) { - FIXME("(%p, %p): stub.\n", iface, stream); + TRACE("(%p, %p).\n", iface, stream); return E_NOTIMPL; } diff --git a/dlls/sapi/tests/stream.c b/dlls/sapi/tests/stream.c index 5d28ab13d95e..cd90cb003997 100644 --- a/dlls/sapi/tests/stream.c +++ b/dlls/sapi/tests/stream.c @@ -93,6 +93,11 @@ static void test_spstream(void) IStream *base_stream, *base_stream2; GUID fmtid, fmtid2; WAVEFORMATEX *wfx = NULL, *wfx2 = NULL; + char buf[4] = {0}; + ULONG read, written; + LARGE_INTEGER zero = {0}; + ULARGE_INTEGER uzero = {0}, size, pos; + STATSTG statstg; HRESULT hr; hr = CoCreateInstance(&CLSID_SpMMAudioOut, NULL, CLSCTX_INPROC_SERVER, @@ -110,12 +115,6 @@ static void test_spstream(void) &IID_ISpStream, (void **)&stream); ok(hr == S_OK, "Failed to create ISpStream interface: %#lx.\n", hr); - hr = ISpStream_SetBaseStream(stream, NULL, NULL, NULL); - ok(hr == E_INVALIDARG, "got %#lx.\n", hr); - - hr = ISpStream_SetBaseStream(stream, base_stream, NULL, NULL); - ok(hr == E_INVALIDARG, "got %#lx.\n", hr); - hr = ISpStream_GetBaseStream(stream, &base_stream2); ok(hr == SPERR_UNINITIALIZED, "got %#lx.\n", hr); @@ -131,6 +130,40 @@ static void test_spstream(void) &IID_ISpStream, (void **)&stream); ok(hr == S_OK, "Failed to create ISpStream interface: %#lx.\n", hr); + hr = ISpStream_Read(stream, buf, sizeof(buf), &read); + ok(hr == SPERR_UNINITIALIZED, "got %#lx.\n", hr); + + hr = ISpStream_Write(stream, buf, sizeof(buf), &written); + ok(hr == SPERR_UNINITIALIZED, "got %#lx.\n", hr); + + hr = ISpStream_Seek(stream, zero, STREAM_SEEK_CUR, &pos); + ok(hr == SPERR_UNINITIALIZED, "got %#lx.\n", hr); + + size.QuadPart = 4; + hr = ISpStream_SetSize(stream, size); + ok(hr == SPERR_UNINITIALIZED, "got %#lx.\n", hr); + + hr = ISpStream_CopyTo(stream, NULL, size, NULL, NULL); + ok(hr == SPERR_UNINITIALIZED, "got %#lx.\n", hr); + + hr = ISpStream_Commit(stream, 0); + ok(hr == SPERR_UNINITIALIZED, "got %#lx.\n", hr); + + hr = ISpStream_Revert(stream); + ok(hr == SPERR_UNINITIALIZED, "got %#lx.\n", hr); + + hr = ISpStream_LockRegion(stream, uzero, size, LOCK_WRITE); + ok(hr == SPERR_UNINITIALIZED, "got %#lx.\n", hr); + + hr = ISpStream_UnlockRegion(stream, uzero, size, LOCK_WRITE); + ok(hr == SPERR_UNINITIALIZED, "got %#lx.\n", hr); + + hr = ISpStream_Stat(stream, &statstg, 0); + ok(hr == SPERR_UNINITIALIZED, "got %#lx.\n", hr); + + hr = ISpStream_Clone(stream, NULL); + ok(hr == E_NOTIMPL, "got %#lx.\n", hr); + hr = ISpStream_GetFormat(stream, &fmtid2, &wfx2); ok(hr == SPERR_UNINITIALIZED, "got %#lx.\n", hr); @@ -157,6 +190,40 @@ static void test_spstream(void) ok(!memcmp(wfx, wfx2, sizeof(WAVEFORMATEX)), "wfx mismatch.\n"); CoTaskMemFree(wfx2); + /* TODO: Many IStream methods are not yet implemented in SpMMSysAudio. */ + hr = ISpStream_Read(stream, buf, sizeof(buf), &read); + todo_wine ok(hr == STG_E_ACCESSDENIED, "got %#lx.\n", hr); + + hr = ISpStream_Write(stream, buf, sizeof(buf), &written); + ok(hr == SP_AUDIO_STOPPED, "got %#lx.\n", hr); + + hr = ISpStream_Seek(stream, zero, STREAM_SEEK_CUR, &pos); + todo_wine ok(hr == S_OK, "got %#lx.\n", hr); + + hr = ISpStream_SetSize(stream, size); + todo_wine ok(hr == S_OK, "got %#lx.\n", hr); + + hr = ISpStream_CopyTo(stream, NULL, size, NULL, NULL); + todo_wine ok(hr == STG_E_ACCESSDENIED, "got %#lx.\n", hr); + + hr = ISpStream_Commit(stream, 0); + todo_wine ok(hr == S_OK, "got %#lx.\n", hr); + + hr = ISpStream_Revert(stream); + ok(hr == E_NOTIMPL, "got %#lx.\n", hr); + + hr = ISpStream_LockRegion(stream, uzero, size, LOCK_WRITE); + ok(hr == E_NOTIMPL, "got %#lx.\n", hr); + + hr = ISpStream_UnlockRegion(stream, uzero, size, LOCK_WRITE); + ok(hr == E_NOTIMPL, "got %#lx.\n", hr); + + hr = ISpStream_Stat(stream, &statstg, 0); + todo_wine ok(hr == S_OK, "got %#lx.\n", hr); + + hr = ISpStream_Clone(stream, NULL); + ok(hr == E_NOTIMPL, "got %#lx.\n", hr); + hr = ISpStream_Close(stream); ok(hr == S_OK, "got %#lx.\n", hr); @@ -169,6 +236,39 @@ static void test_spstream(void) hr = ISpStream_GetFormat(stream, &fmtid2, &wfx2); ok(hr == SPERR_STREAM_CLOSED, "got %#lx.\n", hr); + hr = ISpStream_Read(stream, buf, sizeof(buf), &read); + ok(hr == SPERR_STREAM_CLOSED, "got %#lx.\n", hr); + + hr = ISpStream_Write(stream, buf, sizeof(buf), &written); + ok(hr == SPERR_STREAM_CLOSED, "got %#lx.\n", hr); + + hr = ISpStream_Seek(stream, zero, STREAM_SEEK_CUR, &pos); + ok(hr == SPERR_STREAM_CLOSED, "got %#lx.\n", hr); + + hr = ISpStream_SetSize(stream, size); + ok(hr == SPERR_STREAM_CLOSED, "got %#lx.\n", hr); + + hr = ISpStream_CopyTo(stream, NULL, size, NULL, NULL); + ok(hr == SPERR_STREAM_CLOSED, "got %#lx.\n", hr); + + hr = ISpStream_Commit(stream, 0); + ok(hr == SPERR_STREAM_CLOSED, "got %#lx.\n", hr); + + hr = ISpStream_Revert(stream); + ok(hr == SPERR_STREAM_CLOSED, "got %#lx.\n", hr); + + hr = ISpStream_LockRegion(stream, uzero, size, LOCK_WRITE); + ok(hr == SPERR_STREAM_CLOSED, "got %#lx.\n", hr); + + hr = ISpStream_UnlockRegion(stream, uzero, size, LOCK_WRITE); + ok(hr == SPERR_STREAM_CLOSED, "got %#lx.\n", hr); + + hr = ISpStream_Stat(stream, &statstg, 0); + ok(hr == SPERR_STREAM_CLOSED, "got %#lx.\n", hr); + + hr = ISpStream_Clone(stream, NULL); + ok(hr == E_NOTIMPL, "got %#lx.\n", hr); + hr = ISpStream_Close(stream); ok(hr == SPERR_STREAM_CLOSED, "got %#lx.\n", hr); From d163f09b201696be0490b64ae0681a88739bd61a Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Sun, 12 Jan 2025 23:34:03 -0500 Subject: [PATCH 081/454] sapi: Remove some unnecessary traces. (cherry picked from commit 534d696803f3c963b1541a558500eebbb0a609eb) CW-Bug-Id: #22894 --- dlls/sapi/async.c | 1 - dlls/sapi/mmaudio.c | 2 -- 2 files changed, 3 deletions(-) diff --git a/dlls/sapi/async.c b/dlls/sapi/async.c index 69f962d55021..10097150bd83 100644 --- a/dlls/sapi/async.c +++ b/dlls/sapi/async.c @@ -100,7 +100,6 @@ static void CALLBACK async_worker(TP_CALLBACK_INSTANCE *instance, void *ctx) cancel: async_empty_queue(queue); CoUninitialize(); - TRACE("cancelled.\n"); SetEvent(queue->ready); } diff --git a/dlls/sapi/mmaudio.c b/dlls/sapi/mmaudio.c index 0369157acbca..5ec17ade0d4a 100644 --- a/dlls/sapi/mmaudio.c +++ b/dlls/sapi/mmaudio.c @@ -442,7 +442,6 @@ static HRESULT WINAPI mmsysaudio_Write(ISpMMSysAudio *iface, const void *pv, ULO EnterCriticalSection(&This->pending_cs); ++This->pending_buf_count; - TRACE("pending_buf_count = %Iu\n", This->pending_buf_count); LeaveCriticalSection(&This->pending_cs); ResetEvent(This->event); @@ -569,7 +568,6 @@ static void free_out_buf_proc(struct async_task *task) LeaveCriticalSection(&fbt->audio->pending_cs); if (!buf_count) SetEvent(fbt->audio->event); - TRACE("pending_buf_count = %Iu.\n", buf_count); } static void CALLBACK wave_out_proc(HWAVEOUT hwo, UINT msg, DWORD_PTR instance, DWORD_PTR param1, DWORD_PTR param2) From d954e4649e454750448932b40851310dd6ca0fc4 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Sun, 22 Jun 2025 15:26:02 -0400 Subject: [PATCH 082/454] sapi/tests: Copy SPVTEXTFRAG list into a contiguous array. This simplifies checking fragment field values. (cherry picked from commit 1ece2b833ad8a468acf14ad9b4897f04f9defe88) CW-Bug-Id: #22894 --- dlls/sapi/tests/tts.c | 79 ++++++++++++++++++++++--------------------- 1 file changed, 41 insertions(+), 38 deletions(-) diff --git a/dlls/sapi/tests/tts.c b/dlls/sapi/tests/tts.c index 38c69e6144a0..f523052d451f 100644 --- a/dlls/sapi/tests/tts.c +++ b/dlls/sapi/tests/tts.c @@ -151,60 +151,67 @@ struct test_engine BOOL speak_called; DWORD flags; GUID fmtid; - SPVTEXTFRAG *frag_list; + SPVTEXTFRAG *frags; + size_t frag_count; LONG rate; USHORT volume; }; -static void copy_frag_list(const SPVTEXTFRAG *frag_list, SPVTEXTFRAG **ret_frag_list) +/* Copy frag_list into a contiguous array allocated by a single malloc(). + * The texts are allocated at the end of the array. */ +static void copy_frag_list(const SPVTEXTFRAG *frag_list, SPVTEXTFRAG **ret_frags, size_t *frag_count) { - SPVTEXTFRAG *frag, *prev = NULL; + const SPVTEXTFRAG *frag; + SPVTEXTFRAG *cur; + WCHAR *cur_text; + size_t size = 0; + + *frag_count = 0; if (!frag_list) { - *ret_frag_list = NULL; + *ret_frags = NULL; return; } - while (frag_list) + for (frag = frag_list; frag; frag = frag->pNext) { - frag = malloc(sizeof(*frag) + frag_list->ulTextLen * sizeof(WCHAR)); - memcpy(frag, frag_list, sizeof(*frag)); + size += sizeof(*frag) + (frag->ulTextLen + 1) * sizeof(WCHAR); + (*frag_count)++; + } - if (frag_list->pTextStart) - { - frag->pTextStart = (WCHAR *)(frag + 1); - memcpy(frag + 1, frag_list->pTextStart, frag->ulTextLen * sizeof(WCHAR)); - } + *ret_frags = malloc(size); + cur = *ret_frags; + cur_text = (WCHAR *)(*ret_frags + (*frag_count)); - frag->pNext = NULL; + for (frag = frag_list; frag; frag = frag->pNext, ++cur) + { + memcpy(cur, frag, sizeof(*frag)); - if (prev) - prev->pNext = frag; - else - *ret_frag_list = frag; + cur->pNext = frag->pNext ? cur + 1 : NULL; - prev = frag; - frag_list = frag_list->pNext; + if (frag->pTextStart) + { + memcpy(cur_text, frag->pTextStart, frag->ulTextLen * sizeof(WCHAR)); + cur_text[frag->ulTextLen] = L'\0'; + + cur->pTextStart = (WCHAR *)cur_text; + cur_text += frag->ulTextLen + 1; + } } } static void reset_engine_params(struct test_engine *engine) { - SPVTEXTFRAG *frag, *next; - engine->speak_called = FALSE; engine->flags = 0xdeadbeef; memset(&engine->fmtid, 0xde, sizeof(engine->fmtid)); engine->rate = 0xdeadbeef; engine->volume = 0xbeef; - for (frag = engine->frag_list; frag; frag = next) - { - next = frag->pNext; - free(frag); - } - engine->frag_list = NULL; + free(engine->frags); + engine->frags = NULL; + engine->frag_count = 0; } static inline struct test_engine *impl_from_ISpTTSEngine(ISpTTSEngine *iface) @@ -257,7 +264,7 @@ static HRESULT WINAPI test_engine_Speak(ISpTTSEngine *iface, DWORD flags, REFGUI engine->flags = flags; engine->fmtid = *fmtid; - copy_frag_list(frag_list, &engine->frag_list); + copy_frag_list(frag_list, &engine->frags, &engine->frag_count); engine->speak_called = TRUE; actions = ISpTTSEngineSite_GetActions(site); @@ -637,11 +644,9 @@ static void test_spvoice(void) ok(hr == S_OK, "got %#lx.\n", hr); ok(test_engine.speak_called, "ISpTTSEngine::Speak was not called.\n"); ok(test_engine.flags == SPF_DEFAULT, "got %#lx.\n", test_engine.flags); - ok(test_engine.frag_list != NULL, "frag_list is NULL.\n"); - ok(test_engine.frag_list->pNext == NULL, "frag_list->pNext != NULL.\n"); - ok(test_engine.frag_list->ulTextLen == wcslen(test_text), "got %lu.\n", test_engine.frag_list->ulTextLen); - ok(!wcsncmp(test_text, test_engine.frag_list->pTextStart, wcslen(test_text)), - "got %s.\n", wine_dbgstr_w(test_engine.frag_list->pTextStart)); + ok(test_engine.frag_count == 1, "got %Iu.\n", test_engine.frag_count); + ok(!wcscmp(test_engine.frags[0].pTextStart, test_text), + "got %s.\n", wine_dbgstr_w(test_engine.frags[0].pTextStart)); ok(test_engine.rate == 0, "got %ld.\n", test_engine.rate); ok(test_engine.volume == 100, "got %d.\n", test_engine.volume); ok(stream_num == 1, "got %lu.\n", stream_num); @@ -677,11 +682,9 @@ static void test_spvoice(void) ok(test_engine.speak_called, "ISpTTSEngine::Speak was not called.\n"); ok(test_engine.flags == SPF_NLP_SPEAK_PUNC, "got %#lx.\n", test_engine.flags); - ok(test_engine.frag_list != NULL, "frag_list is NULL.\n"); - ok(test_engine.frag_list->pNext == NULL, "frag_list->pNext != NULL.\n"); - ok(test_engine.frag_list->ulTextLen == wcslen(test_text), "got %lu.\n", test_engine.frag_list->ulTextLen); - ok(!wcsncmp(test_text, test_engine.frag_list->pTextStart, wcslen(test_text)), - "got %s.\n", wine_dbgstr_w(test_engine.frag_list->pTextStart)); + ok(test_engine.frag_count == 1, "got %Iu.\n", test_engine.frag_count); + ok(!wcscmp(test_engine.frags[0].pTextStart, test_text), + "got %s.\n", wine_dbgstr_w(test_engine.frags[0].pTextStart)); ok(test_engine.rate == 0, "got %ld.\n", test_engine.rate); ok(test_engine.volume == 100, "got %d.\n", test_engine.volume); From da04d3228e7bc78089fef359ddccdce9c31d014c Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Sun, 22 Jun 2025 15:26:02 -0400 Subject: [PATCH 083/454] sapi/tests: Introduce simulate_output option in tts. (cherry picked from commit 70f20c8efbb6aee7fd01b77bb48ca1f0b8c54282) CW-Bug-Id: #22894 --- dlls/sapi/tests/tts.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/dlls/sapi/tests/tts.c b/dlls/sapi/tests/tts.c index f523052d451f..7b1e49b1b732 100644 --- a/dlls/sapi/tests/tts.c +++ b/dlls/sapi/tests/tts.c @@ -148,6 +148,7 @@ struct test_engine ISpObjectToken *token; + BOOL simulate_output; BOOL speak_called; DWORD flags; GUID fmtid; @@ -203,6 +204,7 @@ static void copy_frag_list(const SPVTEXTFRAG *frag_list, SPVTEXTFRAG **ret_frags static void reset_engine_params(struct test_engine *engine) { + engine->simulate_output = FALSE; engine->speak_called = FALSE; engine->flags = 0xdeadbeef; memset(&engine->fmtid, 0xde, sizeof(engine->fmtid)); @@ -280,6 +282,9 @@ static HRESULT WINAPI test_engine_Speak(ISpTTSEngine *iface, DWORD flags, REFGUI actions = ISpTTSEngineSite_GetActions(site); ok(actions == SPVES_CONTINUE, "got %#lx.\n", actions); + if (!engine->simulate_output) + return S_OK; + buf = calloc(1, 22050 * 2 / 5); for (i = 0; i < 5; i++) { @@ -637,6 +642,7 @@ static void test_spvoice(void) ISpVoice_SetVolume(voice, 100); reset_engine_params(&test_engine); + test_engine.simulate_output = TRUE; stream_num = 0xdeadbeef; start = GetTickCount(); hr = ISpVoice_Speak(voice, test_text, SPF_DEFAULT, &stream_num); @@ -664,6 +670,7 @@ static void test_spvoice(void) ok(duration < 200, "took %lu ms.\n", duration); reset_engine_params(&test_engine); + test_engine.simulate_output = TRUE; stream_num = 0xdeadbeef; start = GetTickCount(); hr = ISpVoice_Speak(voice, test_text, SPF_DEFAULT | SPF_ASYNC | SPF_NLP_SPEAK_PUNC, &stream_num); @@ -689,6 +696,7 @@ static void test_spvoice(void) ok(test_engine.volume == 100, "got %d.\n", test_engine.volume); reset_engine_params(&test_engine); + test_engine.simulate_output = TRUE; hr = ISpVoice_Speak(voice, test_text, SPF_DEFAULT | SPF_ASYNC, NULL); ok(hr == S_OK, "got %#lx.\n", hr); From e13fdb66dbdfd37cef9030770c6d14ab30702b57 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Sun, 22 Jun 2025 15:26:02 -0400 Subject: [PATCH 084/454] sapi/tests: Add some SSML tests in tts. (cherry picked from commit 3b3e2a9b3aedc93c009ed45b5426029a453577d3) CW-Bug-Id: #22894 --- dlls/sapi/tests/tts.c | 380 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 372 insertions(+), 8 deletions(-) diff --git a/dlls/sapi/tests/tts.c b/dlls/sapi/tests/tts.c index 7b1e49b1b732..1a63c6986cf3 100644 --- a/dlls/sapi/tests/tts.c +++ b/dlls/sapi/tests/tts.c @@ -426,9 +426,24 @@ static const IClassFactoryVtbl ClassFactoryVtbl = { static IClassFactory test_engine_cf = { &ClassFactoryVtbl }; +static const WCHAR test_token_id[] = L"HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Speech\\Voices\\Tokens\\WinetestVoice"; + +static BOOL test_token_created = FALSE; + +#define check_frag_text(i, exp) \ + ok(!wcscmp(test_engine.frags[i].pTextStart, exp), "frag %d text: got %s.\n", \ + i, wine_dbgstr_w(test_engine.frags[i].pTextStart)) + +#define check_frag_text_src_offset(i, exp) \ + ok(test_engine.frags[i].ulTextSrcOffset == exp, "frag %d text src offset: got %lu.\n", \ + i, test_engine.frags[i].ulTextSrcOffset) + +#define check_frag_state_field(i, name, exp, fmt) \ + ok(test_engine.frags[i].State.name == exp, "frag %d state " #name ": got " fmt ".\n", \ + i, test_engine.frags[i].State.name) + static void test_spvoice(void) { - static const WCHAR test_token_id[] = L"HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Speech\\Voices\\Tokens\\WinetestVoice"; static const WCHAR test_text[] = L"Hello! This is a test sentence."; static const WCHAR *get_voices = L"GetVoices"; @@ -462,8 +477,6 @@ static void test_spvoice(void) return; } - RegDeleteTreeA(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Speech\\Voices\\WinetestVoice"); - check_apttype(); ok(test_apt_data.type == APTTYPE_UNITIALIZED, "got apt type %d.\n", test_apt_data.type); @@ -616,6 +629,8 @@ static void test_spvoice(void) ISpDataKey_SetStringValue(attrs_key, L"Vendor", L"Winetest"); ISpDataKey_Release(attrs_key); + test_token_created = TRUE; + hr = ISpVoice_SetVoice(voice, token); ok(hr == S_OK, "got %#lx.\n", hr); @@ -651,8 +666,7 @@ static void test_spvoice(void) ok(test_engine.speak_called, "ISpTTSEngine::Speak was not called.\n"); ok(test_engine.flags == SPF_DEFAULT, "got %#lx.\n", test_engine.flags); ok(test_engine.frag_count == 1, "got %Iu.\n", test_engine.frag_count); - ok(!wcscmp(test_engine.frags[0].pTextStart, test_text), - "got %s.\n", wine_dbgstr_w(test_engine.frags[0].pTextStart)); + check_frag_text(0, test_text); ok(test_engine.rate == 0, "got %ld.\n", test_engine.rate); ok(test_engine.volume == 100, "got %d.\n", test_engine.volume); ok(stream_num == 1, "got %lu.\n", stream_num); @@ -690,8 +704,8 @@ static void test_spvoice(void) ok(test_engine.speak_called, "ISpTTSEngine::Speak was not called.\n"); ok(test_engine.flags == SPF_NLP_SPEAK_PUNC, "got %#lx.\n", test_engine.flags); ok(test_engine.frag_count == 1, "got %Iu.\n", test_engine.frag_count); - ok(!wcscmp(test_engine.frags[0].pTextStart, test_text), - "got %s.\n", wine_dbgstr_w(test_engine.frags[0].pTextStart)); + check_frag_text(0, test_text); + check_frag_text_src_offset(0, 0); ok(test_engine.rate == 0, "got %ld.\n", test_engine.rate); ok(test_engine.volume == 100, "got %d.\n", test_engine.volume); @@ -811,15 +825,365 @@ static void test_spvoice(void) ISpMMSysAudio_Release(audio_out); SysFreeString(req); SysFreeString(opt); +} - RegDeleteTreeA(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Speech\\Voices\\WinetestVoice"); +static void test_spvoice_ssml(void) +{ + static const WCHAR text1[] = + L"text1"; + + /* Only version 1.0 is supported in SAPI. */ + static const WCHAR bad_text1[] = + L"text1"; + + /* version attribute is required in . */ + static const WCHAR bad_text2[] = + L"text1"; + + /* xml:lang attribute is required in . */ + static const WCHAR bad_text3[] = + L"text1"; + + /* xmlns is not required in . */ + static const WCHAR text2[] = + L"text2"; + + static const WCHAR text3[] = + L"" + L"\n" + L"P1S1. P1S2.\n" + L"\n" + L"P2." + L"

P3.

" + L"

P4, S1. P4S2.P4S3.

" + L"

\u4F0D

" + L"

\U0001240B

" /* Two WCHARs needed for \U0001240B */ + L"

P7.

" + L"
"; + + static const WCHAR text4[] = + L"" + L"" + L"One, two." + L""; + + static const WCHAR text5[] = + L"" + L"" + L"50%." + L"+50%." + L"6." + L"0.01000001." + L"0.01." + L"0." + L"-1.0." + L"3." + L""; + + static const WCHAR text6[] = + L"" + L"x-slow." + L"slow." + L"medium." + L"fast." + L"x-fast." + L""; + + static const WCHAR text7[] = + L"" + L"One, Two." /* Empty tags are ignored. */ + L"" + L" Three.Four." + L"" + L"Five." + L""; + + static const WCHAR text8[] = + L"" + L"50%." + L"-50%." + L"10." + L"+10." + L"-10.1." + L"25." + L"75." + L"100." + L"50." + L""; + + static const WCHAR text9[] = + L"" + L"silent." + L"x-soft." + L"soft." + L"medium." + L"loud." + L"x-loud." + L"soft." + L""; + + + ISpVoice *voice; + ISpObjectToken *token; + HRESULT hr; + + if (waveOutGetNumDevs() == 0) { + skip("no wave out devices.\n"); + return; + } + + if (!test_token_created) { + /* w1064_adm */ + win_skip("Test token not created.\n"); + return; + } + + hr = CoCreateInstance(&CLSID_SpVoice, NULL, CLSCTX_INPROC_SERVER, + &IID_ISpVoice, (void **)&voice); + ok(hr == S_OK, "got %#lx.\n", hr); + + hr = ISpVoice_SetOutput(voice, NULL, TRUE); + ok(hr == S_OK, "got %#lx.\n", hr); + + hr = CoCreateInstance(&CLSID_SpObjectToken, NULL, CLSCTX_INPROC_SERVER, + &IID_ISpObjectToken, (void **)&token); + ok(hr == S_OK, "got %#lx.\n", hr); + + hr = ISpObjectToken_SetId(token, NULL, test_token_id, FALSE); + ok(hr == S_OK, "got %#lx.\n", hr); + + hr = ISpVoice_SetVoice(voice, token); + ok(hr == S_OK, "got %#lx.\n", hr); + + reset_engine_params(&test_engine); + + hr = ISpVoice_Speak(voice, text1, SPF_IS_XML | SPF_PARSE_SSML, NULL); + todo_wine ok(hr == S_OK, "got %#lx.\n", hr); + todo_wine ok(test_engine.frag_count == 1, "got %Iu.\n", test_engine.frag_count); + + if (test_engine.frag_count == 1) { + check_frag_text(0, L"text1"); + + check_frag_state_field(0, eAction, SPVA_Speak, "%d"); + ok(test_engine.frags[0].State.LangID == 0x409 || broken(test_engine.frags[0].State.LangID == 0) /* win7 */, + "got %#hx.\n", test_engine.frags[0].State.LangID); + check_frag_state_field(0, EmphAdj, 0, "%ld"); + check_frag_state_field(0, RateAdj, 0, "%ld"); + check_frag_state_field(0, Volume, 100, "%lu"); + check_frag_state_field(0, PitchAdj.MiddleAdj, 0, "%ld"); + check_frag_state_field(0, PitchAdj.RangeAdj, 0, "%ld"); + check_frag_state_field(0, SilenceMSecs, 0, "%lu"); + check_frag_state_field(0, ePartOfSpeech, SPPS_Unknown, "%#x"); + } + + reset_engine_params(&test_engine); + + /* SSML autodetection when SPF_PARSE_SSML is not specified. */ + hr = ISpVoice_Speak(voice, text1, SPF_IS_XML, NULL); + todo_wine ok(hr == S_OK, "got %#lx.\n", hr); + todo_wine ok(test_engine.frag_count == 1, "got %Iu.\n", test_engine.frag_count); + + if (test_engine.frag_count == 1) + check_frag_text(0, L"text1"); + + reset_engine_params(&test_engine); + + /* XML and SSML autodetection when SPF_IS_XML is not specified. */ + hr = ISpVoice_Speak(voice, text1, SPF_DEFAULT, NULL); + ok(hr == S_OK, "got %#lx.\n", hr); + ok(test_engine.frag_count == 1, "got %Iu.\n", test_engine.frag_count); + todo_wine check_frag_text(0, L"text1"); + + reset_engine_params(&test_engine); + + hr = ISpVoice_Speak(voice, bad_text1, SPF_IS_XML | SPF_PARSE_SSML, NULL); + todo_wine ok(hr == SPERR_UNSUPPORTED_FORMAT, "got %#lx.\n", hr); + + hr = ISpVoice_Speak(voice, bad_text2, SPF_IS_XML | SPF_PARSE_SSML, NULL); + todo_wine ok(hr == SPERR_UNSUPPORTED_FORMAT, "got %#lx.\n", hr); + + hr = ISpVoice_Speak(voice, bad_text3, SPF_IS_XML | SPF_PARSE_SSML, NULL); + todo_wine ok(hr == SPERR_UNSUPPORTED_FORMAT || broken(hr == S_OK) /* win7 */, "got %#lx.\n", hr); + + reset_engine_params(&test_engine); + + hr = ISpVoice_Speak(voice, text2, SPF_IS_XML | SPF_PARSE_SSML, NULL); + todo_wine ok(hr == S_OK || broken(hr == SPERR_UNSUPPORTED_FORMAT) /* win7 */, "got %#lx.\n", hr); + + if (hr == S_OK) { + ok(test_engine.frag_count == 1, "got %Iu.\n", test_engine.frag_count); + check_frag_text(0, L"text2"); + check_frag_state_field(0, eAction, SPVA_Speak, "%d"); + check_frag_state_field(0, LangID, 0x409, "%#hx"); + } + + reset_engine_params(&test_engine); + + hr = ISpVoice_Speak(voice, text3, SPF_IS_XML, NULL); + todo_wine ok(hr == S_OK, "got %#lx.\n", hr); + todo_wine ok(test_engine.frag_count == 7 || broken(test_engine.frag_count == 1) /* win7 */, + "got %Iu.\n", test_engine.frag_count); + + if (test_engine.frag_count == 7) { + check_frag_text(0, L"\nP1S1. P1S2.\n\nP2."); + check_frag_text_src_offset(0, 120); + check_frag_state_field(0, eAction, SPVA_Speak, "%d"); + + check_frag_text(1, L"P3."); + check_frag_text_src_offset(1, 140); + check_frag_state_field(1, eAction, SPVA_Speak, "%d"); + + check_frag_text(2, L"P4, S1. P4S2."); + check_frag_text_src_offset(2, 153); + check_frag_state_field(2, eAction, SPVA_Speak, "%d"); + + check_frag_text(3, L"P4S3."); + check_frag_text_src_offset(3, 173); + check_frag_state_field(3, eAction, SPVA_Speak, "%d"); + + check_frag_text(4, L"\u4F0D"); + check_frag_text_src_offset(4, 189); + check_frag_state_field(4, eAction, SPVA_Speak, "%d"); + + check_frag_text(5, L"\U0001240B"); + ok(test_engine.frags[5].ulTextSrcOffset == 197 || /* 189 + 8 = 197 */ + broken(test_engine.frags[5].ulTextSrcOffset == 196), /* Windows gives incorrect offset here */ + "got %lu.\n", test_engine.frags[5].ulTextSrcOffset); + + check_frag_text(6, L"P7."); + check_frag_text_src_offset(6, test_engine.frags[5].ulTextSrcOffset + 9); + } + + reset_engine_params(&test_engine); + + hr = ISpVoice_Speak(voice, text4, SPF_DEFAULT, NULL); + ok(hr == S_OK, "got %#lx.\n", hr); + todo_wine ok(test_engine.frag_count == 2, "got %Iu.\n", test_engine.frag_count); + + if (test_engine.frag_count == 2) { + check_frag_text(0, L"One, "); + check_frag_state_field(0, eAction, SPVA_Speak, "%d"); + check_frag_state_field(0, RateAdj, 0, "%ld"); + + check_frag_text(1, L"two."); + check_frag_state_field(1, eAction, SPVA_Speak, "%d"); + check_frag_state_field(1, RateAdj, -17, "%ld"); /* 3^(-17/10) ~= 0.15 */ + } + + reset_engine_params(&test_engine); + + hr = ISpVoice_Speak(voice, text5, SPF_IS_XML | SPF_PARSE_SSML, NULL); + todo_wine ok(hr == S_OK, "got %#lx.\n", hr); + todo_wine ok(test_engine.frag_count == 8 || broken(test_engine.frag_count == 3) /* win7 */, + "got %Iu.\n", test_engine.frag_count); + + if (test_engine.frag_count == 8) { + check_frag_state_field(0, RateAdj, 4, "%ld"); /* 3^(4/10) ~= 1.5 */ + check_frag_state_field(1, RateAdj, 4, "%ld"); /* 3^(4/10) ~= 1.5 */ + check_frag_state_field(2, RateAdj, 16, "%ld"); /* 3^(16/10) ~= 6 */ + check_frag_state_field(3, RateAdj, -42, "%ld"); /* 3^(-42/10) ~= 0.01000001 */ + check_frag_state_field(4, RateAdj, -10, "%ld"); /* rate = 0.01 */ + check_frag_state_field(5, RateAdj, -10, "%ld"); /* rate = 0 */ + check_frag_state_field(6, RateAdj, 0, "%ld"); /* negative rates are ignored */ + check_frag_state_field(7, RateAdj, 10, "%ld"); /* 3^(10/10) = 3 */ + } + + reset_engine_params(&test_engine); + + hr = ISpVoice_Speak(voice, text6, SPF_IS_XML | SPF_PARSE_SSML, NULL); + todo_wine ok(hr == S_OK || broken(hr == SPERR_UNSUPPORTED_FORMAT) /* win7 */, "got %#lx.\n", hr); + + if (hr == S_OK) { + ok(test_engine.frag_count == 5, "got %Iu.\n", test_engine.frag_count); + + check_frag_state_field(0, RateAdj, -9, "%ld"); /* x-slow */ + check_frag_state_field(1, RateAdj, -4, "%ld"); /* slow */ + check_frag_state_field(2, RateAdj, 0, "%ld"); /* medium */ + check_frag_state_field(3, RateAdj, 4, "%ld"); /* fast */ + check_frag_state_field(4, RateAdj, 9, "%ld"); /* x-fast */ + } + + reset_engine_params(&test_engine); + + hr = ISpVoice_Speak(voice, text7, SPF_IS_XML | SPF_PARSE_SSML, NULL); + todo_wine ok(hr == S_OK || broken(hr == SPERR_UNSUPPORTED_FORMAT) /* win7 */, "got %#lx.\n", hr); + + if (hr == S_OK) { + ok(test_engine.frag_count == 5, "got %Iu.\n", test_engine.frag_count); + + check_frag_text(0, L"One,"); + check_frag_state_field(0, RateAdj, 0, "%ld"); + + check_frag_text(1, L" Two."); + check_frag_state_field(1, RateAdj, 0, "%ld"); + + check_frag_text(2, L" Three."); + check_frag_state_field(2, RateAdj, 4, "%ld"); + + check_frag_text(3, L"Four."); + check_frag_state_field(3, RateAdj, 9, "%ld"); + + check_frag_text(4, L"Five."); + check_frag_state_field(4, RateAdj, 0, "%ld"); + } + + reset_engine_params(&test_engine); + + hr = ISpVoice_Speak(voice, text8, SPF_IS_XML | SPF_PARSE_SSML, NULL); + todo_wine ok(hr == S_OK, "got %#lx.\n", hr); + + if (hr == S_OK) { + ok(test_engine.frag_count == 9, "got %Iu.\n", test_engine.frag_count); + + ok(test_engine.frags[0].State.Volume == 100 || broken(test_engine.frags[0].State.Volume == 50) /* win7 */, + "got %lu.\n", test_engine.frags[0].State.Volume); + check_frag_state_field(1, Volume, 50, "%ld"); + check_frag_state_field(2, Volume, 10, "%lu"); + check_frag_state_field(3, Volume, 100, "%lu"); + + check_frag_state_field(4, Volume, 90, "%lu"); + check_frag_state_field(4, RateAdj, 10, "%ld"); + + check_frag_state_field(5, Volume, 25, "%lu"); + ok(test_engine.frags[6].State.Volume == 75 || broken(test_engine.frags[6].State.Volume == 25) /* win7 */, + "got %lu.\n", test_engine.frags[6].State.Volume); + check_frag_state_field(7, Volume, 100, "%lu"); + check_frag_state_field(8, Volume, 50, "%lu"); + } + + reset_engine_params(&test_engine); + + hr = ISpVoice_Speak(voice, text9, SPF_IS_XML | SPF_PARSE_SSML, NULL); + todo_wine ok(hr == S_OK || broken(hr == SPERR_UNSUPPORTED_FORMAT) /* win7 */, "got %#lx.\n", hr); + + if (hr == S_OK) { + ok(test_engine.frag_count == 7, "got %Iu.\n", test_engine.frag_count); + + check_frag_state_field(0, Volume, 0, "%lu"); /* silent */ + check_frag_state_field(1, Volume, 20, "%lu"); /* x-soft */ + check_frag_state_field(2, Volume, 40, "%lu"); /* soft */ + check_frag_state_field(3, Volume, 60, "%lu"); /* medium */ + check_frag_state_field(4, Volume, 80, "%lu"); /* loud */ + check_frag_state_field(5, Volume, 100, "%lu"); /* x-loud */ + + check_frag_state_field(6, Volume, 40, "%lu"); /* soft */ + } + + reset_engine_params(&test_engine); + ISpVoice_Release(voice); + ISpObjectToken_Release(token); } START_TEST(tts) { CoInitialize(NULL); + RegDeleteTreeA(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Speech\\Voices\\WinetestVoice"); + /* Run spvoice tests before interface tests so that a MTA won't be created before this test is run. */ test_spvoice(); + test_spvoice_ssml(); test_interfaces(); + + RegDeleteTreeA(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Speech\\Voices\\WinetestVoice"); CoUninitialize(); } From b8268eccafa0c90ca781df0f188941f81ca85278 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Sun, 22 Jun 2025 15:26:02 -0400 Subject: [PATCH 085/454] sapi/tts: Support XML-related flags in ISpVoice::Speak. (cherry picked from commit 8afc73bc783605d7a8d296348e7c53bb71779c4a) CW-Bug-Id: #22894 --- dlls/sapi/Makefile.in | 3 +- dlls/sapi/sapi_private.h | 10 ++++ dlls/sapi/tests/tts.c | 9 ++-- dlls/sapi/tts.c | 110 +++++++++++++++++++++++++++++---------- dlls/sapi/xml.c | 38 ++++++++++++++ 5 files changed, 138 insertions(+), 32 deletions(-) create mode 100644 dlls/sapi/xml.c diff --git a/dlls/sapi/Makefile.in b/dlls/sapi/Makefile.in index a6c4d86037eb..da91eca5b8ce 100644 --- a/dlls/sapi/Makefile.in +++ b/dlls/sapi/Makefile.in @@ -14,4 +14,5 @@ SOURCES = \ sapi_typelib.idl \ stream.c \ token.c \ - tts.c + tts.c \ + xml.c diff --git a/dlls/sapi/sapi_private.h b/dlls/sapi/sapi_private.h index 219436f88c14..e47375b129ad 100644 --- a/dlls/sapi/sapi_private.h +++ b/dlls/sapi/sapi_private.h @@ -18,6 +18,8 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ +#include "sapiddk.h" + #include "wine/list.h" struct async_task @@ -63,3 +65,11 @@ enum type_id HRESULT get_typeinfo( enum type_id tid, ITypeInfo **typeinfo ); void release_typelib( void ); + +HRESULT parse_sapi_xml( const WCHAR *contents, DWORD parse_flag, BOOL persist, SPVSTATE *global_state, + SPVTEXTFRAG **frag_list ); + +static inline BOOL isxmlspace( WCHAR c ) +{ + return c == ' ' || c == '\r' || c == '\n' || c == '\t'; +} diff --git a/dlls/sapi/tests/tts.c b/dlls/sapi/tests/tts.c index 1a63c6986cf3..49d312fed705 100644 --- a/dlls/sapi/tests/tts.c +++ b/dlls/sapi/tests/tts.c @@ -990,9 +990,10 @@ static void test_spvoice_ssml(void) /* XML and SSML autodetection when SPF_IS_XML is not specified. */ hr = ISpVoice_Speak(voice, text1, SPF_DEFAULT, NULL); - ok(hr == S_OK, "got %#lx.\n", hr); - ok(test_engine.frag_count == 1, "got %Iu.\n", test_engine.frag_count); - todo_wine check_frag_text(0, L"text1"); + todo_wine ok(hr == S_OK, "got %#lx.\n", hr); + todo_wine ok(test_engine.frag_count == 1, "got %Iu.\n", test_engine.frag_count); + if (test_engine.frag_count == 1) + check_frag_text(0, L"text1"); reset_engine_params(&test_engine); @@ -1057,7 +1058,7 @@ static void test_spvoice_ssml(void) reset_engine_params(&test_engine); hr = ISpVoice_Speak(voice, text4, SPF_DEFAULT, NULL); - ok(hr == S_OK, "got %#lx.\n", hr); + todo_wine ok(hr == S_OK, "got %#lx.\n", hr); todo_wine ok(test_engine.frag_count == 2, "got %Iu.\n", test_engine.frag_count); if (test_engine.frag_count == 2) { diff --git a/dlls/sapi/tts.c b/dlls/sapi/tts.c index 80f0298b51c7..baf7a74789db 100644 --- a/dlls/sapi/tts.c +++ b/dlls/sapi/tts.c @@ -49,6 +49,7 @@ struct speech_voice DWORD actions; USHORT volume; LONG rate; + SPVSTATE state; struct async_queue queue; CRITICAL_SECTION cs; }; @@ -811,6 +812,18 @@ struct speak_task DWORD flags; }; +static void free_frag_list(SPVTEXTFRAG *frag) +{ + SPVTEXTFRAG *next; + + while (frag) + { + next = frag->pNext; + free(frag); + frag = next; + } +} + static HRESULT set_output_format(ISpStreamFormat *output, ISpTTSEngine *engine, GUID *fmtid, WAVEFORMATEX **wfx) { GUID output_fmtid; @@ -894,7 +907,7 @@ static void speak_proc(struct async_task *task) } CoTaskMemFree(wfx); ISpTTSEngine_Release(speak_task->engine); - free(speak_task->frag_list); + free_frag_list(speak_task->frag_list); ISpTTSEngineSite_Release(speak_task->site); if (speak_task->result) @@ -911,23 +924,52 @@ static HRESULT WINAPI spvoice_Speak(ISpVoice *iface, const WCHAR *contents, DWOR struct speech_voice *This = impl_from_ISpVoice(iface); ISpTTSEngineSite *site = NULL; ISpTTSEngine *engine = NULL; - SPVTEXTFRAG *frag; + SPVTEXTFRAG *frag_list; + BOOL async, purge, persist_xml; + DWORD parse_flag, nlp_flags; + BOOL xml; struct speak_task *speak_task = NULL; struct async_result *result = NULL; - size_t contents_len, contents_size; + size_t contents_len; ULONG stream_num; HRESULT hr; TRACE("(%p, %p, %#lx, %p).\n", iface, contents, flags, stream_num_out); - flags &= ~SPF_IS_NOT_XML; - if (flags & ~(SPF_ASYNC | SPF_PURGEBEFORESPEAK | SPF_NLP_SPEAK_PUNC)) + async = flags & SPF_ASYNC; + purge = flags & SPF_PURGEBEFORESPEAK; + persist_xml = flags & SPF_PERSIST_XML; + parse_flag = flags & SPF_PARSE_MASK; + nlp_flags = flags & SPF_NLP_MASK; + + xml = FALSE; + if ((flags & SPF_IS_XML) && (flags & SPF_IS_NOT_XML)) + return E_INVALIDARG; + else if (flags & SPF_IS_XML) + xml = TRUE; + else if (!(flags & SPF_IS_NOT_XML)) + { + if (contents) + { + const WCHAR *c = contents; + + while (*c && isxmlspace(*c)) c++; + xml = *c == '<'; + } + } + + if (parse_flag == SPF_PARSE_MASK) + return E_INVALIDARG; + + flags &= ~(SPF_ASYNC | SPF_PURGEBEFORESPEAK | SPF_IS_XML | SPF_IS_NOT_XML | SPF_PERSIST_XML | + SPF_PARSE_MASK | SPF_NLP_MASK); + if (flags) { - FIXME("flags %#lx not implemented.\n", flags & ~(SPF_ASYNC | SPF_PURGEBEFORESPEAK | SPF_NLP_SPEAK_PUNC)); + FIXME("flags %#lx not implemented.\n", flags); return E_NOTIMPL; } - if (flags & SPF_PURGEBEFORESPEAK) + if (purge) { ISpAudio *audio; @@ -954,9 +996,6 @@ static HRESULT WINAPI spvoice_Speak(ISpVoice *iface, const WCHAR *contents, DWOR else if (!contents) return E_POINTER; - contents_len = wcslen(contents); - contents_size = sizeof(WCHAR) * (contents_len + 1); - if (!This->output) { /* Create a new output stream with the default output. */ @@ -964,6 +1003,28 @@ static HRESULT WINAPI spvoice_Speak(ISpVoice *iface, const WCHAR *contents, DWOR return hr; } + if (xml) + { + if (FAILED(hr = parse_sapi_xml(contents, parse_flag, persist_xml, &This->state, &frag_list))) + return hr; + } + else + { + contents_len = wcslen(contents); + + if (!(frag_list = malloc(sizeof(*frag_list) + (contents_len + 1) * sizeof(WCHAR)))) + return E_OUTOFMEMORY; + + memcpy(frag_list + 1, contents, (contents_len + 1) * sizeof(WCHAR)); + memcpy(&frag_list->State, &This->state, sizeof(This->state)); + + frag_list->pNext = NULL; + frag_list->State.eAction = SPVA_Speak; + frag_list->pTextStart = (WCHAR *)(frag_list + 1); + frag_list->ulTextLen = contents_len; + frag_list->ulTextSrcOffset = 0; + } + EnterCriticalSection(&This->cs); if (!This->engine_token) @@ -972,31 +1033,22 @@ static HRESULT WINAPI spvoice_Speak(ISpVoice *iface, const WCHAR *contents, DWOR if (FAILED(hr = ISpVoice_SetVoice(iface, NULL))) { LeaveCriticalSection(&This->cs); - return hr; + goto fail; } } + if (!This->engine && FAILED(hr = ISpObjectToken_CreateInstance(This->engine_token, NULL, CLSCTX_ALL, &IID_ISpTTSEngine, (void **)&This->engine))) { LeaveCriticalSection(&This->cs); ERR("Failed to create engine: %#lx.\n", hr); - return hr; + goto fail; } engine = This->engine; ISpTTSEngine_AddRef(engine); LeaveCriticalSection(&This->cs); - if (!(frag = malloc(sizeof(*frag) + contents_size))) - return E_OUTOFMEMORY; - memset(frag, 0, sizeof(*frag)); - memcpy(frag + 1, contents, contents_size); - frag->State.eAction = SPVA_Speak; - frag->State.Volume = 100; - frag->pTextStart = (WCHAR *)(frag + 1); - frag->ulTextLen = contents_len; - frag->ulTextSrcOffset = 0; - stream_num = InterlockedIncrement(&This->cur_stream_num); if (FAILED(hr = ttsenginesite_create(This, stream_num, &site))) { @@ -1010,11 +1062,11 @@ static HRESULT WINAPI spvoice_Speak(ISpVoice *iface, const WCHAR *contents, DWOR speak_task->result = NULL; speak_task->voice = This; speak_task->engine = engine; - speak_task->frag_list = frag; + speak_task->frag_list = frag_list; speak_task->site = site; - speak_task->flags = flags & SPF_NLP_SPEAK_PUNC; + speak_task->flags = nlp_flags; - if (!(flags & SPF_ASYNC)) + if (!async) { if (!(result = malloc(sizeof(*result)))) { @@ -1035,7 +1087,7 @@ static HRESULT WINAPI spvoice_Speak(ISpVoice *iface, const WCHAR *contents, DWOR if (stream_num_out) *stream_num_out = stream_num; - if (flags & SPF_ASYNC) + if (async) return S_OK; else { @@ -1049,7 +1101,7 @@ static HRESULT WINAPI spvoice_Speak(ISpVoice *iface, const WCHAR *contents, DWOR fail: if (site) ISpTTSEngineSite_Release(site); if (engine) ISpTTSEngine_Release(engine); - free(frag); + free_frag_list(frag_list); free(speak_task); if (result) { @@ -1499,6 +1551,10 @@ HRESULT speech_voice_create(IUnknown *outer, REFIID iid, void **obj) This->actions = SPVES_CONTINUE; This->volume = 100; This->rate = 0; + + memset(&This->state, 0, sizeof(This->state)); + This->state.Volume = 100; + memset(&This->queue, 0, sizeof(This->queue)); InitializeCriticalSection(&This->cs); diff --git a/dlls/sapi/xml.c b/dlls/sapi/xml.c new file mode 100644 index 000000000000..f61864c3254a --- /dev/null +++ b/dlls/sapi/xml.c @@ -0,0 +1,38 @@ +/* + * Speech API (SAPI) XML parser implementation. + * + * Copyright 2025 Shaun Ren for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#define COBJMACROS + +#include "objbase.h" + +#include "sapiddk.h" + +#include "wine/debug.h" + +#include "sapi_private.h" + +WINE_DEFAULT_DEBUG_CHANNEL(sapi); + +HRESULT parse_sapi_xml(const WCHAR *contents, DWORD parse_flag, BOOL persist, SPVSTATE *global_state, + SPVTEXTFRAG **frag_list) +{ + FIXME("(%p, %#lx, %d, %p, %p): stub.\n", contents, parse_flag, persist, global_state, frag_list); + return E_NOTIMPL; +} From 08074acabc33cfca2a809306da2fc042e733aef3 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Fri, 4 Jul 2025 18:14:08 -0400 Subject: [PATCH 086/454] sapi/xml: Add a stub SSML parser. Based on the XML parser in ntdll/actctx.c. (cherry picked from commit 4b100a5030a24704e6f9f17f001535edb5dde06b) CW-Bug-Id: #22894 --- dlls/sapi/xml.c | 420 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 418 insertions(+), 2 deletions(-) diff --git a/dlls/sapi/xml.c b/dlls/sapi/xml.c index f61864c3254a..442537fef4cc 100644 --- a/dlls/sapi/xml.c +++ b/dlls/sapi/xml.c @@ -3,6 +3,13 @@ * * Copyright 2025 Shaun Ren for CodeWeavers * + * Based on ntdll/actctx.c + * Copyright 2004 Jon Griffiths + * Copyright 2007 Eric Pouech + * Copyright 2007 Jacek Caban for CodeWeavers + * Copyright 2007 Alexandre Julliard + * Copyright 2013 Nikolay Sivov + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either @@ -18,11 +25,14 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ +#include + #define COBJMACROS #include "objbase.h" #include "sapiddk.h" +#include "sperror.h" #include "wine/debug.h" @@ -30,9 +40,415 @@ WINE_DEFAULT_DEBUG_CHANNEL(sapi); + +#define MAX_NAMESPACES 64 + +typedef struct +{ + const WCHAR *ptr; + unsigned int len; +} xmlstr_t; + +struct xml_elem +{ + xmlstr_t name; + xmlstr_t ns; + int ns_pos; +}; + +struct xml_attr +{ + xmlstr_t name; + xmlstr_t value; +}; + +struct xml_parser +{ + const WCHAR *ptr; + const WCHAR *end; + struct xml_attr namespaces[MAX_NAMESPACES]; + int ns_pos; + BOOL error; +}; + +static const xmlstr_t empty_xmlstr; + +static const WCHAR ssml_ns[] = L"http://www.w3.org/2001/10/synthesis"; + +static inline const char *debugstr_xmlstr(const xmlstr_t *str) +{ + return debugstr_wn(str->ptr, str->len); +} + +static inline BOOL xmlstr_eq(const xmlstr_t* xmlstr, const WCHAR *str) +{ + return !wcsncmp(xmlstr->ptr, str, xmlstr->len) && !str[xmlstr->len]; +} + +static inline BOOL xml_elem_eq(const struct xml_elem *elem, const WCHAR *ns, const WCHAR *name) +{ + if (!xmlstr_eq(&elem->name, name)) return FALSE; + return xmlstr_eq(&elem->ns, ns); +} + +static BOOL xml_name_eq(const struct xml_elem *elem1, const struct xml_elem *elem2) +{ + return (elem1->name.len == elem2->name.len && + elem1->ns.len == elem2->ns.len && + !wcsncmp(elem1->name.ptr, elem2->name.ptr, elem1->name.len) && + !wcsncmp(elem1->ns.ptr, elem2->ns.ptr, elem1->ns.len)); +} + +static BOOL set_error(struct xml_parser *parser) +{ + parser->error = TRUE; + return FALSE; +} + +static BOOL is_xmlns_attr(const struct xml_attr *attr) +{ + const int len = wcslen(L"xmlns"); + if (attr->name.len < len) return FALSE; + if (wcsncmp(attr->name.ptr, L"xmlns", len)) return FALSE; + return (attr->name.len == len || attr->name.ptr[len] == ':'); +} + +static void push_xmlns(struct xml_parser *parser, const struct xml_attr *attr) +{ + const int len = wcslen(L"xmlns"); + struct xml_attr *ns; + + if (parser->ns_pos == MAX_NAMESPACES - 1) + { + FIXME("Too many namespaces.\n"); + set_error(parser); + return; + } + ns = &parser->namespaces[parser->ns_pos++]; + ns->value = attr->value; + if (attr->name.len > len) + { + ns->name.ptr = attr->name.ptr + len + 1; + ns->name.len = attr->name.len - len - 1; + } + else ns->name = empty_xmlstr; +} + +static xmlstr_t find_xmlns(struct xml_parser *parser, const xmlstr_t *name) +{ + int i; + + for (i = parser->ns_pos - 1; i >= 0; i--) + { + if (parser->namespaces[i].name.len == name->len && + !wcsncmp(parser->namespaces[i].name.ptr, name->ptr, name->len)) + return parser->namespaces[i].value; + } + if (parser->ns_pos) WARN("Namespace %s not found.\n", debugstr_xmlstr(name)); + return empty_xmlstr; +} + +static void skip_xml_spaces(struct xml_parser *parser) +{ + while (parser->ptr < parser->end && isxmlspace(*parser->ptr)) + parser->ptr++; +} + +static BOOL next_xml_attr(struct xml_parser *parser, struct xml_attr *attr, BOOL *end) +{ + const WCHAR *ptr; + WCHAR quote; + + *end = FALSE; + + if (parser->error) return FALSE; + + skip_xml_spaces(parser); + + if (parser->ptr == parser->end) + return set_error(parser); + + if (*parser->ptr == '/') + { + parser->ptr++; + if (parser->ptr == parser->end || *parser->ptr != '>') + return set_error(parser); + + parser->ptr++; + *end = TRUE; + return FALSE; + } + + if (*parser->ptr == '>') + { + parser->ptr++; + return FALSE; + } + + ptr = parser->ptr; + while (ptr < parser->end && *ptr != '=' && *ptr != '>' && !isxmlspace(*ptr)) ptr++; + + if (ptr == parser->end) + return set_error(parser); + + attr->name.ptr = parser->ptr; + attr->name.len = ptr-parser->ptr; + parser->ptr = ptr; + + /* skip spaces before '=' */ + while (ptr < parser->end && *ptr != '=' && isxmlspace(*ptr)) ptr++; + if (ptr == parser->end || *ptr != '=') + return set_error(parser); + + /* skip '=' itself */ + ptr++; + if (ptr == parser->end) + return set_error(parser); + + /* skip spaces after '=' */ + while (ptr < parser->end && *ptr != '"' && *ptr != '\'' && isxmlspace(*ptr)) ptr++; + + if (ptr == parser->end || (*ptr != '"' && *ptr != '\'')) return set_error(parser); + + quote = *ptr++; + attr->value.ptr = ptr; + if (ptr == parser->end) + return set_error(parser); + + while (ptr < parser->end && *ptr != quote) ptr++; + if (ptr == parser->end) + { + parser->ptr = parser->end; + return set_error(parser); + } + + attr->value.len = ptr - attr->value.ptr; + parser->ptr = ptr + 1; + if (parser->ptr != parser->end) return TRUE; + + return set_error(parser); +} + +static void read_xml_elem(struct xml_parser *parser, struct xml_elem *elem) +{ + const WCHAR *ptr = parser->ptr; + + elem->ns = empty_xmlstr; + elem->name.ptr = ptr; + while (ptr < parser->end && !isxmlspace(*ptr) && *ptr != '>' && *ptr != '/') + { + if (*ptr == ':') + { + elem->ns.ptr = elem->name.ptr; + elem->ns.len = ptr - elem->ns.ptr; + elem->name.ptr = ptr + 1; + } + ptr++; + } + elem->name.len = ptr - elem->name.ptr; + parser->ptr = ptr; +} + +static inline BOOL is_special_xml_markup(const struct xml_elem *elem) +{ + return *elem->name.ptr == '!' || *elem->name.ptr == '?'; +} + +static BOOL skip_special_xml_markup(struct xml_parser *parser, struct xml_elem *elem) +{ + const WCHAR *ptr; + + if (parser->error) return FALSE; + + if (elem->name.len > 1 && elem->name.ptr[0] == '!' && elem->name.ptr[1] == '[') + { + /* */ + for (ptr = parser->ptr; ptr < parser->end - 3; ptr++) + { + if (ptr[0] == ']' && ptr[1] == ']' && ptr[2] == '>') + { + parser->ptr = ptr + 3; + return TRUE; + } + } + } + else if (xmlstr_eq(&elem->name, L"!--")) + { + /* */ + for (ptr = parser->ptr; ptr < parser->end - 2; ptr++) + { + if (ptr[0] == '-' && ptr[1] == '-' && ptr[2] == '>') + { + parser->ptr = ptr + 3; + return TRUE; + } + } + } + else if (*elem->name.ptr == '!') + { + /* */ + for (ptr = parser->ptr; ptr < parser->end; ptr++) + { + if (*ptr == '>') + { + parser->ptr = ptr + 1; + return TRUE; + } + } + } + else if (*elem->name.ptr == '?') + { + /* */ + for (ptr = parser->ptr; ptr < parser->end - 1; ptr++) + { + if (ptr[0] == '?' && ptr[1] == '>') + { + parser->ptr = ptr + 2; + return TRUE; + } + } + } + + return FALSE; +} + +static BOOL next_xml_elem(struct xml_parser *parser, struct xml_elem *elem, const struct xml_elem *parent) +{ + const WCHAR *ptr; + struct xml_attr attr; + BOOL end = FALSE; + + parser->ns_pos = parent->ns_pos; /* restore namespace stack to parent state */ + + if (parser->error) return FALSE; + + skip_xml_spaces(parser); + + if (parser->ptr == parser->end || *parser->ptr != '<') + return set_error(parser); + parser->ptr++; + + /* check for element terminating the parent element */ + if (parser->ptr < parser->end && *parser->ptr == '/') + { + parser->ptr++; + read_xml_elem(parser, elem); + elem->ns = find_xmlns(parser, &elem->ns); + if (!xml_name_eq(elem, parent)) + { + ERR("Wrong closing element %s for %s.\n", + debugstr_xmlstr(&elem->name), debugstr_xmlstr(&parent->name)); + return set_error(parser); + } + skip_xml_spaces(parser); + if (parser->ptr == parser->end || *parser->ptr++ != '>') + return set_error(parser); + return FALSE; + } + + read_xml_elem(parser, elem); + if (!elem->name.len) + return set_error(parser); + + if (!is_special_xml_markup(elem)) + { + /* parse namespace attributes */ + ptr = parser->ptr; + while (next_xml_attr(parser, &attr, &end)) + { + if (is_xmlns_attr(&attr)) push_xmlns(parser, &attr); + } + parser->ptr = ptr; + elem->ns = find_xmlns(parser, &elem->ns); + elem->ns_pos = parser->ns_pos; + } + + if (parser->ptr != parser->end) return TRUE; + else return set_error(parser); +} + +static HRESULT parse_ssml_speak_elem(struct xml_parser *parser, SPVSTATE *state) +{ + return E_NOTIMPL; +} + +static HRESULT parse_ssml_contents(const WCHAR *contents, const WCHAR *end, SPVSTATE *state, SPVTEXTFRAG **frag_list) +{ + struct xml_parser parser = {0}; + struct xml_elem parent = {0}; + struct xml_elem elem; + HRESULT hr; + + parser.ptr = contents; + parser.end = end; + + /* Default SSML namespace. */ + parser.namespaces[0].name = empty_xmlstr; + parser.namespaces[0].value.ptr = ssml_ns; + parser.namespaces[0].value.len = wcslen(ssml_ns); + parser.ns_pos = 1; + + parent.ns = parser.namespaces[0].value; + parent.ns_pos = 1; + + for (;;) + { + if (!next_xml_elem(&parser, &elem, &parent)) + return SPERR_UNSUPPORTED_FORMAT; + + if (!is_special_xml_markup(&elem)) + break; + if (!skip_special_xml_markup(&parser, &elem)) + return SPERR_UNSUPPORTED_FORMAT; + } + + if (!xml_elem_eq(&elem, ssml_ns, L"speak")) + return SPERR_UNSUPPORTED_FORMAT; + if (FAILED(hr = parse_ssml_speak_elem(&parser, state))) + return hr; + + if (next_xml_elem(&parser, &elem, &parent)) + { + ERR("Unexpected element %s after .\n", debugstr_xmlstr(&elem.name)); + return SPERR_UNSUPPORTED_FORMAT; + } + + if (parser.error) + return SPERR_UNSUPPORTED_FORMAT; + + return S_OK; +} + HRESULT parse_sapi_xml(const WCHAR *contents, DWORD parse_flag, BOOL persist, SPVSTATE *global_state, SPVTEXTFRAG **frag_list) { - FIXME("(%p, %#lx, %d, %p, %p): stub.\n", contents, parse_flag, persist, global_state, frag_list); - return E_NOTIMPL; + SPVSTATE state = *global_state; + const WCHAR *end; + HRESULT hr; + + TRACE("(%p, %#lx, %d, %p, %p).\n", contents, parse_flag, persist, global_state, frag_list); + + if (parse_flag == SPF_PARSE_SAPI) + { + FIXME("SAPI XML parsing is not implemented.\n"); + return E_NOTIMPL; + } + + assert(!state.pPhoneIds); + assert(!state.Context.pCategory && !state.Context.pBefore && !state.Context.pAfter); + + end = contents + wcslen(contents); + + if (FAILED(hr = parse_ssml_contents(contents, end, &state, frag_list))) + return hr; + + if (persist) + { + assert(!state.pPhoneIds); + assert(!state.Context.pCategory && !state.Context.pBefore && !state.Context.pAfter); + + *global_state = state; + } + + return S_OK; } From ce1c20bbfa3db8d74e6677944d5ac79bf212133e Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Wed, 9 Jul 2025 18:55:27 -0400 Subject: [PATCH 087/454] sapi/xml: Parse the SSML root element. (cherry picked from commit 3164ebe5a215424ff1f733ae7916a8ebeaef3f19) CW-Bug-Id: #22894 --- dlls/sapi/tests/tts.c | 6 +- dlls/sapi/xml.c | 135 ++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 133 insertions(+), 8 deletions(-) diff --git a/dlls/sapi/tests/tts.c b/dlls/sapi/tests/tts.c index 49d312fed705..f355ff7c98d7 100644 --- a/dlls/sapi/tests/tts.c +++ b/dlls/sapi/tests/tts.c @@ -998,13 +998,13 @@ static void test_spvoice_ssml(void) reset_engine_params(&test_engine); hr = ISpVoice_Speak(voice, bad_text1, SPF_IS_XML | SPF_PARSE_SSML, NULL); - todo_wine ok(hr == SPERR_UNSUPPORTED_FORMAT, "got %#lx.\n", hr); + ok(hr == SPERR_UNSUPPORTED_FORMAT, "got %#lx.\n", hr); hr = ISpVoice_Speak(voice, bad_text2, SPF_IS_XML | SPF_PARSE_SSML, NULL); - todo_wine ok(hr == SPERR_UNSUPPORTED_FORMAT, "got %#lx.\n", hr); + ok(hr == SPERR_UNSUPPORTED_FORMAT, "got %#lx.\n", hr); hr = ISpVoice_Speak(voice, bad_text3, SPF_IS_XML | SPF_PARSE_SSML, NULL); - todo_wine ok(hr == SPERR_UNSUPPORTED_FORMAT || broken(hr == S_OK) /* win7 */, "got %#lx.\n", hr); + ok(hr == SPERR_UNSUPPORTED_FORMAT || broken(hr == S_OK) /* win7 */, "got %#lx.\n", hr); reset_engine_params(&test_engine); diff --git a/dlls/sapi/xml.c b/dlls/sapi/xml.c index 442537fef4cc..7ed7505f355e 100644 --- a/dlls/sapi/xml.c +++ b/dlls/sapi/xml.c @@ -80,11 +80,32 @@ static inline const char *debugstr_xmlstr(const xmlstr_t *str) return debugstr_wn(str->ptr, str->len); } +static WCHAR *xmlstrcpyW(WCHAR *dst, const xmlstr_t* src) +{ + memcpy(dst, src->ptr, src->len * sizeof(WCHAR)); + dst[src->len] = 0; + return dst; +} + +static WCHAR *xmlstrdupW(const xmlstr_t* str) +{ + WCHAR *strW; + + if (!(strW = malloc((str->len + 1) * sizeof(WCHAR)))) + return NULL; + return xmlstrcpyW(strW, str); +} + static inline BOOL xmlstr_eq(const xmlstr_t* xmlstr, const WCHAR *str) { return !wcsncmp(xmlstr->ptr, str, xmlstr->len) && !str[xmlstr->len]; } +static inline BOOL xml_attr_eq(const struct xml_attr *attr, const WCHAR *name) +{ + return xmlstr_eq(&attr->name, name); +} + static inline BOOL xml_elem_eq(const struct xml_elem *elem, const WCHAR *ns, const WCHAR *name) { if (!xmlstr_eq(&elem->name, name)) return FALSE; @@ -367,11 +388,118 @@ static BOOL next_xml_elem(struct xml_parser *parser, struct xml_elem *elem, cons else return set_error(parser); } -static HRESULT parse_ssml_speak_elem(struct xml_parser *parser, SPVSTATE *state) +static BOOL next_text_or_xml_elem(struct xml_parser *parser, BOOL *is_text, struct xml_elem *elem, + const struct xml_elem *parent) { + if (parser->error) return FALSE; + + while (parser->ptr < parser->end) + { + if (*parser->ptr == '<') + { + *is_text = FALSE; + + if (!next_xml_elem(parser, elem, parent)) + return FALSE; + + if (!is_special_xml_markup(elem)) + return TRUE; + else if (!skip_special_xml_markup(parser, elem)) + return set_error(parser); + } + else + { + *is_text = TRUE; + return TRUE; + } + } + return FALSE; +} + +static HRESULT add_sapi_text_fragment(struct xml_parser *parser, const SPVSTATE *state) +{ + FIXME("stub.\n"); return E_NOTIMPL; } +static HRESULT parse_ssml_elems(struct xml_parser *parser, const SPVSTATE *state, const struct xml_elem *parent) +{ + struct xml_elem elem; + BOOL is_text; + HRESULT hr = S_OK; + + while (SUCCEEDED(hr) && next_text_or_xml_elem(parser, &is_text, &elem, parent)) + { + if (is_text) + { + hr = add_sapi_text_fragment(parser, state); + } + else + { + FIXME("Unknown element %s.\n", debugstr_xmlstr(&elem.name)); + hr = E_NOTIMPL; + } + } + + if (SUCCEEDED(hr) && parser->error) + return SPERR_UNSUPPORTED_FORMAT; + return hr; +} + +static HRESULT parse_ssml_speak_elem(struct xml_parser *parser, const struct xml_elem *parent, SPVSTATE *state) +{ + struct xml_attr attr; + BOOL end = FALSE; + BOOL has_version = FALSE; + LCID lcid = 0; + + while (next_xml_attr(parser, &attr, &end)) + { + if (xml_attr_eq(&attr, L"version")) + { + if (!xmlstr_eq(&attr.value, L"1.0")) + { + ERR("Invalid SSML version %s.\n", debugstr_xmlstr(&attr.value)); + return SPERR_UNSUPPORTED_FORMAT; + } + + has_version = TRUE; + } + else if (xml_attr_eq(&attr, L"xml:lang")) + { + WCHAR *lang = xmlstrdupW(&attr.value); + + if (!lang) return E_OUTOFMEMORY; + lcid = LocaleNameToLCID(lang, 0); + free(lang); + + if (!lcid) + { + ERR("Invalid xml:lang %s.\n", debugstr_xmlstr(&attr.value)); + return SPERR_UNSUPPORTED_FORMAT; + } + } + } + + if (!has_version) + { + ERR("Missing version attribute.\n"); + return SPERR_UNSUPPORTED_FORMAT; + } + if (!lcid) + { + ERR("Missing xml:lang attribute.\n"); + return SPERR_UNSUPPORTED_FORMAT; + } + + if (end) return S_OK; + + state->eAction = SPVA_Speak; + state->LangID = LANGIDFROMLCID(lcid); + + return parse_ssml_elems(parser, state, parent); +} + static HRESULT parse_ssml_contents(const WCHAR *contents, const WCHAR *end, SPVSTATE *state, SPVTEXTFRAG **frag_list) { struct xml_parser parser = {0}; @@ -404,7 +532,7 @@ static HRESULT parse_ssml_contents(const WCHAR *contents, const WCHAR *end, SPVS if (!xml_elem_eq(&elem, ssml_ns, L"speak")) return SPERR_UNSUPPORTED_FORMAT; - if (FAILED(hr = parse_ssml_speak_elem(&parser, state))) + if (FAILED(hr = parse_ssml_speak_elem(&parser, &elem, state))) return hr; if (next_xml_elem(&parser, &elem, &parent)) @@ -413,9 +541,6 @@ static HRESULT parse_ssml_contents(const WCHAR *contents, const WCHAR *end, SPVS return SPERR_UNSUPPORTED_FORMAT; } - if (parser.error) - return SPERR_UNSUPPORTED_FORMAT; - return S_OK; } From db714ef64c7225075f91a5597299355c81de1a22 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Fri, 27 Jun 2025 11:37:43 -0400 Subject: [PATCH 088/454] sapi/xml: Implement add_sapi_text_fragement(). (cherry picked from commit dfcf7be3cf394167341897e2d32eddb742d42eb3) CW-Bug-Id: #22894 --- dlls/sapi/tests/tts.c | 48 +++++++++++++++++++------------------------ dlls/sapi/xml.c | 44 ++++++++++++++++++++++++++++++++++++--- 2 files changed, 62 insertions(+), 30 deletions(-) diff --git a/dlls/sapi/tests/tts.c b/dlls/sapi/tests/tts.c index f355ff7c98d7..dbb8736f76e2 100644 --- a/dlls/sapi/tests/tts.c +++ b/dlls/sapi/tests/tts.c @@ -958,42 +958,36 @@ static void test_spvoice_ssml(void) reset_engine_params(&test_engine); hr = ISpVoice_Speak(voice, text1, SPF_IS_XML | SPF_PARSE_SSML, NULL); - todo_wine ok(hr == S_OK, "got %#lx.\n", hr); - todo_wine ok(test_engine.frag_count == 1, "got %Iu.\n", test_engine.frag_count); - - if (test_engine.frag_count == 1) { - check_frag_text(0, L"text1"); - - check_frag_state_field(0, eAction, SPVA_Speak, "%d"); - ok(test_engine.frags[0].State.LangID == 0x409 || broken(test_engine.frags[0].State.LangID == 0) /* win7 */, - "got %#hx.\n", test_engine.frags[0].State.LangID); - check_frag_state_field(0, EmphAdj, 0, "%ld"); - check_frag_state_field(0, RateAdj, 0, "%ld"); - check_frag_state_field(0, Volume, 100, "%lu"); - check_frag_state_field(0, PitchAdj.MiddleAdj, 0, "%ld"); - check_frag_state_field(0, PitchAdj.RangeAdj, 0, "%ld"); - check_frag_state_field(0, SilenceMSecs, 0, "%lu"); - check_frag_state_field(0, ePartOfSpeech, SPPS_Unknown, "%#x"); - } + ok(hr == S_OK, "got %#lx.\n", hr); + ok(test_engine.frag_count == 1, "got %Iu.\n", test_engine.frag_count); + check_frag_text(0, L"text1"); + + check_frag_state_field(0, eAction, SPVA_Speak, "%d"); + ok(test_engine.frags[0].State.LangID == 0x409 || broken(test_engine.frags[0].State.LangID == 0) /* win7 */, + "got %#hx.\n", test_engine.frags[0].State.LangID); + check_frag_state_field(0, EmphAdj, 0, "%ld"); + check_frag_state_field(0, RateAdj, 0, "%ld"); + check_frag_state_field(0, Volume, 100, "%lu"); + check_frag_state_field(0, PitchAdj.MiddleAdj, 0, "%ld"); + check_frag_state_field(0, PitchAdj.RangeAdj, 0, "%ld"); + check_frag_state_field(0, SilenceMSecs, 0, "%lu"); + check_frag_state_field(0, ePartOfSpeech, SPPS_Unknown, "%#x"); reset_engine_params(&test_engine); /* SSML autodetection when SPF_PARSE_SSML is not specified. */ hr = ISpVoice_Speak(voice, text1, SPF_IS_XML, NULL); - todo_wine ok(hr == S_OK, "got %#lx.\n", hr); - todo_wine ok(test_engine.frag_count == 1, "got %Iu.\n", test_engine.frag_count); - - if (test_engine.frag_count == 1) - check_frag_text(0, L"text1"); + ok(hr == S_OK, "got %#lx.\n", hr); + ok(test_engine.frag_count == 1, "got %Iu.\n", test_engine.frag_count); + check_frag_text(0, L"text1"); reset_engine_params(&test_engine); /* XML and SSML autodetection when SPF_IS_XML is not specified. */ hr = ISpVoice_Speak(voice, text1, SPF_DEFAULT, NULL); - todo_wine ok(hr == S_OK, "got %#lx.\n", hr); - todo_wine ok(test_engine.frag_count == 1, "got %Iu.\n", test_engine.frag_count); - if (test_engine.frag_count == 1) - check_frag_text(0, L"text1"); + ok(hr == S_OK, "got %#lx.\n", hr); + ok(test_engine.frag_count == 1, "got %Iu.\n", test_engine.frag_count); + check_frag_text(0, L"text1"); reset_engine_params(&test_engine); @@ -1009,7 +1003,7 @@ static void test_spvoice_ssml(void) reset_engine_params(&test_engine); hr = ISpVoice_Speak(voice, text2, SPF_IS_XML | SPF_PARSE_SSML, NULL); - todo_wine ok(hr == S_OK || broken(hr == SPERR_UNSUPPORTED_FORMAT) /* win7 */, "got %#lx.\n", hr); + ok(hr == S_OK || broken(hr == SPERR_UNSUPPORTED_FORMAT) /* win7 */, "got %#lx.\n", hr); if (hr == S_OK) { ok(test_engine.frag_count == 1, "got %Iu.\n", test_engine.frag_count); diff --git a/dlls/sapi/xml.c b/dlls/sapi/xml.c index 7ed7505f355e..518a687a5e82 100644 --- a/dlls/sapi/xml.c +++ b/dlls/sapi/xml.c @@ -64,11 +64,14 @@ struct xml_attr struct xml_parser { + const WCHAR *base; const WCHAR *ptr; const WCHAR *end; struct xml_attr namespaces[MAX_NAMESPACES]; int ns_pos; BOOL error; + + SPVTEXTFRAG *tail_frag; }; static const xmlstr_t empty_xmlstr; @@ -418,8 +421,40 @@ static BOOL next_text_or_xml_elem(struct xml_parser *parser, BOOL *is_text, stru static HRESULT add_sapi_text_fragment(struct xml_parser *parser, const SPVSTATE *state) { - FIXME("stub.\n"); - return E_NOTIMPL; + const WCHAR *text, *ptr; + SPVTEXTFRAG *frag; + size_t len; + WCHAR *buf; + + if (parser->error) + return SPERR_UNSUPPORTED_FORMAT; + + text = parser->ptr; + + for (ptr = parser->ptr; ptr < parser->end; ptr++) if (*ptr == '<') break; + len = ptr - parser->ptr; + parser->ptr = ptr; + + if (!len) + return S_OK; + + if (!(frag = malloc(sizeof(*frag) + (len + 1) * sizeof(WCHAR)))) + return E_OUTOFMEMORY; + + buf = (WCHAR *)(frag + 1); + memcpy(buf, text, len * sizeof(WCHAR)); + buf[len] = 0; + + frag->pNext = NULL; + frag->State = *state; + frag->pTextStart = buf; + frag->ulTextLen = len; + frag->ulTextSrcOffset = text - parser->base; + + parser->tail_frag->pNext = frag; + parser->tail_frag = frag; + + return S_OK; } static HRESULT parse_ssml_elems(struct xml_parser *parser, const SPVSTATE *state, const struct xml_elem *parent) @@ -504,11 +539,13 @@ static HRESULT parse_ssml_contents(const WCHAR *contents, const WCHAR *end, SPVS { struct xml_parser parser = {0}; struct xml_elem parent = {0}; + SPVTEXTFRAG head_frag = {0}; struct xml_elem elem; HRESULT hr; - parser.ptr = contents; + parser.base = parser.ptr = contents; parser.end = end; + parser.tail_frag = &head_frag; /* Default SSML namespace. */ parser.namespaces[0].name = empty_xmlstr; @@ -541,6 +578,7 @@ static HRESULT parse_ssml_contents(const WCHAR *contents, const WCHAR *end, SPVS return SPERR_UNSUPPORTED_FORMAT; } + *frag_list = head_frag.pNext; return S_OK; } From 1ddc8f108a15941061703a1dc85b9e3686a4b928 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Tue, 24 Jun 2025 13:01:22 -0400 Subject: [PATCH 089/454] sapi/xml: Parse the

and SSML elements. (cherry picked from commit 304b3dc8802ffdc79a84b9f54d186f07619c5865) CW-Bug-Id: #22894 --- dlls/sapi/tests/tts.c | 4 ++-- dlls/sapi/xml.c | 9 ++++++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/dlls/sapi/tests/tts.c b/dlls/sapi/tests/tts.c index dbb8736f76e2..569ee918164e 100644 --- a/dlls/sapi/tests/tts.c +++ b/dlls/sapi/tests/tts.c @@ -1015,8 +1015,8 @@ static void test_spvoice_ssml(void) reset_engine_params(&test_engine); hr = ISpVoice_Speak(voice, text3, SPF_IS_XML, NULL); - todo_wine ok(hr == S_OK, "got %#lx.\n", hr); - todo_wine ok(test_engine.frag_count == 7 || broken(test_engine.frag_count == 1) /* win7 */, + ok(hr == S_OK, "got %#lx.\n", hr); + ok(test_engine.frag_count == 7 || broken(test_engine.frag_count == 1) /* win7 */, "got %Iu.\n", test_engine.frag_count); if (test_engine.frag_count == 7) { diff --git a/dlls/sapi/xml.c b/dlls/sapi/xml.c index 518a687a5e82..aad32462272f 100644 --- a/dlls/sapi/xml.c +++ b/dlls/sapi/xml.c @@ -459,8 +459,9 @@ static HRESULT add_sapi_text_fragment(struct xml_parser *parser, const SPVSTATE static HRESULT parse_ssml_elems(struct xml_parser *parser, const SPVSTATE *state, const struct xml_elem *parent) { + struct xml_attr attr; struct xml_elem elem; - BOOL is_text; + BOOL is_text, end; HRESULT hr = S_OK; while (SUCCEEDED(hr) && next_text_or_xml_elem(parser, &is_text, &elem, parent)) @@ -469,6 +470,12 @@ static HRESULT parse_ssml_elems(struct xml_parser *parser, const SPVSTATE *state { hr = add_sapi_text_fragment(parser, state); } + else if (xml_elem_eq(&elem, ssml_ns, L"p") || xml_elem_eq(&elem, ssml_ns, L"s")) + { + while (next_xml_attr(parser, &attr, &end)) ; + if (end) continue; + hr = parse_ssml_elems(parser, state, &elem); + } else { FIXME("Unknown element %s.\n", debugstr_xmlstr(&elem.name)); From 8ba594bab3d94a3284f3f0f02c992d08fa6ce692 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Fri, 4 Jul 2025 18:05:40 -0400 Subject: [PATCH 090/454] sapi/xml: Parse the rate attribute in the SSML element. (cherry picked from commit d2de72b4a115f1e105c6030070dc17ccbe73e6a3) CW-Bug-Id: #22894 --- dlls/sapi/tests/tts.c | 26 +++++------ dlls/sapi/xml.c | 100 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 112 insertions(+), 14 deletions(-) diff --git a/dlls/sapi/tests/tts.c b/dlls/sapi/tests/tts.c index 569ee918164e..86b484b268e1 100644 --- a/dlls/sapi/tests/tts.c +++ b/dlls/sapi/tests/tts.c @@ -1052,24 +1052,22 @@ static void test_spvoice_ssml(void) reset_engine_params(&test_engine); hr = ISpVoice_Speak(voice, text4, SPF_DEFAULT, NULL); - todo_wine ok(hr == S_OK, "got %#lx.\n", hr); - todo_wine ok(test_engine.frag_count == 2, "got %Iu.\n", test_engine.frag_count); + ok(hr == S_OK, "got %#lx.\n", hr); + ok(test_engine.frag_count == 2, "got %Iu.\n", test_engine.frag_count); - if (test_engine.frag_count == 2) { - check_frag_text(0, L"One, "); - check_frag_state_field(0, eAction, SPVA_Speak, "%d"); - check_frag_state_field(0, RateAdj, 0, "%ld"); + check_frag_text(0, L"One, "); + check_frag_state_field(0, eAction, SPVA_Speak, "%d"); + check_frag_state_field(0, RateAdj, 0, "%ld"); - check_frag_text(1, L"two."); - check_frag_state_field(1, eAction, SPVA_Speak, "%d"); - check_frag_state_field(1, RateAdj, -17, "%ld"); /* 3^(-17/10) ~= 0.15 */ - } + check_frag_text(1, L"two."); + check_frag_state_field(1, eAction, SPVA_Speak, "%d"); + check_frag_state_field(1, RateAdj, -17, "%ld"); /* 3^(-17/10) ~= 0.15 */ reset_engine_params(&test_engine); hr = ISpVoice_Speak(voice, text5, SPF_IS_XML | SPF_PARSE_SSML, NULL); - todo_wine ok(hr == S_OK, "got %#lx.\n", hr); - todo_wine ok(test_engine.frag_count == 8 || broken(test_engine.frag_count == 3) /* win7 */, + ok(hr == S_OK, "got %#lx.\n", hr); + ok(test_engine.frag_count == 8 || broken(test_engine.frag_count == 3) /* win7 */, "got %Iu.\n", test_engine.frag_count); if (test_engine.frag_count == 8) { @@ -1086,7 +1084,7 @@ static void test_spvoice_ssml(void) reset_engine_params(&test_engine); hr = ISpVoice_Speak(voice, text6, SPF_IS_XML | SPF_PARSE_SSML, NULL); - todo_wine ok(hr == S_OK || broken(hr == SPERR_UNSUPPORTED_FORMAT) /* win7 */, "got %#lx.\n", hr); + ok(hr == S_OK || broken(hr == SPERR_UNSUPPORTED_FORMAT) /* win7 */, "got %#lx.\n", hr); if (hr == S_OK) { ok(test_engine.frag_count == 5, "got %Iu.\n", test_engine.frag_count); @@ -1101,7 +1099,7 @@ static void test_spvoice_ssml(void) reset_engine_params(&test_engine); hr = ISpVoice_Speak(voice, text7, SPF_IS_XML | SPF_PARSE_SSML, NULL); - todo_wine ok(hr == S_OK || broken(hr == SPERR_UNSUPPORTED_FORMAT) /* win7 */, "got %#lx.\n", hr); + ok(hr == S_OK || broken(hr == SPERR_UNSUPPORTED_FORMAT) /* win7 */, "got %#lx.\n", hr); if (hr == S_OK) { ok(test_engine.frag_count == 5, "got %Iu.\n", test_engine.frag_count); diff --git a/dlls/sapi/xml.c b/dlls/sapi/xml.c index aad32462272f..d6faa45277a9 100644 --- a/dlls/sapi/xml.c +++ b/dlls/sapi/xml.c @@ -25,6 +25,7 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ +#include #include #define COBJMACROS @@ -457,6 +458,101 @@ static HRESULT add_sapi_text_fragment(struct xml_parser *parser, const SPVSTATE return S_OK; } +struct string_value +{ + const WCHAR *str; + LONG value; +}; + +static BOOL lookup_string_value(const struct string_value *table, size_t count, const xmlstr_t *str, LONG *res) +{ + size_t i; + + for (i = 0; i < count; i++) + { + if (xmlstr_eq(str, table[i].str)) + { + *res = table[i].value; + return TRUE; + } + } + return FALSE; +} + +static HRESULT parse_double_value(const xmlstr_t *value, double *res, size_t *read_len) +{ + WCHAR *buf, *end; + + if (!value->len) + return SPERR_UNSUPPORTED_FORMAT; + + if (!(buf = xmlstrdupW(value))) + return E_OUTOFMEMORY; + + *res = wcstod(buf, &end); + *read_len = end - buf; + + free(buf); + return S_OK; +} + +static HRESULT parse_ssml_elems(struct xml_parser *parser, const SPVSTATE *state, const struct xml_elem *parent); + +static HRESULT parse_ssml_prosody_elem(struct xml_parser *parser, SPVSTATE state, const struct xml_elem *parent) +{ + static const struct string_value rate_values[] = + { + { L"x-slow", -9 }, + { L"slow", -4 }, + { L"medium", 0 }, + { L"fast", 4 }, + { L"x-fast", 9 }, + }; + + struct xml_attr attr; + BOOL end = FALSE; + size_t read_len; + HRESULT hr; + + while (next_xml_attr(parser, &attr, &end)) + { + if (xml_attr_eq(&attr, L"rate")) + { + double rate; + + if (lookup_string_value(rate_values, ARRAY_SIZE(rate_values), &attr.value, &state.RateAdj)) + continue; + + if (FAILED(hr = parse_double_value(&attr.value, &rate, &read_len))) + return hr; + if (read_len < attr.value.len - 1 || + (read_len == attr.value.len - 1 && attr.value.ptr[read_len] != '%')) + { + ERR("Invalid value %s for the rate attribute in .\n", debugstr_xmlstr(&attr.value)); + return SPERR_UNSUPPORTED_FORMAT; + } + + if (attr.value.ptr[attr.value.len - 1] == '%') + rate = 1 + rate / 100; + + if (rate < 0) + state.RateAdj = 0; + else if (rate <= 0.01) + state.RateAdj = -10; + else + state.RateAdj = lround(log(rate) * (10 / log(3))); + } + else + { + FIXME("Unknown attribute %s.\n", debugstr_xmlstr(&attr.name)); + return E_NOTIMPL; + } + } + + if (end) return S_OK; + return parse_ssml_elems(parser, &state, parent); +} + static HRESULT parse_ssml_elems(struct xml_parser *parser, const SPVSTATE *state, const struct xml_elem *parent) { struct xml_attr attr; @@ -476,6 +572,10 @@ static HRESULT parse_ssml_elems(struct xml_parser *parser, const SPVSTATE *state if (end) continue; hr = parse_ssml_elems(parser, state, &elem); } + else if (xml_elem_eq(&elem, ssml_ns, L"prosody")) + { + hr = parse_ssml_prosody_elem(parser, *state, &elem); + } else { FIXME("Unknown element %s.\n", debugstr_xmlstr(&elem.name)); From 71bb96b994723733c0d7c6b9356e947f04874964 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Thu, 26 Jun 2025 22:38:08 -0400 Subject: [PATCH 091/454] sapi/xml: Parse the volume attribute in the SSML element. (cherry picked from commit fed5c19609eac8f1c2791e021a6eba08f73e3b4c) CW-Bug-Id: #22894 --- dlls/sapi/tests/tts.c | 4 ++-- dlls/sapi/xml.c | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/dlls/sapi/tests/tts.c b/dlls/sapi/tests/tts.c index 86b484b268e1..4f75aa88cac8 100644 --- a/dlls/sapi/tests/tts.c +++ b/dlls/sapi/tests/tts.c @@ -1123,7 +1123,7 @@ static void test_spvoice_ssml(void) reset_engine_params(&test_engine); hr = ISpVoice_Speak(voice, text8, SPF_IS_XML | SPF_PARSE_SSML, NULL); - todo_wine ok(hr == S_OK, "got %#lx.\n", hr); + ok(hr == S_OK, "got %#lx.\n", hr); if (hr == S_OK) { ok(test_engine.frag_count == 9, "got %Iu.\n", test_engine.frag_count); @@ -1147,7 +1147,7 @@ static void test_spvoice_ssml(void) reset_engine_params(&test_engine); hr = ISpVoice_Speak(voice, text9, SPF_IS_XML | SPF_PARSE_SSML, NULL); - todo_wine ok(hr == S_OK || broken(hr == SPERR_UNSUPPORTED_FORMAT) /* win7 */, "got %#lx.\n", hr); + ok(hr == S_OK || broken(hr == SPERR_UNSUPPORTED_FORMAT) /* win7 */, "got %#lx.\n", hr); if (hr == S_OK) { ok(test_engine.frag_count == 7, "got %Iu.\n", test_engine.frag_count); diff --git a/dlls/sapi/xml.c b/dlls/sapi/xml.c index d6faa45277a9..d54df1ef88fe 100644 --- a/dlls/sapi/xml.c +++ b/dlls/sapi/xml.c @@ -496,6 +496,13 @@ static HRESULT parse_double_value(const xmlstr_t *value, double *res, size_t *re return S_OK; } +static inline long lclamp(long value, long value_min, long value_max) +{ + if (value < value_min) return value_min; + if (value > value_max) return value_max; + return value; +} + static HRESULT parse_ssml_elems(struct xml_parser *parser, const SPVSTATE *state, const struct xml_elem *parent); static HRESULT parse_ssml_prosody_elem(struct xml_parser *parser, SPVSTATE state, const struct xml_elem *parent) @@ -509,6 +516,16 @@ static HRESULT parse_ssml_prosody_elem(struct xml_parser *parser, SPVSTATE state { L"x-fast", 9 }, }; + static const struct string_value volume_values[] = + { + { L"silent", 0 }, + { L"x-soft", 20 }, + { L"soft", 40 }, + { L"medium", 60 }, + { L"loud", 80 }, + { L"x-loud", 100 }, + }; + struct xml_attr attr; BOOL end = FALSE; size_t read_len; @@ -542,6 +559,29 @@ static HRESULT parse_ssml_prosody_elem(struct xml_parser *parser, SPVSTATE state else state.RateAdj = lround(log(rate) * (10 / log(3))); } + else if (xml_attr_eq(&attr, L"volume")) + { + double volume; + + if (lookup_string_value(volume_values, ARRAY_SIZE(volume_values), &attr.value, (LONG *)&state.Volume)) + continue; + + if (FAILED(hr = parse_double_value(&attr.value, &volume, &read_len))) + return hr; + if (read_len < attr.value.len - 1 || + (read_len == attr.value.len - 1 && attr.value.ptr[read_len] != '%')) + { + ERR("Invalid value %s for the volume attribute in .\n", debugstr_xmlstr(&attr.value)); + return SPERR_UNSUPPORTED_FORMAT; + } + + if (attr.value.ptr[attr.value.len - 1] == '%') + volume = state.Volume * (1 + volume / 100); + else if (attr.value.ptr[0] == '+' || attr.value.ptr[0] == '-') + volume = state.Volume + volume; + + state.Volume = lclamp(lround(volume), 0, 100); + } else { FIXME("Unknown attribute %s.\n", debugstr_xmlstr(&attr.name)); From d0abf84a9f3188535a3c085ebef533dcbf598d6e Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Wed, 2 Jul 2025 16:22:13 -0400 Subject: [PATCH 092/454] sapi/xml: Parse the pitch attribute in the SSML element. (cherry picked from commit fd80e351e21a83b4f46345015f50df068ddbfa00) CW-Bug-Id: #22894 --- dlls/sapi/tests/tts.c | 53 +++++++++++++++++++++++++++++++++++++++++++ dlls/sapi/xml.c | 36 +++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+) diff --git a/dlls/sapi/tests/tts.c b/dlls/sapi/tests/tts.c index 4f75aa88cac8..43f8f93de1f2 100644 --- a/dlls/sapi/tests/tts.c +++ b/dlls/sapi/tests/tts.c @@ -922,6 +922,28 @@ static void test_spvoice_ssml(void) L"soft." L""; + static const WCHAR text10[] = + L"" + L"300Hz." + L"600Hz." + L"+300Hz." + L"-300Hz." + L"41.4%." + L"+41.4%." + L"-50%." + L"-98.99999%." + L"-99%." + L"-101%." + L"+100%." + L"-29%." + L"x-low." + L"low." + L"medium." + L"high." + L"x-high." + L"high-low." + L""; + ISpVoice *voice; ISpObjectToken *token; @@ -1162,6 +1184,37 @@ static void test_spvoice_ssml(void) check_frag_state_field(6, Volume, 40, "%lu"); /* soft */ } + reset_engine_params(&test_engine); + + hr = ISpVoice_Speak(voice, text10, SPF_IS_XML | SPF_PARSE_SSML, NULL); + ok(hr == S_OK || broken(hr == SPERR_UNSUPPORTED_FORMAT) /* win7 */, "got %#lx.\n", hr); + + if (hr == S_OK) { + ok(test_engine.frag_count == 18, "got %Iu.\n", test_engine.frag_count); + + check_frag_state_field(0, PitchAdj.MiddleAdj, 0, "%ld"); /* Absolute Hz values are ignored. */ + check_frag_state_field(1, PitchAdj.MiddleAdj, 0, "%ld"); + check_frag_state_field(2, PitchAdj.MiddleAdj, 0, "%ld"); + check_frag_state_field(3, PitchAdj.MiddleAdj, 0, "%ld"); + check_frag_state_field(4, PitchAdj.MiddleAdj, 12, "%ld"); /* 2^(12/24) ~= 1.414. */ + check_frag_state_field(5, PitchAdj.MiddleAdj, 12, "%ld"); /* 2^(12/24) ~= 1.414. */ + check_frag_state_field(6, PitchAdj.MiddleAdj, -24, "%ld"); /* 2^(-24/24) = 0.5. */ + check_frag_state_field(7, PitchAdj.MiddleAdj, -159, "%ld"); /* 2^(-159/24) ~= 0.0100001. */ + check_frag_state_field(8, PitchAdj.MiddleAdj, -10, "%ld"); /* -99%. */ + check_frag_state_field(9, PitchAdj.MiddleAdj, -10, "%ld"); /* -101%. */ + check_frag_state_field(10, PitchAdj.MiddleAdj, 24, "%ld"); /* 2^(24/24) = 1. */ + check_frag_state_field(11, PitchAdj.MiddleAdj, -12, "%ld"); /* 2^(-12/24) ~= 0.707. */ + + check_frag_state_field(12, PitchAdj.MiddleAdj, -9, "%ld"); /* x-low */ + check_frag_state_field(13, PitchAdj.MiddleAdj, -4, "%ld"); /* low */ + check_frag_state_field(14, PitchAdj.MiddleAdj, 0, "%ld"); /* medium */ + check_frag_state_field(15, PitchAdj.MiddleAdj, 4, "%ld"); /* high */ + check_frag_state_field(16, PitchAdj.MiddleAdj, 9, "%ld"); /* x-high */ + + check_frag_state_field(17, PitchAdj.MiddleAdj, -4, "%ld"); /* low */ + } + + reset_engine_params(&test_engine); ISpVoice_Release(voice); ISpObjectToken_Release(token); diff --git a/dlls/sapi/xml.c b/dlls/sapi/xml.c index d54df1ef88fe..e6fb9a17c708 100644 --- a/dlls/sapi/xml.c +++ b/dlls/sapi/xml.c @@ -526,6 +526,15 @@ static HRESULT parse_ssml_prosody_elem(struct xml_parser *parser, SPVSTATE state { L"x-loud", 100 }, }; + static const struct string_value pitch_values[] = + { + { L"x-low", -9 }, + { L"low", -4 }, + { L"medium", 0 }, + { L"high", 4 }, + { L"x-high", 9 }, + }; + struct xml_attr attr; BOOL end = FALSE; size_t read_len; @@ -582,6 +591,33 @@ static HRESULT parse_ssml_prosody_elem(struct xml_parser *parser, SPVSTATE state state.Volume = lclamp(lround(volume), 0, 100); } + else if (xml_attr_eq(&attr, L"pitch")) + { + double pitch; + + if (lookup_string_value(pitch_values, ARRAY_SIZE(pitch_values), &attr.value, &state.PitchAdj.MiddleAdj)) + continue; + + if (FAILED(hr = parse_double_value(&attr.value, &pitch, &read_len))) + return hr; + + if (attr.value.len > 2 && read_len == attr.value.len - 2 && + attr.value.ptr[read_len] == 'H' && attr.value.ptr[read_len + 1] == 'z') + { + WARN("Ignoring Hz pitch value %s in .\n", debugstr_xmlstr(&attr.value)); + continue; + } + else if (read_len != attr.value.len - 1 || attr.value.ptr[read_len] != '%') + { + ERR("Invalid value %s for the pitch attribute in .\n", debugstr_xmlstr(&attr.value)); + return SPERR_UNSUPPORTED_FORMAT; + } + + if (pitch > -99) + state.PitchAdj.MiddleAdj += lround(log2(1 + pitch / 100) * 24); + else + state.PitchAdj.MiddleAdj -= 10; + } else { FIXME("Unknown attribute %s.\n", debugstr_xmlstr(&attr.name)); From 7db77e738fd5de2894f6d6492d6ed752fb49f270 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Mon, 14 Jul 2025 15:22:32 -0400 Subject: [PATCH 093/454] sapi/stream: Remove the FIXME message for unknown ISpStream interfaces. SpVoice may query ISpStream objects for an ISpAudio interface, which currently generates unnecessary FIXME log messages. (cherry picked from commit e374980d0bfc7413e0cc1da5fcca5e9c8c851281) CW-Bug-Id: #22894 --- dlls/sapi/stream.c | 1 - 1 file changed, 1 deletion(-) diff --git a/dlls/sapi/stream.c b/dlls/sapi/stream.c index 41826b8c9493..6a43128acd5f 100644 --- a/dlls/sapi/stream.c +++ b/dlls/sapi/stream.c @@ -66,7 +66,6 @@ static HRESULT WINAPI spstream_QueryInterface(ISpStream *iface, REFIID iid, void else { *obj = NULL; - FIXME("interface %s not implemented.\n", debugstr_guid(iid)); return E_NOINTERFACE; } From adbbe5db536bab0b50e786516121df01eebdbf03 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Mon, 14 Jul 2025 13:24:32 -0400 Subject: [PATCH 094/454] sapi/tests: Test resampler support in ISpVoice. (cherry picked from commit 6126c918f94e5724780d383fb56f6427473944ae) CW-Bug-Id: #22894 --- dlls/sapi/tests/tts.c | 149 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 135 insertions(+), 14 deletions(-) diff --git a/dlls/sapi/tests/tts.c b/dlls/sapi/tests/tts.c index 43f8f93de1f2..4d3f32dc36b0 100644 --- a/dlls/sapi/tests/tts.c +++ b/dlls/sapi/tests/tts.c @@ -18,8 +18,13 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ +#include +#include + #define COBJMACROS +#include "objbase.h" + #include "sapiddk.h" #include "sperror.h" @@ -141,6 +146,8 @@ static void test_interfaces(void) #define TESTENGINE_CLSID L"{57C7E6B1-2FC2-4E8E-B968-1410A39E7198}" static const GUID CLSID_TestEngine = {0x57C7E6B1,0x2FC2,0x4E8E,{0xB9,0x68,0x14,0x10,0xA3,0x9E,0x71,0x98}}; +static const unsigned int test_engine_sample_rate = 22050; + struct test_engine { ISpTTSEngine ISpTTSEngine_iface; @@ -148,7 +155,10 @@ struct test_engine ISpObjectToken *token; - BOOL simulate_output; + + const char *output_data; + size_t output_len; + BOOL speak_called; DWORD flags; GUID fmtid; @@ -204,7 +214,8 @@ static void copy_frag_list(const SPVTEXTFRAG *frag_list, SPVTEXTFRAG **ret_frags static void reset_engine_params(struct test_engine *engine) { - engine->simulate_output = FALSE; + engine->output_data = NULL; + engine->output_len = 0; engine->speak_called = FALSE; engine->flags = 0xdeadbeef; memset(&engine->fmtid, 0xde, sizeof(engine->fmtid)); @@ -216,6 +227,32 @@ static void reset_engine_params(struct test_engine *engine) engine->frag_count = 0; } +static char *make_sin_data(int sin_freq, size_t time_ms, size_t sample_rate, size_t *len) +{ + double ang_freq; + char *data; + size_t i; + int val; + + *len = sample_rate * sizeof(int16_t) * time_ms / 1000; + if (!(data = malloc(*len))) + return NULL; + + if (!sin_freq) + { + memset(data, 0, *len); + return data; + } + + ang_freq = 2 * M_PI * sin_freq / sample_rate; + for (i = 0; i < *len / sizeof(int16_t); i++) + { + val = floor(32768 * sin(ang_freq * i) + 0.5); + ((int16_t *)data)[i] = min(max(-32768, val), 32767); + } + return data; +} + static inline struct test_engine *impl_from_ISpTTSEngine(ISpTTSEngine *iface) { return CONTAINING_RECORD(iface, struct test_engine, ISpTTSEngine_iface); @@ -258,9 +295,11 @@ static HRESULT WINAPI test_engine_Speak(ISpTTSEngine *iface, DWORD flags, REFGUI const WAVEFORMATEX *wfx, const SPVTEXTFRAG *frag_list, ISpTTSEngineSite *site) { + static const int num_out_iters = 5; + struct test_engine *engine = impl_from_ISpTTSEngine(iface); + size_t out_iter_len; DWORD actions; - char *buf; int i; HRESULT hr; @@ -282,19 +321,18 @@ static HRESULT WINAPI test_engine_Speak(ISpTTSEngine *iface, DWORD flags, REFGUI actions = ISpTTSEngineSite_GetActions(site); ok(actions == SPVES_CONTINUE, "got %#lx.\n", actions); - if (!engine->simulate_output) + if (!engine->output_len) return S_OK; - buf = calloc(1, 22050 * 2 / 5); - for (i = 0; i < 5; i++) + out_iter_len = engine->output_len / num_out_iters; + for (i = 0; i < num_out_iters; i++) { if (ISpTTSEngineSite_GetActions(site) & SPVES_ABORT) break; - hr = ISpTTSEngineSite_Write(site, buf, 22050 * 2 / 5, NULL); + hr = ISpTTSEngineSite_Write(site, engine->output_data + i * out_iter_len, out_iter_len, NULL); ok(hr == S_OK || hr == SP_AUDIO_STOPPED, "got %#lx.\n", hr); - Sleep(100); + Sleep(20); } - free(buf); return S_OK; } @@ -307,10 +345,10 @@ static HRESULT WINAPI test_engine_GetOutputFormat(ISpTTSEngine *iface, const GUI *out_wfx = CoTaskMemAlloc(sizeof(WAVEFORMATEX)); (*out_wfx)->wFormatTag = WAVE_FORMAT_PCM; (*out_wfx)->nChannels = 1; - (*out_wfx)->nSamplesPerSec = 22050; + (*out_wfx)->nSamplesPerSec = test_engine_sample_rate; (*out_wfx)->wBitsPerSample = 16; (*out_wfx)->nBlockAlign = 2; - (*out_wfx)->nAvgBytesPerSec = 22050 * 2; + (*out_wfx)->nAvgBytesPerSec = test_engine_sample_rate * 2; (*out_wfx)->cbSize = 0; return S_OK; @@ -458,6 +496,12 @@ static void test_spvoice(void) USHORT volume; ULONG stream_num; DWORD regid; + WAVEFORMATEX wfx; + ISpStream *spstream; + IStream *mem_stream; + char *wave_data = NULL; + size_t wave_len = 0; + STATSTG statstg; DWORD start, duration; ISpeechVoice *speech_voice; ISpeechObjectTokens *speech_tokens; @@ -470,6 +514,7 @@ static void test_spvoice(void) DISPID dispid; DISPPARAMS params; VARIANT args[2], ret; + int i; HRESULT hr; if (waveOutGetNumDevs() == 0) { @@ -656,8 +701,12 @@ static void test_spvoice(void) ISpVoice_SetRate(voice, 0); ISpVoice_SetVolume(voice, 100); + wave_data = make_sin_data(0, 1000, test_engine_sample_rate, &wave_len); + reset_engine_params(&test_engine); - test_engine.simulate_output = TRUE; + test_engine.output_data = wave_data; + test_engine.output_len = wave_len; + stream_num = 0xdeadbeef; start = GetTickCount(); hr = ISpVoice_Speak(voice, test_text, SPF_DEFAULT, &stream_num); @@ -684,7 +733,9 @@ static void test_spvoice(void) ok(duration < 200, "took %lu ms.\n", duration); reset_engine_params(&test_engine); - test_engine.simulate_output = TRUE; + test_engine.output_data = wave_data; + test_engine.output_len = wave_len; + stream_num = 0xdeadbeef; start = GetTickCount(); hr = ISpVoice_Speak(voice, test_text, SPF_DEFAULT | SPF_ASYNC | SPF_NLP_SPEAK_PUNC, &stream_num); @@ -710,7 +761,9 @@ static void test_spvoice(void) ok(test_engine.volume == 100, "got %d.\n", test_engine.volume); reset_engine_params(&test_engine); - test_engine.simulate_output = TRUE; + test_engine.output_data = wave_data; + test_engine.output_len = wave_len; + hr = ISpVoice_Speak(voice, test_text, SPF_DEFAULT | SPF_ASYNC, NULL); ok(hr == S_OK, "got %#lx.\n", hr); @@ -721,6 +774,73 @@ static void test_spvoice(void) ok(hr == S_OK, "got %#lx.\n", hr); ok(duration < 300, "took %lu ms.\n", duration); + free(wave_data); + wave_data = NULL; + + /* Test ISPVoice resampler */ + hr = CoCreateInstance(&CLSID_SpStream, NULL, CLSCTX_INPROC_SERVER, + &IID_ISpStream, (void **)&spstream); + ok(hr == S_OK, "Failed to create SpStream: %#lx.\n", hr); + + hr = CreateStreamOnHGlobal(NULL, TRUE, &mem_stream); + ok(hr == S_OK, "Failed to create memory stream: %#lx.\n", hr); + + wfx.wFormatTag = WAVE_FORMAT_PCM; + wfx.nChannels = 1; + wfx.nSamplesPerSec = 16000; + wfx.nAvgBytesPerSec = 16000 * 2; + wfx.nBlockAlign = 2; + wfx.wBitsPerSample = 16; + wfx.cbSize = 0; + + hr = ISpStream_SetBaseStream(spstream, mem_stream, &SPDFID_WaveFormatEx, &wfx); + ok(hr == S_OK, "got %#lx.\n", hr); + + hr = ISpVoice_SetOutput(voice, (IUnknown *)spstream, TRUE); + ok(hr == S_OK, "got %#lx.\n", hr); + + wave_data = make_sin_data(50, 200, test_engine_sample_rate, &wave_len); + reset_engine_params(&test_engine); + test_engine.output_data = wave_data; + test_engine.output_len = wave_len; + + hr = ISpVoice_Speak(voice, test_text, SPF_DEFAULT, NULL); + todo_wine ok(hr == S_OK, "got %#lx.\n", hr); + todo_wine ok(test_engine.speak_called, "ISpTTSEngine::Speak was not called.\n"); + + hr = ISpVoice_SetOutput(voice, NULL, TRUE); + ok(hr == S_OK, "got %#lx.\n", hr); + + free(wave_data); + wave_data = make_sin_data(50, 200, 16000, &wave_len); + + hr = IStream_Stat(mem_stream, &statstg, STATFLAG_DEFAULT); + ok(hr == S_OK, "got %#lx.\n", hr); + todo_wine ok(fabs((double)statstg.cbSize.QuadPart / wave_len - 1) < 0.02, + "got %I64u, expected %Iu (+/-2%%).\n", statstg.cbSize.QuadPart, wave_len); + + if (statstg.cbSize.QuadPart > 0) { + size_t check_len = min((size_t)statstg.cbSize.QuadPart, wave_len) / sizeof(int16_t); + unsigned int max_diff = 0; + const void *mem_data; + HGLOBAL mem_global; + + hr = GetHGlobalFromStream(mem_stream, &mem_global); + ok(hr == S_OK, "got %#lx.\n", hr); + + mem_data = GlobalLock(mem_global); + for (i = 0; i < check_len; i++) { + int out = ((int16_t *)mem_data)[i], exp = ((int16_t *)wave_data)[i]; + max_diff = max(max_diff, abs(out - exp)); + } + GlobalUnlock(mem_global); + + ok(max_diff < 32768 * 0.02, "got max_diff %u.\n", max_diff); + } + + ISpStream_Release(spstream); + IStream_Release(mem_stream); + hr = ISpVoice_QueryInterface(voice, &IID_ISpeechVoice, (void **)&speech_voice); ok(hr == S_OK, "got %#lx.\n", hr); @@ -823,6 +943,7 @@ static void test_spvoice(void) ISpVoice_Release(voice); ISpObjectToken_Release(token); ISpMMSysAudio_Release(audio_out); + free(wave_data); SysFreeString(req); SysFreeString(opt); } From 6a6b7e2774b14981e033cc9f055ec9e7c2c844db Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Mon, 14 Jul 2025 20:47:31 -0400 Subject: [PATCH 095/454] sapi/tts: Implement TTS engine audio output resampler. This implementation uses the audio resampler DSP from Media Foundation. (cherry picked from commit 56087d7b762fa89c436263e3e914de910399f898) CW-Bug-Id: #22894 --- dlls/sapi/Makefile.in | 4 +- dlls/sapi/tests/tts.c | 8 +- dlls/sapi/tts.c | 183 +++++++++++++++++++++++++++++++++++++++--- 3 files changed, 178 insertions(+), 17 deletions(-) diff --git a/dlls/sapi/Makefile.in b/dlls/sapi/Makefile.in index da91eca5b8ce..8286ff8adc05 100644 --- a/dlls/sapi/Makefile.in +++ b/dlls/sapi/Makefile.in @@ -1,6 +1,6 @@ MODULE = sapi.dll -IMPORTS = uuid ole32 oleaut32 user32 advapi32 -DELAYIMPORTS = winmm +IMPORTS = uuid ole32 oleaut32 user32 advapi32 mfuuid wmcodecdspuuid +DELAYIMPORTS = winmm mfplat SOURCES = \ async.c \ diff --git a/dlls/sapi/tests/tts.c b/dlls/sapi/tests/tts.c index 4d3f32dc36b0..c89bffae5a09 100644 --- a/dlls/sapi/tests/tts.c +++ b/dlls/sapi/tests/tts.c @@ -805,8 +805,8 @@ static void test_spvoice(void) test_engine.output_len = wave_len; hr = ISpVoice_Speak(voice, test_text, SPF_DEFAULT, NULL); - todo_wine ok(hr == S_OK, "got %#lx.\n", hr); - todo_wine ok(test_engine.speak_called, "ISpTTSEngine::Speak was not called.\n"); + ok(hr == S_OK, "got %#lx.\n", hr); + ok(test_engine.speak_called, "ISpTTSEngine::Speak was not called.\n"); hr = ISpVoice_SetOutput(voice, NULL, TRUE); ok(hr == S_OK, "got %#lx.\n", hr); @@ -816,8 +816,8 @@ static void test_spvoice(void) hr = IStream_Stat(mem_stream, &statstg, STATFLAG_DEFAULT); ok(hr == S_OK, "got %#lx.\n", hr); - todo_wine ok(fabs((double)statstg.cbSize.QuadPart / wave_len - 1) < 0.02, - "got %I64u, expected %Iu (+/-2%%).\n", statstg.cbSize.QuadPart, wave_len); + ok(fabs((double)statstg.cbSize.QuadPart / wave_len - 1) < 0.02, + "got %I64u, expected %Iu.\n", statstg.cbSize.QuadPart, wave_len); if (statstg.cbSize.QuadPart > 0) { size_t check_len = min((size_t)statstg.cbSize.QuadPart, wave_len) / sizeof(int16_t); diff --git a/dlls/sapi/tts.c b/dlls/sapi/tts.c index baf7a74789db..54537d927be1 100644 --- a/dlls/sapi/tts.c +++ b/dlls/sapi/tts.c @@ -26,6 +26,11 @@ #include "winbase.h" #include "objbase.h" +#include "mfapi.h" +#include "mferror.h" +#include "mftransform.h" +#include "wmcodecdsp.h" + #include "sapiddk.h" #include "sperror.h" @@ -43,6 +48,7 @@ struct speech_voice LONG ref; ISpStreamFormat *output; + IMFTransform *resampler; ISpObjectToken *engine_token; ISpTTSEngine *engine; LONG cur_stream_num; @@ -76,6 +82,9 @@ struct tts_engine_site struct speech_voice *voice; ULONG stream_num; + BOOL use_resampler; + IMFSample *out_sample; + IMFMediaBuffer *out_buf; }; static inline struct tts_engine_site *impl_from_ISpTTSEngineSite(ISpTTSEngineSite *iface) @@ -83,6 +92,23 @@ static inline struct tts_engine_site *impl_from_ISpTTSEngineSite(ISpTTSEngineSit return CONTAINING_RECORD(iface, struct tts_engine_site, ISpTTSEngineSite_iface); } +static const char *debugstr_wfx(const WAVEFORMATEX *wfx) +{ + if (!wfx) return "(null)"; + if (wfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE) + { + const WAVEFORMATEXTENSIBLE *wfxe = (const WAVEFORMATEXTENSIBLE *)wfx; + + return wine_dbg_sprintf( + "tag: %#x (%s), ch: %u (mask: %#lx), rate: %lu, avgbps: %lu, align: %u, depth: %u", + wfx->wFormatTag, debugstr_guid(&wfxe->SubFormat), wfx->nChannels, wfxe->dwChannelMask, + wfx->nSamplesPerSec, wfx->nAvgBytesPerSec, wfx->nBlockAlign, wfx->wBitsPerSample); + } + return wine_dbg_sprintf("tag: %#x, ch: %u, rate: %lu, avgbps: %lu, align: %u, depth: %u", + wfx->wFormatTag, wfx->nChannels, wfx->nSamplesPerSec, + wfx->nAvgBytesPerSec, wfx->nBlockAlign, wfx->wBitsPerSample); +} + static HRESULT create_token_category(const WCHAR *cat_id, ISpObjectTokenCategory **cat) { HRESULT hr; @@ -170,6 +196,7 @@ static ULONG WINAPI speech_voice_Release(ISpeechVoice *iface) { async_cancel_queue(&This->queue); if (This->output) ISpStreamFormat_Release(This->output); + if (This->resampler) IMFTransform_Release(This->resampler); if (This->engine_token) ISpObjectToken_Release(This->engine_token); if (This->engine) ISpTTSEngine_Release(This->engine); DeleteCriticalSection(&This->cs); @@ -824,16 +851,68 @@ static void free_frag_list(SPVTEXTFRAG *frag) } } -static HRESULT set_output_format(ISpStreamFormat *output, ISpTTSEngine *engine, GUID *fmtid, WAVEFORMATEX **wfx) +static HRESULT setup_resampler(struct speech_voice *voice, const WAVEFORMATEX *in_wfx, + const WAVEFORMATEX *out_wfx) +{ + IMFMediaType *cur_in_type = NULL, *cur_out_type = NULL; + IMFMediaType *in_type = NULL, *out_type = NULL; + DWORD flags; + HRESULT hr; + + TRACE("Resampling TTS engine output\n"); + TRACE(" in_wfx: %s\n", debugstr_wfx(in_wfx)); + TRACE("to\n"); + TRACE(" out_wfx: %s\n", debugstr_wfx(out_wfx)); + + if (!voice->resampler && + FAILED(hr = CoCreateInstance(&CLSID_CResamplerMediaObject, NULL, CLSCTX_INPROC_SERVER, + &IID_IMFTransform, (void **)&voice->resampler))) + { + ERR("Failed to create CResamplerMediaObject: %#lx.\n", hr); + return hr; + } + + if (FAILED(hr = MFCreateMediaType(&in_type)) || + FAILED(hr = MFInitMediaTypeFromWaveFormatEx(in_type, in_wfx, sizeof(WAVEFORMATEX) + in_wfx->cbSize))) + goto done; + + if (FAILED(hr = MFCreateMediaType(&out_type)) || + FAILED(hr = MFInitMediaTypeFromWaveFormatEx(out_type, out_wfx, sizeof(WAVEFORMATEX) + out_wfx->cbSize))) + goto done; + + if (FAILED(IMFTransform_GetInputCurrentType(voice->resampler, 0, &cur_in_type)) || + IMFMediaType_IsEqual(cur_in_type, in_type, &flags) != S_OK) + { + if (FAILED(hr = IMFTransform_SetInputType(voice->resampler, 0, in_type, 0))) + goto done; + } + + if (FAILED(IMFTransform_GetOutputCurrentType(voice->resampler, 0, &cur_out_type)) || + IMFMediaType_IsEqual(cur_out_type, out_type, &flags) != S_OK) + { + if (FAILED(hr = IMFTransform_SetOutputType(voice->resampler, 0, out_type, 0))) + goto done; + } + +done: + if (in_type) IMFMediaType_Release(in_type); + if (out_type) IMFMediaType_Release(out_type); + if (cur_in_type) IMFMediaType_Release(cur_in_type); + if (cur_out_type) IMFMediaType_Release(cur_out_type); + return hr; +} + +static HRESULT set_output_format(struct speech_voice *voice, GUID *fmtid, WAVEFORMATEX **engine_wfx, + BOOL *use_resampler) { GUID output_fmtid; WAVEFORMATEX *output_wfx = NULL; ISpAudio *audio = NULL; HRESULT hr; - if (FAILED(hr = ISpStreamFormat_GetFormat(output, &output_fmtid, &output_wfx))) + if (FAILED(hr = ISpStreamFormat_GetFormat(voice->output, &output_fmtid, &output_wfx))) return hr; - if (FAILED(hr = ISpTTSEngine_GetOutputFormat(engine, &output_fmtid, output_wfx, fmtid, wfx))) + if (FAILED(hr = ISpTTSEngine_GetOutputFormat(voice->engine, &output_fmtid, output_wfx, fmtid, engine_wfx))) goto done; if (!IsEqualGUID(fmtid, &SPDFID_WaveFormatEx)) { @@ -841,12 +920,22 @@ static HRESULT set_output_format(ISpStreamFormat *output, ISpTTSEngine *engine, goto done; } - if (memcmp(output_wfx, *wfx, sizeof(WAVEFORMATEX)) || - memcmp(output_wfx + 1, *wfx + 1, output_wfx->cbSize)) + *use_resampler = FALSE; + + if (memcmp(output_wfx, *engine_wfx, sizeof(WAVEFORMATEX)) || + memcmp(output_wfx + 1, *engine_wfx + 1, output_wfx->cbSize)) { - if (FAILED(hr = ISpStreamFormat_QueryInterface(output, &IID_ISpAudio, (void **)&audio)) || - FAILED(hr = ISpAudio_SetFormat(audio, &SPDFID_WaveFormatEx, *wfx))) - goto done; + if (SUCCEEDED(ISpStreamFormat_QueryInterface(voice->output, &IID_ISpAudio, (void **)&audio))) + { + hr = ISpAudio_SetFormat(audio, &SPDFID_WaveFormatEx, *engine_wfx); + if (hr == SPERR_UNSUPPORTED_FORMAT) + *use_resampler = TRUE; + } + else + *use_resampler = TRUE; + + if (*use_resampler) + hr = setup_resampler(voice, *engine_wfx, output_wfx); } done: @@ -859,6 +948,7 @@ static void speak_proc(struct async_task *task) { struct speak_task *speak_task = (struct speak_task *)task; struct speech_voice *This = speak_task->voice; + struct tts_engine_site *site = impl_from_ISpTTSEngineSite(speak_task->site); GUID fmtid; WAVEFORMATEX *wfx = NULL; ISpAudio *audio = NULL; @@ -875,7 +965,7 @@ static void speak_proc(struct async_task *task) goto done; } - if (FAILED(hr = set_output_format(This->output, This->engine, &fmtid, &wfx))) + if (FAILED(hr = set_output_format(This, &fmtid, &wfx, &site->use_resampler))) { LeaveCriticalSection(&This->cs); ERR("failed setting output format: %#lx.\n", hr); @@ -1356,8 +1446,9 @@ static ULONG WINAPI ttsenginesite_Release(ISpTTSEngineSite *iface) if (!ref) { - if (This->voice) - ISpeechVoice_Release(&This->voice->ISpeechVoice_iface); + if (This->voice) ISpeechVoice_Release(&This->voice->ISpeechVoice_iface); + if (This->out_sample) IMFSample_Release(This->out_sample); + if (This->out_buf) IMFMediaBuffer_Release(This->out_buf); free(This); } @@ -1392,6 +1483,69 @@ static DWORD WINAPI ttsenginesite_GetActions(ISpTTSEngineSite *iface) return actions; } +static HRESULT resample_engine_output(struct tts_engine_site *This, const void *buf, ULONG cb, ULONG *cb_written) +{ + MFT_OUTPUT_DATA_BUFFER mft_buf; + IMFMediaBuffer *in_buf = NULL; + IMFSample *in_sample = NULL; + BYTE *in_data, *out_data; + DWORD out_len; + DWORD status; + HRESULT hr; + + if (FAILED(hr = MFCreateSample(&in_sample)) || + FAILED(hr = MFCreateMemoryBuffer(cb, &in_buf)) || + FAILED(hr = IMFSample_AddBuffer(in_sample, in_buf))) + goto done; + + if (!This->out_sample) + { + if (FAILED(hr = MFCreateSample(&This->out_sample)) || + FAILED(hr = MFCreateMemoryBuffer(16384, &This->out_buf)) || + FAILED(hr = IMFSample_AddBuffer(This->out_sample, This->out_buf))) + goto done; + } + + if (FAILED(hr = IMFMediaBuffer_Lock(in_buf, &in_data, NULL, NULL))) + goto done; + memcpy(in_data, buf, cb); + IMFMediaBuffer_Unlock(in_buf); + + IMFMediaBuffer_SetCurrentLength(in_buf, cb); + + if (FAILED(hr = IMFTransform_ProcessInput(This->voice->resampler, 0, in_sample, 0))) + goto done; + + while (SUCCEEDED(hr)) + { + memset(&mft_buf, 0, sizeof(mft_buf)); + mft_buf.pSample = This->out_sample; + + if (FAILED(hr = IMFTransform_ProcessOutput(This->voice->resampler, 0, 1, &mft_buf, &status))) + { + if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) + hr = S_OK; + break; + } + + if (FAILED(hr = IMFMediaBuffer_GetCurrentLength(This->out_buf, &out_len)) || + FAILED(hr = IMFMediaBuffer_Lock(This->out_buf, &out_data, NULL, NULL))) + break; + + hr = ISpStreamFormat_Write(This->voice->output, out_data, out_len, NULL); + IMFMediaBuffer_Unlock(This->out_buf); + } + +done: + if (in_sample) IMFSample_Release(in_sample); + if (in_buf) IMFMediaBuffer_Release(in_buf); + + if (cb_written) + *cb_written = SUCCEEDED(hr) ? cb : 0; + + return hr; +} + static HRESULT WINAPI ttsenginesite_Write(ISpTTSEngineSite *iface, const void *buf, ULONG cb, ULONG *cb_written) { struct tts_engine_site *This = impl_from_ISpTTSEngineSite(iface); @@ -1401,6 +1555,9 @@ static HRESULT WINAPI ttsenginesite_Write(ISpTTSEngineSite *iface, const void *b if (!This->voice->output) return SPERR_UNINITIALIZED; + if (This->use_resampler) + return resample_engine_output(This, buf, cb, cb_written); + return ISpStreamFormat_Write(This->voice->output, buf, cb, cb_written); } @@ -1472,6 +1629,9 @@ static HRESULT ttsenginesite_create(struct speech_voice *voice, ULONG stream_num This->ref = 1; This->voice = voice; This->stream_num = stream_num; + This->use_resampler = FALSE; + This->out_sample = NULL; + This->out_buf = NULL; ISpeechVoice_AddRef(&This->voice->ISpeechVoice_iface); @@ -1545,6 +1705,7 @@ HRESULT speech_voice_create(IUnknown *outer, REFIID iid, void **obj) This->ref = 1; This->output = NULL; + This->resampler = NULL; This->engine_token = NULL; This->engine = NULL; This->cur_stream_num = 0; From e6c26f4454d8091ffc5edb4ca7fc330318876568 Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Sat, 5 Jul 2025 13:37:02 -0400 Subject: [PATCH 096/454] sapi/tts: Support allow_format_changes in ISpVoice::SetOutput. (cherry picked from commit da13c9b44ecb9de672fe6ec125dca18a38fe5135) CW-Bug-Id: #22894 --- dlls/sapi/tts.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/dlls/sapi/tts.c b/dlls/sapi/tts.c index 54537d927be1..159635a45595 100644 --- a/dlls/sapi/tts.c +++ b/dlls/sapi/tts.c @@ -49,6 +49,7 @@ struct speech_voice ISpStreamFormat *output; IMFTransform *resampler; + BOOL allow_format_changes; ISpObjectToken *engine_token; ISpTTSEngine *engine; LONG cur_stream_num; @@ -683,9 +684,6 @@ static HRESULT WINAPI spvoice_SetOutput(ISpVoice *iface, IUnknown *unk, BOOL all TRACE("(%p, %p, %d).\n", iface, unk, allow_format_changes); - if (!allow_format_changes) - FIXME("ignoring allow_format_changes = FALSE.\n"); - if (FAILED(hr = async_start_queue(&This->queue))) return hr; @@ -719,6 +717,8 @@ static HRESULT WINAPI spvoice_SetOutput(ISpVoice *iface, IUnknown *unk, BOOL all ISpStreamFormat_Release(This->output); This->output = stream; + This->allow_format_changes = allow_format_changes; + LeaveCriticalSection(&This->cs); return S_OK; @@ -925,7 +925,8 @@ static HRESULT set_output_format(struct speech_voice *voice, GUID *fmtid, WAVEFO if (memcmp(output_wfx, *engine_wfx, sizeof(WAVEFORMATEX)) || memcmp(output_wfx + 1, *engine_wfx + 1, output_wfx->cbSize)) { - if (SUCCEEDED(ISpStreamFormat_QueryInterface(voice->output, &IID_ISpAudio, (void **)&audio))) + if (voice->allow_format_changes && + SUCCEEDED(ISpStreamFormat_QueryInterface(voice->output, &IID_ISpAudio, (void **)&audio))) { hr = ISpAudio_SetFormat(audio, &SPDFID_WaveFormatEx, *engine_wfx); if (hr == SPERR_UNSUPPORTED_FORMAT) @@ -1706,6 +1707,7 @@ HRESULT speech_voice_create(IUnknown *outer, REFIID iid, void **obj) This->output = NULL; This->resampler = NULL; + This->allow_format_changes = TRUE; This->engine_token = NULL; This->engine = NULL; This->cur_stream_num = 0; From c97fc66d3ef30f3d2dbfa061bc25b72ec01f7e4d Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Wed, 16 Jul 2025 21:59:07 -0400 Subject: [PATCH 097/454] protontts: Implement rate support. CW-Bug-Id: #22894 --- dlls/protontts/tts.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/dlls/protontts/tts.c b/dlls/protontts/tts.c index b9036a88aab5..39d7a270b2a9 100644 --- a/dlls/protontts/tts.c +++ b/dlls/protontts/tts.c @@ -18,6 +18,7 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ +#include #include #define COBJMACROS @@ -64,6 +65,13 @@ static inline struct ttsengine *impl_from_ISpObjectWithToken(ISpObjectWithToken return CONTAINING_RECORD(iface, struct ttsengine, ISpObjectWithToken_iface); } +static inline long lclamp(long value, long value_min, long value_max) +{ + if (value < value_min) return value_min; + if (value > value_max) return value_max; + return value; +} + static BOOL WINAPI init_tts(INIT_ONCE *once, void *param, void **ctx) { WINE_UNIX_CALL(unix_tts_create, &tts); @@ -195,9 +203,11 @@ static HRESULT WINAPI ttsengine_Speak(ISpTTSEngine *iface, DWORD flags, REFGUID { struct ttsengine *This = impl_from_ISpTTSEngine(iface); USHORT global_volume = 100; + LONG global_rate = 0; HANDLE abort_event; HANDLE thread = NULL; char *text = NULL; + DWORD actions; HRESULT hr = S_OK; TRACE("(%p, %#lx, %s, %p, %p, %p).\n", iface, flags, debugstr_guid(fmtid), wfx, frag_list, site); @@ -208,14 +218,17 @@ static HRESULT WINAPI ttsengine_Speak(ISpTTSEngine *iface, DWORD flags, REFGUID if (!(abort_event = CreateEventW(NULL, FALSE, FALSE, NULL))) return HRESULT_FROM_WIN32(GetLastError()); - tts_voice_set_length_scale(This->voice, This->base_length_scale); for (; frag_list; frag_list = frag_list->pNext) { struct tts_voice_synthesize_params params; + long rate; bool done; - if (ISpTTSEngineSite_GetActions(site) & SPVES_ABORT) + actions = ISpTTSEngineSite_GetActions(site); + if (actions & SPVES_ABORT) return S_OK; + if (actions & SPVES_RATE) + ISpTTSEngineSite_GetRate(site, &global_rate); params.size = WideCharToMultiByte(CP_UTF8, 0, frag_list->pTextStart, frag_list->ulTextLen, NULL, 0, NULL, NULL) + 1; if (!(text = malloc(params.size))) @@ -226,6 +239,9 @@ static HRESULT WINAPI ttsengine_Speak(ISpTTSEngine *iface, DWORD flags, REFGUID WideCharToMultiByte(CP_UTF8, 0, frag_list->pTextStart, frag_list->ulTextLen, text, params.size, NULL, NULL); text[params.size - 1] = '\0'; + rate = lclamp(global_rate + frag_list->State.RateAdj, -10, 10); + tts_voice_set_length_scale(This->voice, This->base_length_scale * powf(3, rate * -0.1f)); + params.voice = This->voice; params.text = text; params.abort_event = abort_event; @@ -238,7 +254,6 @@ static HRESULT WINAPI ttsengine_Speak(ISpTTSEngine *iface, DWORD flags, REFGUID for (done = false; !done;) { - DWORD actions; void *buf; UINT32 size; From 7e1706502552cde76a4eac242feb8a2a5375b03a Mon Sep 17 00:00:00 2001 From: Shaun Ren Date: Thu, 17 Jul 2025 23:14:08 -0400 Subject: [PATCH 098/454] protontts: Check for available data more frequently. --- dlls/protontts/tts.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/protontts/tts.c b/dlls/protontts/tts.c index 39d7a270b2a9..06762939e86c 100644 --- a/dlls/protontts/tts.c +++ b/dlls/protontts/tts.c @@ -257,7 +257,7 @@ static HRESULT WINAPI ttsengine_Speak(ISpTTSEngine *iface, DWORD flags, REFGUID void *buf; UINT32 size; - Sleep(50); + Sleep(10); actions = ISpTTSEngineSite_GetActions(site); if (actions & SPVES_ABORT) From 264abf7a378617603ee182f6249cc321db463502 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 15 Sep 2025 18:34:18 +0200 Subject: [PATCH 099/454] HACK: win32u: Move AdjustWindowRect hack to only apply to syscall. Similar to how it was done in Proton 9, we only hacked the version that was in user32. CW-Bug-Id: #25938 --- dlls/win32u/defwnd.c | 13 ------------- dlls/win32u/sysparams.c | 14 ++++++++++++++ dlls/win32u/window.c | 2 +- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/dlls/win32u/defwnd.c b/dlls/win32u/defwnd.c index 6174523ea589..67f371ac9fe4 100644 --- a/dlls/win32u/defwnd.c +++ b/dlls/win32u/defwnd.c @@ -258,19 +258,6 @@ BOOL adjust_window_rect( RECT *rect, DWORD style, BOOL menu, DWORD ex_style, UIN NONCLIENTMETRICSW ncm = {.cbSize = sizeof(ncm)}; int adjust = 0; - if (user_driver->pHasWindowManager( "steamcompmgr" )) - { - /* Disable gamescope undecorated windows hack for following games. They don't expect client - * rect equals to window rect when in windowed mode. */ - const char *sgi = getenv( "SteamGameId" ); - if ( - !((style & WS_POPUP) && (ex_style & WS_EX_TOOLWINDOW)) /* Bug 20038: game splash screens */ - && !(sgi && !strcmp( sgi, "2563800" )) /* Bug 23342: The Last Game */ - && !(sgi && !strcmp( sgi, "1240440" )) /* Bug 23802: Halo Infinite */ - ) - return TRUE; - } - NtUserSystemParametersInfoForDpi( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0, dpi ); if ((ex_style & (WS_EX_STATICEDGE|WS_EX_DLGMODALFRAME)) == WS_EX_STATICEDGE) diff --git a/dlls/win32u/sysparams.c b/dlls/win32u/sysparams.c index b53dbb17f18b..78a7d3c4a798 100644 --- a/dlls/win32u/sysparams.c +++ b/dlls/win32u/sysparams.c @@ -7347,6 +7347,20 @@ ULONG_PTR WINAPI NtUserCallTwoParam( ULONG_PTR arg1, ULONG_PTR arg2, ULONG code case NtUserCallTwoParam_AdjustWindowRect: { struct adjust_window_rect_params *params = (void *)arg2; + + if (user_driver->pHasWindowManager( "steamcompmgr" )) + { + /* Disable gamescope undecorated windows hack for following games. They don't expect client + * rect equals to window rect when in windowed mode. */ + const char *sgi = getenv( "SteamGameId" ); + if ( + !((params->style & WS_POPUP) && (params->ex_style & WS_EX_TOOLWINDOW)) /* Bug 20038: game splash screens */ + && !(sgi && !strcmp( sgi, "2563800" )) /* Bug 23342: The Last Game */ + && !(sgi && !strcmp( sgi, "1240440" )) /* Bug 23802: Halo Infinite */ + ) + return TRUE; + } + return adjust_window_rect( (RECT *)arg1, params->style, params->menu, params->ex_style, params->dpi ); } diff --git a/dlls/win32u/window.c b/dlls/win32u/window.c index 16291036001f..631606afc312 100644 --- a/dlls/win32u/window.c +++ b/dlls/win32u/window.c @@ -1907,7 +1907,7 @@ static RECT get_visible_rect( HWND hwnd, BOOL shaped, UINT style, UINT ex_style, if (IsRectEmpty( &rects->window ) || EqualRect( &rects->window, &rects->client ) || shaped || !decorated_mode) return rects->window; if (!user_driver->pGetWindowStyleMasks( hwnd, style, ex_style, &style_mask, &ex_style_mask )) return rects->window; - if (!NtUserAdjustWindowRect( &rect, style & style_mask, FALSE, ex_style & ex_style_mask, dpi )) return rects->window; + if (!adjust_window_rect( &rect, style & style_mask, FALSE, ex_style & ex_style_mask, dpi )) return rects->window; visible_rect = rects->window; visible_rect.left -= rect.left; From 451aace69a7ae7cc876eac0bf489284415b928eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 15 Sep 2025 18:29:46 +0200 Subject: [PATCH 100/454] winex11: Try harder to get offscreen blit destination rectangle. When toplevel is in another process we won't have window data. CW-Bug-Id: #25938 --- dlls/winex11.drv/opengl.c | 12 +++++++++--- dlls/winex11.drv/vulkan.c | 15 +++++++++++---- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/dlls/winex11.drv/opengl.c b/dlls/winex11.drv/opengl.c index 6e2e1e5e8c1c..c6b3e9fe6223 100644 --- a/dlls/winex11.drv/opengl.c +++ b/dlls/winex11.drv/opengl.c @@ -3301,15 +3301,21 @@ static void present_gl_drawable( HWND hwnd, HDC hdc, struct gl_drawable *gl, BOO NtUserMapWindowPoints( hwnd, toplevel, (POINT *)&rect_dst, 2, dpi ); if (IsRectEmpty( &rect_dst ) || IsRectEmpty( &gl->rect )) return; rect_dst = map_rect_virt_to_raw_for_monitor( NtUserMonitorFromWindow( toplevel, MONITOR_DEFAULTTONEAREST ), rect_dst, dpi ); + + window = get_dc_drawable( hdc, &rect ); + region = get_dc_monitor_region( hwnd, hdc ); + if ((data = get_win_data( toplevel ))) { OffsetRect( &rect_dst, data->rects.client.left - data->rects.visible.left, data->rects.client.top - data->rects.visible.top ); release_win_data( data ); } - - window = get_dc_drawable( hdc, &rect ); - region = get_dc_monitor_region( hwnd, hdc ); + else + { + OffsetRect( &rect_dst, rect.left, rect.top ); + WARN( "Using rect %s for other process window\n", wine_dbgstr_rect( &rect_dst ) ); + } if (get_dc_drawable( gl->hdc_dst, &rect ) != window || !EqualRect( &rect, &rect_dst )) set_dc_drawable( gl->hdc_dst, window, &rect_dst, IncludeInferiors ); diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c index 6e7f47cb3490..fbc9f933f436 100644 --- a/dlls/winex11.drv/vulkan.c +++ b/dlls/winex11.drv/vulkan.c @@ -341,16 +341,23 @@ static void X11DRV_vulkan_surface_presented( HWND hwnd, void *private, VkResult NtUserMapWindowPoints( hwnd, toplevel, (POINT *)&rect_dst, 2, dpi ); if (IsRectEmpty( &rect_dst ) || IsRectEmpty( &surface->rect )) return; rect_dst = map_rect_virt_to_raw_for_monitor( NtUserMonitorFromWindow( toplevel, MONITOR_DEFAULTTONEAREST ), rect_dst, dpi ); + + if (!(hdc = NtUserGetDCEx( hwnd, 0, DCX_CACHE | DCX_USESTYLE ))) return; + window = X11DRV_get_whole_window( toplevel ); + region = get_dc_monitor_region( hwnd, hdc ); + if ((data = get_win_data( toplevel ))) { OffsetRect( &rect_dst, data->rects.client.left - data->rects.visible.left, data->rects.client.top - data->rects.visible.top ); release_win_data( data ); } - - if (!(hdc = NtUserGetDCEx( hwnd, 0, DCX_CACHE | DCX_USESTYLE ))) return; - window = X11DRV_get_whole_window( toplevel ); - region = get_dc_monitor_region( hwnd, hdc ); + else + { + get_dc_drawable( hdc, &rect ); + OffsetRect( &rect_dst, rect.left, rect.top ); + WARN( "Using rect %s for other process window\n", wine_dbgstr_rect( &rect_dst ) ); + } if (get_dc_drawable( surface->hdc_dst, &rect ) != window || !EqualRect( &rect, &rect_dst )) set_dc_drawable( surface->hdc_dst, window, &rect_dst, IncludeInferiors ); From 0be68e3584f0ef59e3dddc4384773984b5058e33 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 7 Apr 2025 19:47:10 -0600 Subject: [PATCH 101/454] winex11.drv: HACK: Only offscreen client window in X11DRV_vulkan_surface_presented() on Gamescope. CW-Bug-Id: #25154 --- dlls/winex11.drv/vulkan.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c index fbc9f933f436..1eeaf4a7a18e 100644 --- a/dlls/winex11.drv/vulkan.c +++ b/dlls/winex11.drv/vulkan.c @@ -288,7 +288,8 @@ static void X11DRV_vulkan_surface_update( HWND hwnd, void *private ) TRACE( "%p %p\n", hwnd, private ); vulkan_surface_update_size( hwnd, surface ); - vulkan_surface_update_offscreen( hwnd, surface ); + if (surface->offscreen || !X11DRV_HasWindowManager( "steamcompmgr" )) + vulkan_surface_update_offscreen( hwnd, surface ); } static int force_present_to_surface(void) @@ -310,6 +311,7 @@ static int force_present_to_surface(void) static void X11DRV_vulkan_surface_presented( HWND hwnd, void *private, VkResult result ) { struct x11drv_vulkan_surface *surface = private; + BOOL was_offscreen = surface->offscreen; struct window_surface *win_surface; struct x11drv_win_data *data; RECT rect_dst, rect; @@ -335,6 +337,12 @@ static void X11DRV_vulkan_surface_presented( HWND hwnd, void *private, VkResult return; } + if (!was_offscreen && X11DRV_HasWindowManager( "steamcompmgr" )) + { + NtUserPostMessage( hwnd, WM_WINE_UPDATEWINDOWSTATE, 0, 0 ); + return; + } + toplevel = NtUserGetAncestor( hwnd, GA_ROOT ); dpi = NtUserGetDpiForWindow( hwnd ); NtUserGetClientRect( hwnd, &rect_dst, dpi ); From 638ba427b96c86a629744895a21398c6f5897d4f Mon Sep 17 00:00:00 2001 From: Brendan McGrath Date: Thu, 17 Apr 2025 08:55:26 +1000 Subject: [PATCH 102/454] amend! winegstreamer: Push flush event when flushing. winegstreamer: Push flush event when flushing. This ensures the gstreamer pipeline is empty whilst we flush our queues. (cherry picked from commit 9015585da9b3374a1e7b92e747435dde11ed2001) CW-Bug-Id: #25177 From 0df288b11b6609eb0af953926e3daae46fd7ef7c Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 15 Nov 2024 15:37:09 -0600 Subject: [PATCH 103/454] mshtml, jscript: HACK: Process sent messages and WM_TIMER often enough for FFXIV launcher. CW-Bug-Id: #24435 --- dlls/jscript/compile.c | 1 + dlls/jscript/function.c | 2 ++ dlls/jscript/jscript.h | 2 ++ dlls/jscript/jsutils.c | 27 +++++++++++++++++++++++++++ dlls/mshtml/mutation.c | 29 +++++++++++++++++++++++++++++ 5 files changed, 61 insertions(+) diff --git a/dlls/jscript/compile.c b/dlls/jscript/compile.c index 2d2c2c9f40ad..fba796625714 100644 --- a/dlls/jscript/compile.c +++ b/dlls/jscript/compile.c @@ -2772,5 +2772,6 @@ HRESULT compile_script(script_ctx_t *ctx, const WCHAR *code, UINT64 source_conte } *ret = compiler.code; + hack_pump_messages(); return S_OK; } diff --git a/dlls/jscript/function.c b/dlls/jscript/function.c index cc4927ef786b..5fab24b03402 100644 --- a/dlls/jscript/function.c +++ b/dlls/jscript/function.c @@ -971,6 +971,8 @@ static HRESULT InterpretedFunction_call(script_ctx_t *ctx, FunctionInstance *fun TRACE("%p\n", function); + hack_pump_messages(); + if(flags & DISPATCH_CONSTRUCT) { hres = create_object(ctx, &function->function.dispex, &new_obj); if(FAILED(hres)) diff --git a/dlls/jscript/jscript.h b/dlls/jscript/jscript.h index 1d651dccd206..0adf1ba5eb38 100644 --- a/dlls/jscript/jscript.h +++ b/dlls/jscript/jscript.h @@ -665,3 +665,5 @@ static inline void unlock_module(void) { InterlockedDecrement(&module_ref); } + +void hack_pump_messages(void); diff --git a/dlls/jscript/jsutils.c b/dlls/jscript/jsutils.c index 1d6fa06a993f..1b437814ff18 100644 --- a/dlls/jscript/jsutils.c +++ b/dlls/jscript/jsutils.c @@ -1105,3 +1105,30 @@ HRESULT create_jscaller(script_ctx_t *ctx) ctx->jscaller = ret; return S_OK; } + +void hack_pump_messages(void) +{ + static BOOL initialized; + static HWND hwnd; + static DWORD last; + DWORD tick; + MSG msg; + + if (!initialized) + { + const char *sgi = getenv("SteamGameId"); + if (sgi && !strcmp(sgi, "39210")) hwnd = FindWindowW(NULL, L"FFXIVLauncher"); + if (hwnd) ERR("HACK: injecting PeekMessage into mshtml / jscript processing.\n"); + initialized = TRUE; + } + if (!hwnd) return; + + tick = GetTickCount(); + if (tick - last < 50) return; + last = tick; + if (PeekMessageW(&msg, hwnd, WM_TIMER, WM_TIMER, PM_REMOVE)) + { + TRACE("dispatching WM_TIMER.\n"); + DispatchMessageW(&msg); + } +} diff --git a/dlls/mshtml/mutation.c b/dlls/mshtml/mutation.c index 11e31d38968d..c25fcf0db049 100644 --- a/dlls/mshtml/mutation.c +++ b/dlls/mshtml/mutation.c @@ -891,6 +891,33 @@ static void NSAPI nsDocumentObserver_StyleRuleRemoved(nsIDocumentObserver *iface { } +static void hack_pump_messages(void) +{ + static BOOL initialized; + static HWND hwnd; + static DWORD last; + DWORD tick; + MSG msg; + + if (!initialized) + { + const char *sgi = getenv("SteamGameId"); + if (sgi && !strcmp(sgi, "39210")) hwnd = FindWindowW(NULL, L"FFXIVLauncher"); + if (hwnd) ERR("HACK: injecting PeekMessage into mshtml / jscript processing.\n"); + initialized = TRUE; + } + if (!hwnd) return; + + tick = GetTickCount(); + if (tick - last < 50) return; + last = tick; + if (PeekMessageW(&msg, hwnd, WM_TIMER, WM_TIMER, PM_REMOVE)) + { + TRACE("dispatching WM_TIMER.\n"); + DispatchMessageW(&msg); + } +} + static void NSAPI nsDocumentObserver_BindToDocument(nsIDocumentObserver *iface, nsIDocument *aDocument, nsIContent *aContent) { @@ -905,6 +932,8 @@ static void NSAPI nsDocumentObserver_BindToDocument(nsIDocumentObserver *iface, TRACE("(%p)->(%p %p)\n", This, aDocument, aContent); + hack_pump_messages(); + if(This->document_mode < COMPAT_MODE_IE10) { nsres = nsIContent_QueryInterface(aContent, &IID_nsIDOMComment, (void**)&nscomment); if(NS_SUCCEEDED(nsres)) { From 33480bc58cfa1da7e329da03e8894f0457c13765 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20K=C3=B6lbl?= Date: Mon, 13 Feb 2023 15:05:53 +0100 Subject: [PATCH 104/454] windows.media.speech: Add Vosk checks to autoconf. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Bernhard Kölbl CW-Bug-Id: #20134 --- configure.ac | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 716ba305502d..9acc7ce2845b 100644 --- a/configure.ac +++ b/configure.ac @@ -62,6 +62,7 @@ AC_ARG_WITH(udev, AS_HELP_STRING([--without-udev],[do not use udev (plug an AC_ARG_WITH(unwind, AS_HELP_STRING([--without-unwind],[do not use the libunwind library (exception handling)])) AC_ARG_WITH(usb, AS_HELP_STRING([--without-usb],[do not use the libusb library])) AC_ARG_WITH(v4l2, AS_HELP_STRING([--without-v4l2],[do not use v4l2 (video capture)])) +AC_ARG_WITH(vosk, AS_HELP_STRING([--without-vosk],[do not use Vosk])) AC_ARG_WITH(vulkan, AS_HELP_STRING([--without-vulkan],[do not use Vulkan])) AC_ARG_WITH(wayland, AS_HELP_STRING([--without-wayland],[do not build the Wayland driver])) AC_ARG_WITH(xcomposite,AS_HELP_STRING([--without-xcomposite],[do not use the Xcomposite extension]), @@ -446,7 +447,8 @@ AC_CHECK_HEADERS(\ syscall.h \ utime.h \ valgrind/memcheck.h \ - valgrind/valgrind.h + valgrind/valgrind.h \ + vosk_api.h ) AC_HEADER_MAJOR() @@ -1886,6 +1888,14 @@ then WINE_WARNING([No sound system was found. Windows applications will be silent.]) fi +dnl **** Check for Vosk **** +if test x$with_vosk != xno +then + WINE_CHECK_SONAME(vosk,vosk_recognizer_new) +fi +WINE_NOTICE_WITH(vosk,[test x$ac_cv_lib_soname_vosk = x], + [libvosk ${notice_platform}development files not found, speech recognition won't be supported.]) + dnl *** Check for Vulkan *** if test "x$with_vulkan" != "xno" then From 906e1b7c7a74e9d53703d98d73546b7539f2a1b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20K=C3=B6lbl?= Date: Mon, 13 Feb 2023 00:17:50 +0100 Subject: [PATCH 105/454] windows.media.speech: Add unixlib stub. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Bernhard Kölbl CW-Bug-Id: #20134 --- dlls/windows.media.speech/Makefile.in | 2 + dlls/windows.media.speech/main.c | 26 ++++++ dlls/windows.media.speech/private.h | 4 + dlls/windows.media.speech/unixlib.c | 125 ++++++++++++++++++++++++++ dlls/windows.media.speech/unixlib.h | 39 ++++++++ 5 files changed, 196 insertions(+) create mode 100644 dlls/windows.media.speech/unixlib.c create mode 100644 dlls/windows.media.speech/unixlib.h diff --git a/dlls/windows.media.speech/Makefile.in b/dlls/windows.media.speech/Makefile.in index 5be66d8367ef..11a8ad2bba85 100644 --- a/dlls/windows.media.speech/Makefile.in +++ b/dlls/windows.media.speech/Makefile.in @@ -1,4 +1,5 @@ MODULE = windows.media.speech.dll +UNIXLIB = windows.media.speech.so IMPORTS = combase uuid SOURCES = \ @@ -9,4 +10,5 @@ SOURCES = \ main.c \ recognizer.c \ synthesizer.c \ + unixlib.c \ vector.c diff --git a/dlls/windows.media.speech/main.c b/dlls/windows.media.speech/main.c index e772a7915887..d53e1599eb82 100644 --- a/dlls/windows.media.speech/main.c +++ b/dlls/windows.media.speech/main.c @@ -20,10 +20,36 @@ #include "initguid.h" #include "private.h" +#include "unixlib.h" + #include "wine/debug.h" WINE_DEFAULT_DEBUG_CHANNEL(speech); +BOOL WINAPI DllMain( HINSTANCE instance, DWORD reason, void *reserved ) +{ + NTSTATUS status; + + switch (reason) + { + case DLL_PROCESS_ATTACH: + DisableThreadLibraryCalls(instance); + + if ((status = __wine_init_unix_call())) + ERR("loading the unixlib failed with status %#lx.\n", status); + + if ((status = WINE_UNIX_CALL(unix_process_attach, NULL))) + WARN("initializing the unixlib failed with status %#lx.\n", status); + + break; + case DLL_PROCESS_DETACH: + WINE_UNIX_CALL(unix_process_detach, NULL); + break; + } + + return TRUE; +} + HRESULT WINAPI DllGetClassObject(REFCLSID clsid, REFIID riid, void **out) { FIXME("clsid %s, riid %s, out %p stub!\n", debugstr_guid(clsid), debugstr_guid(riid), out); diff --git a/dlls/windows.media.speech/private.h b/dlls/windows.media.speech/private.h index d03fe0e773ed..4b6a8327d6e6 100644 --- a/dlls/windows.media.speech/private.h +++ b/dlls/windows.media.speech/private.h @@ -22,6 +22,10 @@ #include +#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "winerror.h" +#include "winternl.h" #define COBJMACROS #include "corerror.h" #include "windef.h" diff --git a/dlls/windows.media.speech/unixlib.c b/dlls/windows.media.speech/unixlib.c new file mode 100644 index 000000000000..6a9dac6f067c --- /dev/null +++ b/dlls/windows.media.speech/unixlib.c @@ -0,0 +1,125 @@ +/* + * Unixlib for Windows.Media.Speech + * + * Copyright 2023 Bernhard Kölbl for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#if 0 +#pragma makedep unix +#endif + +#include "config.h" + +#include +#include +#include +#include +#include + +#ifdef SONAME_LIBVOSK +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wstrict-prototypes" +#include +#pragma GCC diagnostic pop +#endif + +#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "winerror.h" +#include "winternl.h" + +#include "wine/debug.h" + +#include "unixlib.h" + +WINE_DEFAULT_DEBUG_CHANNEL(speech); +#ifdef SONAME_LIBVOSK +WINE_DECLARE_DEBUG_CHANNEL(winediag); + +static void *vosk_handle; +#define MAKE_FUNCPTR( f ) static typeof(f) * p_##f +MAKE_FUNCPTR(vosk_model_new); +MAKE_FUNCPTR(vosk_model_free); +MAKE_FUNCPTR(vosk_recognizer_new); +MAKE_FUNCPTR(vosk_recognizer_free); +#undef MAKE_FUNCPTR + +static NTSTATUS process_attach( void *args ) +{ + if (!(vosk_handle = dlopen(SONAME_LIBVOSK, RTLD_NOW))) + { + ERR_(winediag)("Wine is unable to load the Unix side dependencies for speech recognition. " + "Make sure Vosk is installed and up to date on your system and try again.\n"); + return STATUS_DLL_NOT_FOUND; + } + +#define LOAD_FUNCPTR( f ) \ + if (!(p_##f = dlsym(vosk_handle, #f))) \ + { \ + ERR("failed to load %s\n", #f); \ + goto error; \ + } + LOAD_FUNCPTR(vosk_model_new) + LOAD_FUNCPTR(vosk_model_free) + LOAD_FUNCPTR(vosk_recognizer_new) + LOAD_FUNCPTR(vosk_recognizer_free) +#undef LOAD_FUNCPTR + + return STATUS_SUCCESS; + +error: + dlclose(vosk_handle); + vosk_handle = NULL; + return STATUS_DLL_NOT_FOUND; +} + +static NTSTATUS process_detach( void *args ) +{ + if (vosk_handle) + { + dlclose(vosk_handle); + vosk_handle = NULL; + } + return STATUS_SUCCESS; +} + +#else /* SONAME_LIBVOSK */ + +#define MAKE_UNSUPPORTED_FUNC( f ) \ + static NTSTATUS f( void *args ) \ + { \ + ERR("wine was compiled without Vosk support. Speech recognition won't work.\n"); \ + return STATUS_NOT_SUPPORTED; \ + } + +MAKE_UNSUPPORTED_FUNC(process_attach) +MAKE_UNSUPPORTED_FUNC(process_detach) +#undef MAKE_UNSUPPORTED_FUNC + +#endif /* SONAME_LIBVOSK */ + +const unixlib_entry_t __wine_unix_call_funcs[] = +{ + process_attach, + process_detach, +}; + +const unixlib_entry_t __wine_unix_call_wow64_funcs[] = +{ + process_attach, + process_detach, +}; diff --git a/dlls/windows.media.speech/unixlib.h b/dlls/windows.media.speech/unixlib.h new file mode 100644 index 000000000000..6c337e54511d --- /dev/null +++ b/dlls/windows.media.speech/unixlib.h @@ -0,0 +1,39 @@ +/* + * Unix library interface for Windows.Media.Speech + * + * Copyright 2023 Bernhard Kölbl for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef __WINE_WINDOWS_MEDIA_SPEECH_UNIXLIB_H +#define __WINE_WINDOWS_MEDIA_SPEECH_UNIXLIB_H + +#include +#include + +#include "windef.h" +#include "winternl.h" +#include "wtypes.h" + +#include "wine/unixlib.h" + +enum unix_funcs +{ + unix_process_attach, + unix_process_detach, +}; + +#endif From 96a97f9cefdd4e65970ed178ca88c602cb807b3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20K=C3=B6lbl?= Date: Mon, 6 Feb 2023 21:24:26 +0100 Subject: [PATCH 106/454] windows.media.speech/tests: Get rid of duplicated hresult. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Bernhard Kölbl CW-Bug-Id: #20134 --- dlls/windows.media.speech/tests/speech.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dlls/windows.media.speech/tests/speech.c b/dlls/windows.media.speech/tests/speech.c index ade056a0a392..9be4fa462df5 100644 --- a/dlls/windows.media.speech/tests/speech.c +++ b/dlls/windows.media.speech/tests/speech.c @@ -42,7 +42,6 @@ #define AsyncStatus_Closed 4 #define SPERR_WINRT_INTERNAL_ERROR 0x800455a0 -#define SPERR_WINRT_INCORRECT_FORMAT 0x80131537 #define IHandler_RecognitionResult ITypedEventHandler_SpeechContinuousRecognitionSession_SpeechContinuousRecognitionResultGeneratedEventArgs #define IHandler_RecognitionResultVtbl ITypedEventHandler_SpeechContinuousRecognitionSession_SpeechContinuousRecognitionResultGeneratedEventArgsVtbl @@ -1082,7 +1081,7 @@ static void test_SpeechSynthesizer(void) operation_ss_stream = (void *)0xdeadbeef; hr = ISpeechSynthesizer_SynthesizeSsmlToStreamAsync(synthesizer, str, &operation_ss_stream); /* Broken on Win 8 + 8.1 */ - ok(hr == S_OK || broken(hr == SPERR_WINRT_INCORRECT_FORMAT), "ISpeechSynthesizer_SynthesizeSsmlToStreamAsync failed, hr %#lx\n", hr); + ok(hr == S_OK || broken(hr == COR_E_FORMAT), "ISpeechSynthesizer_SynthesizeSsmlToStreamAsync failed, hr %#lx\n", hr); if (hr == S_OK) { From 269fd1cc29c0b653a729934e9b1519e26f8f3ac6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20K=C3=B6lbl?= Date: Mon, 20 Feb 2023 23:10:16 +0100 Subject: [PATCH 107/454] windows.media.speech/tests: Allow the SpeechRecognizer creation to fail in Wine. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To allow for error handling of missing Unix-side dependencies. Signed-off-by: Bernhard Kölbl CW-Bug-Id: #20134 --- dlls/windows.media.speech/tests/speech.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/dlls/windows.media.speech/tests/speech.c b/dlls/windows.media.speech/tests/speech.c index 9be4fa462df5..743955ab9bd9 100644 --- a/dlls/windows.media.speech/tests/speech.c +++ b/dlls/windows.media.speech/tests/speech.c @@ -1307,7 +1307,7 @@ static void test_SpeechRecognizer(void) ok(ref == 1, "Got unexpected ref %lu.\n", ref); hr = RoActivateInstance(hstr, &inspectable); - ok(hr == S_OK || broken(hr == SPERR_WINRT_INTERNAL_ERROR), "Got unexpected hr %#lx.\n", hr); + ok(hr == S_OK || hr == SPERR_WINRT_INTERNAL_ERROR, "Got unexpected hr %#lx.\n", hr); if (hr == S_OK) { @@ -1526,7 +1526,7 @@ static void test_SpeechRecognizer(void) } else if (hr == SPERR_WINRT_INTERNAL_ERROR) /* Not sure when this triggers. Probably if a language pack is not installed. */ { - win_skip("Could not init SpeechRecognizer with default language!\n"); + skip("Could not init SpeechRecognizer with default language!\n"); } done: @@ -1743,12 +1743,12 @@ static void test_Recognition(void) ok(hr == S_OK, "WindowsCreateString failed, hr %#lx.\n", hr); hr = RoActivateInstance(hstr, &inspectable); - ok(hr == S_OK || broken(hr == SPERR_WINRT_INTERNAL_ERROR || hr == REGDB_E_CLASSNOTREG), "Got unexpected hr %#lx.\n", hr); + ok(hr == S_OK || hr == SPERR_WINRT_INTERNAL_ERROR || broken(hr == REGDB_E_CLASSNOTREG), "Got unexpected hr %#lx.\n", hr); WindowsDeleteString(hstr); - if (FAILED(hr)) /* Win 8 and 8.1 and Win10 without enabled SR. */ + if (FAILED(hr)) /* Win 8 and 8.1 and Win10 without enabled SR. Wine with missing Unix side dependencies. */ { - win_skip("SpeechRecognizer cannot be activated!\n"); + skip("SpeechRecognizer cannot be activated!\n"); goto done; } From 6fb38b7f911f99dee2de10653f2d999cfbab495b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20K=C3=B6lbl?= Date: Mon, 20 Feb 2023 23:11:02 +0100 Subject: [PATCH 108/454] windows.media.speech: Implement Vosk create and release functions in the unixlib. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Bernhard Kölbl CW-Bug-Id: #20134 --- dlls/windows.media.speech/Makefile.in | 2 +- dlls/windows.media.speech/private.h | 3 + dlls/windows.media.speech/recognizer.c | 42 ++++++ dlls/windows.media.speech/unixlib.c | 169 ++++++++++++++++++++++++- dlls/windows.media.speech/unixlib.h | 16 +++ 5 files changed, 230 insertions(+), 2 deletions(-) diff --git a/dlls/windows.media.speech/Makefile.in b/dlls/windows.media.speech/Makefile.in index 11a8ad2bba85..ea1e42721395 100644 --- a/dlls/windows.media.speech/Makefile.in +++ b/dlls/windows.media.speech/Makefile.in @@ -1,6 +1,6 @@ MODULE = windows.media.speech.dll UNIXLIB = windows.media.speech.so -IMPORTS = combase uuid +IMPORTS = combase uuid user32 SOURCES = \ async.c \ diff --git a/dlls/windows.media.speech/private.h b/dlls/windows.media.speech/private.h index 4b6a8327d6e6..60d09c9f7d1f 100644 --- a/dlls/windows.media.speech/private.h +++ b/dlls/windows.media.speech/private.h @@ -31,6 +31,7 @@ #include "windef.h" #include "winbase.h" #include "winstring.h" +#include "winuser.h" #include "objbase.h" #include "activation.h" @@ -47,6 +48,8 @@ #include "wine/list.h" +#define SPERR_WINRT_INTERNAL_ERROR 0x800455a0 + /* * * Windows.Media.SpeechRecognition diff --git a/dlls/windows.media.speech/recognizer.c b/dlls/windows.media.speech/recognizer.c index a98970d35f9e..85122489d303 100644 --- a/dlls/windows.media.speech/recognizer.c +++ b/dlls/windows.media.speech/recognizer.c @@ -25,6 +25,9 @@ #include "wine/debug.h" +#include "unixlib.h" +#include "wine/unixlib.h" + WINE_DEFAULT_DEBUG_CHANNEL(speech); /* @@ -171,6 +174,8 @@ struct session IAudioCaptureClient *capture_client; WAVEFORMATEX capture_wfx; + speech_recognizer_handle unix_handle; + HANDLE worker_thread, worker_control_event, audio_buf_event; BOOLEAN worker_running, worker_paused; CRITICAL_SECTION cs; @@ -319,7 +324,9 @@ static ULONG WINAPI session_AddRef( ISpeechContinuousRecognitionSession *iface ) static ULONG WINAPI session_Release( ISpeechContinuousRecognitionSession *iface ) { struct session *impl = impl_from_ISpeechContinuousRecognitionSession(iface); + struct speech_release_recognizer_params release_params; ULONG ref = InterlockedDecrement(&impl->ref); + TRACE("iface %p, ref %lu.\n", iface, ref); if (!ref) @@ -345,6 +352,9 @@ static ULONG WINAPI session_Release( ISpeechContinuousRecognitionSession *iface impl->cs.DebugInfo->Spare[0] = 0; DeleteCriticalSection(&impl->cs); + release_params.handle = impl->unix_handle; + WINE_UNIX_CALL(unix_speech_release_recognizer, &release_params); + IVector_ISpeechRecognitionConstraint_Release(impl->constraints); free(impl); } @@ -1080,6 +1090,35 @@ static HRESULT recognizer_factory_create_audio_capture(struct session *session) return hr; } +static HRESULT recognizer_factory_create_unix_instance( struct session *session ) +{ + struct speech_create_recognizer_params create_params = { 0 }; + WCHAR locale[LOCALE_NAME_MAX_LENGTH]; + NTSTATUS status; + INT len; + + if (!(len = GetUserDefaultLocaleName(locale, LOCALE_NAME_MAX_LENGTH))) + return E_FAIL; + + if (CharLowerBuffW(locale, len) != len) + return E_FAIL; + + if (!WideCharToMultiByte(CP_ACP, 0, locale, len, create_params.locale, ARRAY_SIZE(create_params.locale), NULL, NULL)) + return HRESULT_FROM_WIN32(GetLastError()); + + create_params.sample_rate = (FLOAT)session->capture_wfx.nSamplesPerSec; + + if ((status = WINE_UNIX_CALL(unix_speech_create_recognizer, &create_params))) + { + ERR("Unable to create Vosk instance for locale %s, status %#lx. Speech recognition won't work.\n", debugstr_a(create_params.locale), status); + return SPERR_WINRT_INTERNAL_ERROR; + } + + session->unix_handle = create_params.handle; + + return S_OK; +} + static HRESULT WINAPI recognizer_factory_Create( ISpeechRecognizerFactory *iface, ILanguage *language, ISpeechRecognizer **speechrecognizer ) { struct recognizer *impl; @@ -1126,6 +1165,9 @@ static HRESULT WINAPI recognizer_factory_Create( ISpeechRecognizerFactory *iface if (FAILED(hr = recognizer_factory_create_audio_capture(session))) goto error; + if (FAILED(hr = recognizer_factory_create_unix_instance(session))) + goto error; + InitializeCriticalSectionEx(&session->cs, 0, RTL_CRITICAL_SECTION_FLAG_FORCE_DEBUG_INFO); session->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": recognition_session.cs"); diff --git a/dlls/windows.media.speech/unixlib.c b/dlls/windows.media.speech/unixlib.c index 6a9dac6f067c..155780a3e4bd 100644 --- a/dlls/windows.media.speech/unixlib.c +++ b/dlls/windows.media.speech/unixlib.c @@ -26,9 +26,12 @@ #include #include -#include #include +#include +#include #include +#include +#include #ifdef SONAME_LIBVOSK #pragma GCC diagnostic push @@ -97,6 +100,164 @@ static NTSTATUS process_detach( void *args ) return STATUS_SUCCESS; } +static inline speech_recognizer_handle vosk_recognizer_to_handle( VoskRecognizer *recognizer ) +{ + return (speech_recognizer_handle)(UINT_PTR)recognizer; +} + +static inline VoskRecognizer *vosk_recognizer_from_handle( speech_recognizer_handle handle ) +{ + return (VoskRecognizer *)(UINT_PTR)handle; +} + +static NTSTATUS find_model_by_locale_and_path( const char *path, const char *locale, VoskModel **model ) +{ + static const char *vosk_model_identifier_small = "vosk-model-small-"; + static const char *vosk_model_identifier = "vosk-model-"; + size_t ident_small_len = strlen(vosk_model_identifier_small); + size_t ident_len = strlen(vosk_model_identifier); + char *ent_name, *model_path, *best_match, *delim; + NTSTATUS status = STATUS_UNSUCCESSFUL; + struct dirent *dirent; + size_t path_len, len; + DIR *dir; + + TRACE("path %s, locale %s, model %p.\n", path, debugstr_a(locale), model); + + if (!path || !locale || (len = strlen(locale)) < 4) + return STATUS_UNSUCCESSFUL; + + if (!(dir = opendir(path))) + return STATUS_UNSUCCESSFUL; + + delim = strchr(locale, '-'); + path_len = strlen(path); + best_match = NULL; + *model = NULL; + + while ((dirent = readdir(dir))) + { + ent_name = dirent->d_name; + + if (!strncmp(ent_name, vosk_model_identifier_small, ident_small_len)) + ent_name += ident_small_len; + else if (!strncmp(ent_name, vosk_model_identifier, ident_len)) + ent_name += ident_len; + else + continue; + + /* + * Find the first matching model for lang and region (en-us). + * If there isn't any, pick the first one just matching lang (en). + */ + if (!strncmp(ent_name, locale, len)) + { + if (best_match) free(best_match); + best_match = strdup(dirent->d_name); + break; + } + + if (!best_match && !strncmp(ent_name, locale, delim - locale)) + best_match = strdup(dirent->d_name); + } + + closedir(dir); + + if (!best_match) + return STATUS_UNSUCCESSFUL; + + if (!(model_path = malloc(path_len + 1 /* '/' */ + strlen(best_match) + 1))) + { + status = STATUS_NO_MEMORY; + goto done; + } + + sprintf(model_path, "%s/%s", path, best_match); + + TRACE("trying to load Vosk model %s.\n", debugstr_a(model_path)); + + if ((*model = p_vosk_model_new(model_path)) != NULL) + status = STATUS_SUCCESS; + +done: + free(model_path); + free(best_match); + + return status; +} + +static NTSTATUS find_model_by_locale( const char *locale, VoskModel **model ) +{ + const char *suffix = NULL; + char *env, *path = NULL; + NTSTATUS status; + + TRACE("locale %s, model %p.\n", debugstr_a(locale), model); + + if (!model) + return STATUS_UNSUCCESSFUL; + + if (!find_model_by_locale_and_path(getenv("VOSK_MODEL_PATH"), locale, model)) + return STATUS_SUCCESS; + if (!find_model_by_locale_and_path("/usr/share/vosk", locale, model)) + return STATUS_SUCCESS; + + if ((env = getenv("XDG_CACHE_HOME"))) + suffix = "/vosk"; + else if ((env = getenv("HOME"))) + suffix = "/.cache/vosk"; + else + return STATUS_UNSUCCESSFUL; + + if (!(path = malloc(strlen(env) + strlen(suffix) + 1))) + return STATUS_NO_MEMORY; + + sprintf(path, "%s%s", env, suffix); + status = find_model_by_locale_and_path(path, locale, model); + free(path); + + return status; +} + +static NTSTATUS speech_create_recognizer( void *args ) +{ + struct speech_create_recognizer_params *params = args; + VoskRecognizer *recognizer = NULL; + VoskModel *model = NULL; + NTSTATUS status = STATUS_SUCCESS; + + TRACE("args %p.\n", args); + + if (!vosk_handle) + return STATUS_NOT_SUPPORTED; + + if ((status = find_model_by_locale(params->locale, &model))) + return status; + + if (!(recognizer = p_vosk_recognizer_new(model, params->sample_rate))) + status = STATUS_UNSUCCESSFUL; + + /* VoskModel is reference-counted. A VoskRecognizer keeps a reference to its model. */ + p_vosk_model_free(model); + + params->handle = vosk_recognizer_to_handle(recognizer); + return status; +} + +static NTSTATUS speech_release_recognizer( void *args ) +{ + struct speech_release_recognizer_params *params = args; + + TRACE("args %p.\n", args); + + if (!vosk_handle) + return STATUS_NOT_SUPPORTED; + + p_vosk_recognizer_free(vosk_recognizer_from_handle(params->handle)); + + return STATUS_SUCCESS; +} + #else /* SONAME_LIBVOSK */ #define MAKE_UNSUPPORTED_FUNC( f ) \ @@ -108,6 +269,8 @@ static NTSTATUS process_detach( void *args ) MAKE_UNSUPPORTED_FUNC(process_attach) MAKE_UNSUPPORTED_FUNC(process_detach) +MAKE_UNSUPPORTED_FUNC(speech_create_recognizer) +MAKE_UNSUPPORTED_FUNC(speech_release_recognizer) #undef MAKE_UNSUPPORTED_FUNC #endif /* SONAME_LIBVOSK */ @@ -116,10 +279,14 @@ const unixlib_entry_t __wine_unix_call_funcs[] = { process_attach, process_detach, + speech_create_recognizer, + speech_release_recognizer, }; const unixlib_entry_t __wine_unix_call_wow64_funcs[] = { process_attach, process_detach, + speech_create_recognizer, + speech_release_recognizer, }; diff --git a/dlls/windows.media.speech/unixlib.h b/dlls/windows.media.speech/unixlib.h index 6c337e54511d..974e8d5f7976 100644 --- a/dlls/windows.media.speech/unixlib.h +++ b/dlls/windows.media.speech/unixlib.h @@ -30,10 +30,26 @@ #include "wine/unixlib.h" +typedef UINT64 speech_recognizer_handle; + +struct speech_create_recognizer_params +{ + speech_recognizer_handle handle; + CHAR locale[LOCALE_NAME_MAX_LENGTH]; + FLOAT sample_rate; +}; + +struct speech_release_recognizer_params +{ + speech_recognizer_handle handle; +}; + enum unix_funcs { unix_process_attach, unix_process_detach, + unix_speech_create_recognizer, + unix_speech_release_recognizer, }; #endif From 1d2815e817755255dc9f8acf5e591d499b418975 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20K=C3=B6lbl?= Date: Mon, 6 Mar 2023 23:51:31 +0100 Subject: [PATCH 109/454] windows.media.speech: Move unix side recognizer creation to recognizer_CompileConstraintsAsync(). MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Bernhard Kölbl CW-Bug-Id: #20134 --- dlls/windows.media.speech/recognizer.c | 76 ++++++++++++++------------ 1 file changed, 41 insertions(+), 35 deletions(-) diff --git a/dlls/windows.media.speech/recognizer.c b/dlls/windows.media.speech/recognizer.c index 85122489d303..f6d9bffe4edc 100644 --- a/dlls/windows.media.speech/recognizer.c +++ b/dlls/windows.media.speech/recognizer.c @@ -735,17 +735,55 @@ static HRESULT WINAPI recognizer_get_UIOptions( ISpeechRecognizer *iface, ISpeec return E_NOTIMPL; } +static HRESULT recognizer_create_unix_instance( struct session *session ) +{ + struct speech_create_recognizer_params create_params = { 0 }; + WCHAR locale[LOCALE_NAME_MAX_LENGTH]; + NTSTATUS status; + INT len; + + if (!(len = GetUserDefaultLocaleName(locale, LOCALE_NAME_MAX_LENGTH))) + return E_FAIL; + + if (CharLowerBuffW(locale, len) != len) + return E_FAIL; + + if (!WideCharToMultiByte(CP_ACP, 0, locale, len, create_params.locale, ARRAY_SIZE(create_params.locale), NULL, NULL)) + return HRESULT_FROM_WIN32(GetLastError()); + + create_params.sample_rate = (FLOAT)session->capture_wfx.nSamplesPerSec; + + if ((status = WINE_UNIX_CALL(unix_speech_create_recognizer, &create_params))) + { + ERR("Unable to create Vosk instance for locale %s, status %#lx. Speech recognition won't work.\n", debugstr_a(create_params.locale), status); + return SPERR_WINRT_INTERNAL_ERROR; + } + + session->unix_handle = create_params.handle; + + return S_OK; +} + static HRESULT recognizer_compile_constraints_async( IInspectable *invoker, IInspectable **result ) { - return compilation_result_create(SpeechRecognitionResultStatus_Success, (ISpeechRecognitionCompilationResult **) result); + struct recognizer *impl = impl_from_ISpeechRecognizer((ISpeechRecognizer *)invoker); + struct session *session = impl_from_ISpeechContinuousRecognitionSession(impl->session); + HRESULT hr; + + if (FAILED(hr = recognizer_create_unix_instance(session))) + { + WARN("Failed to created recognizer instance.\n"); + return compilation_result_create(SpeechRecognitionResultStatus_GrammarCompilationFailure, (ISpeechRecognitionCompilationResult **) result); + } + else return compilation_result_create(SpeechRecognitionResultStatus_Success, (ISpeechRecognitionCompilationResult **) result); } static HRESULT WINAPI recognizer_CompileConstraintsAsync( ISpeechRecognizer *iface, IAsyncOperation_SpeechRecognitionCompilationResult **operation ) { IAsyncOperation_IInspectable **value = (IAsyncOperation_IInspectable **)operation; - FIXME("iface %p, operation %p semi-stub!\n", iface, operation); - return async_operation_inspectable_create(&IID_IAsyncOperation_SpeechRecognitionCompilationResult, NULL, recognizer_compile_constraints_async, value); + TRACE("iface %p, operation %p semi-stub!\n", iface, operation); + return async_operation_inspectable_create(&IID_IAsyncOperation_SpeechRecognitionCompilationResult, (IInspectable *)iface, recognizer_compile_constraints_async, value); } static HRESULT WINAPI recognizer_RecognizeAsync( ISpeechRecognizer *iface, @@ -1090,35 +1128,6 @@ static HRESULT recognizer_factory_create_audio_capture(struct session *session) return hr; } -static HRESULT recognizer_factory_create_unix_instance( struct session *session ) -{ - struct speech_create_recognizer_params create_params = { 0 }; - WCHAR locale[LOCALE_NAME_MAX_LENGTH]; - NTSTATUS status; - INT len; - - if (!(len = GetUserDefaultLocaleName(locale, LOCALE_NAME_MAX_LENGTH))) - return E_FAIL; - - if (CharLowerBuffW(locale, len) != len) - return E_FAIL; - - if (!WideCharToMultiByte(CP_ACP, 0, locale, len, create_params.locale, ARRAY_SIZE(create_params.locale), NULL, NULL)) - return HRESULT_FROM_WIN32(GetLastError()); - - create_params.sample_rate = (FLOAT)session->capture_wfx.nSamplesPerSec; - - if ((status = WINE_UNIX_CALL(unix_speech_create_recognizer, &create_params))) - { - ERR("Unable to create Vosk instance for locale %s, status %#lx. Speech recognition won't work.\n", debugstr_a(create_params.locale), status); - return SPERR_WINRT_INTERNAL_ERROR; - } - - session->unix_handle = create_params.handle; - - return S_OK; -} - static HRESULT WINAPI recognizer_factory_Create( ISpeechRecognizerFactory *iface, ILanguage *language, ISpeechRecognizer **speechrecognizer ) { struct recognizer *impl; @@ -1165,9 +1174,6 @@ static HRESULT WINAPI recognizer_factory_Create( ISpeechRecognizerFactory *iface if (FAILED(hr = recognizer_factory_create_audio_capture(session))) goto error; - if (FAILED(hr = recognizer_factory_create_unix_instance(session))) - goto error; - InitializeCriticalSectionEx(&session->cs, 0, RTL_CRITICAL_SECTION_FLAG_FORCE_DEBUG_INFO); session->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": recognition_session.cs"); From 660a28b0c102d7de5422b06e6888d11a571c5a65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20K=C3=B6lbl?= Date: Mon, 27 Feb 2023 13:22:00 +0100 Subject: [PATCH 110/454] windows.media.speech: Implement recognition in the unixlib. CW-Bug-Id: #20134 --- dlls/windows.media.speech/recognizer.c | 35 +++++++++++- dlls/windows.media.speech/unixlib.c | 79 ++++++++++++++++++++++++++ dlls/windows.media.speech/unixlib.h | 26 ++++++++- 3 files changed, 138 insertions(+), 2 deletions(-) diff --git a/dlls/windows.media.speech/recognizer.c b/dlls/windows.media.speech/recognizer.c index f6d9bffe4edc..5ca7bf7f62af 100644 --- a/dlls/windows.media.speech/recognizer.c +++ b/dlls/windows.media.speech/recognizer.c @@ -196,10 +196,13 @@ static DWORD CALLBACK session_worker_thread_cb( void *args ) { ISpeechContinuousRecognitionSession *iface = args; struct session *impl = impl_from_ISpeechContinuousRecognitionSession(iface); + struct speech_get_recognition_result_params recognition_result_params; + struct speech_recognize_audio_params recognize_audio_params; BOOLEAN running = TRUE, paused = FALSE; UINT32 frame_count, tmp_buf_size; BYTE *audio_buf, *tmp_buf = NULL; DWORD flags, status; + NTSTATUS nt_status; HANDLE events[2]; HRESULT hr; @@ -269,7 +272,37 @@ static DWORD CALLBACK session_worker_thread_cb( void *args ) IAudioCaptureClient_ReleaseBuffer(impl->capture_client, frames_available); } - /* TODO: Send mic data to recognizer and handle results. */ + recognize_audio_params.handle = impl->unix_handle; + recognize_audio_params.samples = tmp_buf; + recognize_audio_params.samples_size = tmp_buf_offset; + recognize_audio_params.status = RECOGNITION_STATUS_EXCEPTION; + + if (NT_ERROR(nt_status = WINE_UNIX_CALL(unix_speech_recognize_audio, &recognize_audio_params))) + WARN("unix_speech_recognize_audio failed with status %#lx.\n", nt_status); + + if (recognize_audio_params.status != RECOGNITION_STATUS_RESULT_AVAILABLE) + continue; + + recognition_result_params.handle = impl->unix_handle; + recognition_result_params.result_buf = NULL; + recognition_result_params.result_buf_size = 512; + + do + { + recognition_result_params.result_buf = realloc(recognition_result_params.result_buf, recognition_result_params.result_buf_size); + } + while (WINE_UNIX_CALL(unix_speech_get_recognition_result, &recognition_result_params) == STATUS_BUFFER_TOO_SMALL && + recognition_result_params.result_buf); + + if (!recognition_result_params.result_buf) + { + WARN("memory allocation failed.\n"); + break; + } + + /* TODO: Compare recognized text to available options. */ + + free(recognition_result_params.result_buf); } else { diff --git a/dlls/windows.media.speech/unixlib.c b/dlls/windows.media.speech/unixlib.c index 155780a3e4bd..c41bb4cc445a 100644 --- a/dlls/windows.media.speech/unixlib.c +++ b/dlls/windows.media.speech/unixlib.c @@ -59,6 +59,9 @@ MAKE_FUNCPTR(vosk_model_new); MAKE_FUNCPTR(vosk_model_free); MAKE_FUNCPTR(vosk_recognizer_new); MAKE_FUNCPTR(vosk_recognizer_free); +MAKE_FUNCPTR(vosk_recognizer_accept_waveform); +MAKE_FUNCPTR(vosk_recognizer_final_result); +MAKE_FUNCPTR(vosk_recognizer_reset); #undef MAKE_FUNCPTR static NTSTATUS process_attach( void *args ) @@ -80,6 +83,9 @@ static NTSTATUS process_attach( void *args ) LOAD_FUNCPTR(vosk_model_free) LOAD_FUNCPTR(vosk_recognizer_new) LOAD_FUNCPTR(vosk_recognizer_free) + LOAD_FUNCPTR(vosk_recognizer_accept_waveform) + LOAD_FUNCPTR(vosk_recognizer_final_result) + LOAD_FUNCPTR(vosk_recognizer_reset) #undef LOAD_FUNCPTR return STATUS_SUCCESS; @@ -258,6 +264,73 @@ static NTSTATUS speech_release_recognizer( void *args ) return STATUS_SUCCESS; } +static NTSTATUS speech_recognize_audio( void *args ) +{ + struct speech_recognize_audio_params *params = args; + VoskRecognizer *recognizer = vosk_recognizer_from_handle(params->handle); + + if (!vosk_handle) + return STATUS_NOT_SUPPORTED; + + if (!recognizer) + return STATUS_UNSUCCESSFUL; + + params->status = p_vosk_recognizer_accept_waveform(recognizer, (const char *)params->samples, params->samples_size); + + return STATUS_SUCCESS; +} + +static NTSTATUS speech_get_recognition_result( void* args ) +{ + struct speech_get_recognition_result_params *params = args; + VoskRecognizer *recognizer = vosk_recognizer_from_handle(params->handle); + static const char *result_json_start = "{\n \"text\" : \""; + const size_t json_start_len = strlen(result_json_start); + static size_t last_result_len = 0; + static char *last_result = NULL; + const char *tmp = NULL; + + TRACE("args %p.\n", args); + + if (!vosk_handle) + return STATUS_NOT_SUPPORTED; + + if (!recognizer) + return STATUS_UNSUCCESSFUL; + + if (!last_result) + { + if ((tmp = p_vosk_recognizer_final_result(recognizer))) + { + last_result = strdup(tmp); + tmp = last_result; + + /* Operations to remove the JSON wrapper "{\n \"text\" : \"some recognized text\"\n}" -> "some recognized text\0" */ + memmove(last_result, last_result + json_start_len, strlen(last_result) - json_start_len + 1); + last_result = strrchr(last_result, '\"'); + last_result[0] = '\0'; + + last_result = (char *)tmp; + last_result_len = strlen(last_result); + TRACE("last_result %s.\n", debugstr_a(last_result)); + } + else return STATUS_NOT_FOUND; + } + else if (params->result_buf_size >= last_result_len + 1) + { + memcpy(params->result_buf, last_result, last_result_len + 1); + p_vosk_recognizer_reset(recognizer); + + free (last_result); + last_result = NULL; + + return STATUS_SUCCESS; + } + + params->result_buf_size = last_result_len + 1; + return STATUS_BUFFER_TOO_SMALL; +} + #else /* SONAME_LIBVOSK */ #define MAKE_UNSUPPORTED_FUNC( f ) \ @@ -271,6 +344,8 @@ MAKE_UNSUPPORTED_FUNC(process_attach) MAKE_UNSUPPORTED_FUNC(process_detach) MAKE_UNSUPPORTED_FUNC(speech_create_recognizer) MAKE_UNSUPPORTED_FUNC(speech_release_recognizer) +MAKE_UNSUPPORTED_FUNC(speech_recognize_audio) +MAKE_UNSUPPORTED_FUNC(speech_get_recognition_result) #undef MAKE_UNSUPPORTED_FUNC #endif /* SONAME_LIBVOSK */ @@ -281,6 +356,8 @@ const unixlib_entry_t __wine_unix_call_funcs[] = process_detach, speech_create_recognizer, speech_release_recognizer, + speech_recognize_audio, + speech_get_recognition_result, }; const unixlib_entry_t __wine_unix_call_wow64_funcs[] = @@ -289,4 +366,6 @@ const unixlib_entry_t __wine_unix_call_wow64_funcs[] = process_detach, speech_create_recognizer, speech_release_recognizer, + speech_recognize_audio, + speech_get_recognition_result, }; diff --git a/dlls/windows.media.speech/unixlib.h b/dlls/windows.media.speech/unixlib.h index 974e8d5f7976..a07d76705d53 100644 --- a/dlls/windows.media.speech/unixlib.h +++ b/dlls/windows.media.speech/unixlib.h @@ -44,12 +44,36 @@ struct speech_release_recognizer_params speech_recognizer_handle handle; }; -enum unix_funcs +enum speech_recognition_status +{ + RECOGNITION_STATUS_CONTINUING, + RECOGNITION_STATUS_RESULT_AVAILABLE, + RECOGNITION_STATUS_EXCEPTION, +}; + +struct speech_recognize_audio_params +{ + speech_recognizer_handle handle; + const BYTE *samples; + UINT32 samples_size; + enum speech_recognition_status status; +}; + +struct speech_get_recognition_result_params +{ + speech_recognizer_handle handle; + char *result_buf; + UINT32 result_buf_size; +}; + +enum vosk_funcs { unix_process_attach, unix_process_detach, unix_speech_create_recognizer, unix_speech_release_recognizer, + unix_speech_recognize_audio, + unix_speech_get_recognition_result, }; #endif From 8b7bb135dde614a0b0240647352eaa6928bc5683 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20K=C3=B6lbl?= Date: Wed, 25 May 2022 14:14:18 +0200 Subject: [PATCH 111/454] windows.media.speech: Compare recognized words with available constraints. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Bernhard Kölbl CW-Bug-Id: #20134 --- dlls/windows.media.speech/recognizer.c | 115 ++++++++++++++++++++++++- 1 file changed, 114 insertions(+), 1 deletion(-) diff --git a/dlls/windows.media.speech/recognizer.c b/dlls/windows.media.speech/recognizer.c index 5ca7bf7f62af..1d7eb1cea726 100644 --- a/dlls/windows.media.speech/recognizer.c +++ b/dlls/windows.media.speech/recognizer.c @@ -192,18 +192,105 @@ static inline struct session *impl_from_ISpeechContinuousRecognitionSession( ISp return CONTAINING_RECORD(iface, struct session, ISpeechContinuousRecognitionSession_iface); } +static HRESULT session_find_constraint_by_string(struct session *session, WCHAR *str, HSTRING *hstr_out, ISpeechRecognitionConstraint **out) +{ + ISpeechRecognitionListConstraint *list_constraint; + IIterable_IInspectable *constraints_iterable; + IIterator_IInspectable *constraints_iterator; + ISpeechRecognitionConstraint *constraint; + IIterable_HSTRING *commands_iterable; + IIterator_HSTRING *commands_iterator; + BOOLEAN has_constraint, has_command; + IVector_HSTRING *commands; + const WCHAR *command_str; + HSTRING command; + HRESULT hr; + + TRACE("session %p, str %s, out %p.\n", session, debugstr_w(str), out); + + if (FAILED(hr = IVector_ISpeechRecognitionConstraint_QueryInterface(session->constraints, &IID_IIterable_ISpeechRecognitionConstraint, (void **)&constraints_iterable))) + return hr; + + if (FAILED(hr = IIterable_IInspectable_First(constraints_iterable, &constraints_iterator))) + { + IIterable_IInspectable_Release(constraints_iterable); + return hr; + } + + *out = NULL; + + for (hr = IIterator_IInspectable_get_HasCurrent(constraints_iterator, &has_constraint); SUCCEEDED(hr) && has_constraint && !(*out); hr = IIterator_IInspectable_MoveNext(constraints_iterator, &has_constraint)) + { + list_constraint = NULL; + commands_iterable = NULL; + commands_iterator = NULL; + commands = NULL; + + if (FAILED(IIterator_IInspectable_get_Current(constraints_iterator, (IInspectable **)&constraint))) + goto skip; + + if (FAILED(ISpeechRecognitionConstraint_QueryInterface(constraint, &IID_ISpeechRecognitionListConstraint, (void**)&list_constraint))) + goto skip; + + if (FAILED(ISpeechRecognitionListConstraint_get_Commands(list_constraint, &commands))) + goto skip; + + if (FAILED(IVector_HSTRING_QueryInterface(commands, &IID_IIterable_HSTRING, (void **)&commands_iterable))) + goto skip; + + if (FAILED(IIterable_HSTRING_First(commands_iterable, &commands_iterator))) + goto skip; + + for (hr = IIterator_HSTRING_get_HasCurrent(commands_iterator, &has_command); SUCCEEDED(hr) && has_command && !(*out); hr = IIterator_HSTRING_MoveNext(commands_iterator, &has_command)) + { + if (FAILED(IIterator_HSTRING_get_Current(commands_iterator, &command))) + continue; + + command_str = WindowsGetStringRawBuffer(command, NULL); + + TRACE("Comparing str %s to command_str %s.\n", debugstr_w(str), debugstr_w(command_str)); + + if (!wcsicmp(str, command_str)) + { + TRACE("constraint %p has str %s.\n", constraint, debugstr_w(str)); + ISpeechRecognitionConstraint_AddRef((*out = constraint)); + WindowsDuplicateString(command, hstr_out); + } + + WindowsDeleteString(command); + } + +skip: + if (commands_iterator) IIterator_HSTRING_Release(commands_iterator); + if (commands_iterable) IIterable_HSTRING_Release(commands_iterable); + if (commands) IVector_HSTRING_Release(commands); + + if (list_constraint) ISpeechRecognitionListConstraint_Release(list_constraint); + if (constraint) ISpeechRecognitionConstraint_Release(constraint); + } + + IIterator_IInspectable_Release(constraints_iterator); + IIterable_IInspectable_Release(constraints_iterable); + + hr = (*out) ? S_OK : COR_E_KEYNOTFOUND; + return hr; +} + static DWORD CALLBACK session_worker_thread_cb( void *args ) { ISpeechContinuousRecognitionSession *iface = args; struct session *impl = impl_from_ISpeechContinuousRecognitionSession(iface); struct speech_get_recognition_result_params recognition_result_params; struct speech_recognize_audio_params recognize_audio_params; + ISpeechRecognitionConstraint *constraint; BOOLEAN running = TRUE, paused = FALSE; UINT32 frame_count, tmp_buf_size; BYTE *audio_buf, *tmp_buf = NULL; + WCHAR *recognized_text; DWORD flags, status; NTSTATUS nt_status; HANDLE events[2]; + HSTRING hstring; HRESULT hr; SetThreadDescription(GetCurrentThread(), L"wine_speech_recognition_session_worker"); @@ -253,6 +340,7 @@ static DWORD CALLBACK session_worker_thread_cb( void *args ) { SIZE_T packet_size = 0, tmp_buf_offset = 0; UINT32 frames_available = 0; + INT recognized_text_len = 0; while (tmp_buf_offset < tmp_buf_size && IAudioCaptureClient_GetBuffer(impl->capture_client, &audio_buf, &frames_available, &flags, NULL, NULL) == S_OK) @@ -300,8 +388,33 @@ static DWORD CALLBACK session_worker_thread_cb( void *args ) break; } - /* TODO: Compare recognized text to available options. */ + /* Silence was recognized. */ + if (!strcmp(recognition_result_params.result_buf, "")) + { + free(recognition_result_params.result_buf); + continue; + } + + recognized_text_len = MultiByteToWideChar(CP_UTF8, 0, recognition_result_params.result_buf, -1, NULL, 0); + + if (!(recognized_text = malloc(recognized_text_len * sizeof(WCHAR)))) + { + free(recognition_result_params.result_buf); + WARN("memory allocation failed.\n"); + break; + } + + MultiByteToWideChar(CP_UTF8, 0, recognition_result_params.result_buf, -1, recognized_text, recognized_text_len); + + if (SUCCEEDED(hr = session_find_constraint_by_string(impl, recognized_text, &hstring, &constraint))) + { + /* TODO: Send event. */ + + WindowsDeleteString(hstring); + ISpeechRecognitionConstraint_Release(constraint); + } + free(recognized_text); free(recognition_result_params.result_buf); } else From 477568a0c66c3743c3a839f83ba414503aeb5e0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20K=C3=B6lbl?= Date: Mon, 30 May 2022 18:36:51 +0200 Subject: [PATCH 112/454] windows.media.speech: Send event on recognition success. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Bernhard Kölbl CW-Bug-Id: #20134 --- dlls/windows.media.speech/recognizer.c | 368 ++++++++++++++++++++++++- 1 file changed, 367 insertions(+), 1 deletion(-) diff --git a/dlls/windows.media.speech/recognizer.c b/dlls/windows.media.speech/recognizer.c index 1d7eb1cea726..1a07915eac8c 100644 --- a/dlls/windows.media.speech/recognizer.c +++ b/dlls/windows.media.speech/recognizer.c @@ -30,6 +30,363 @@ WINE_DEFAULT_DEBUG_CHANNEL(speech); +struct recognition_result +{ + ISpeechRecognitionResult ISpeechRecognitionResult_iface; + ISpeechRecognitionResult2 ISpeechRecognitionResult2_iface; + LONG ref; + + ISpeechRecognitionConstraint *constraint; + HSTRING text; +}; + +static inline struct recognition_result *impl_from_ISpeechRecognitionResult( ISpeechRecognitionResult *iface ) +{ + return CONTAINING_RECORD(iface, struct recognition_result, ISpeechRecognitionResult_iface); +} + +static HRESULT WINAPI recognition_result_QueryInterface( ISpeechRecognitionResult *iface, REFIID iid, void **out ) +{ + struct recognition_result *impl = impl_from_ISpeechRecognitionResult(iface); + + TRACE("iface %p, iid %s, out %p.\n", iface, debugstr_guid(iid), out); + + if (IsEqualGUID(iid, &IID_IUnknown) || + IsEqualGUID(iid, &IID_IInspectable) || + IsEqualGUID(iid, &IID_IAgileObject) || + IsEqualGUID(iid, &IID_ISpeechRecognitionResult)) + { + IInspectable_AddRef((*out = &impl->ISpeechRecognitionResult_iface)); + return S_OK; + } + + if (IsEqualGUID(iid, &IID_ISpeechRecognitionResult2)) + { + IInspectable_AddRef((*out = &impl->ISpeechRecognitionResult2_iface)); + return S_OK; + } + + WARN("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(iid)); + *out = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI recognition_result_AddRef( ISpeechRecognitionResult *iface ) +{ + struct recognition_result *impl = impl_from_ISpeechRecognitionResult(iface); + ULONG ref = InterlockedIncrement(&impl->ref); + TRACE("iface %p, ref %lu.\n", iface, ref); + return ref; +} + +static ULONG WINAPI recognition_result_Release( ISpeechRecognitionResult *iface ) +{ + struct recognition_result *impl = impl_from_ISpeechRecognitionResult(iface); + + ULONG ref = InterlockedDecrement(&impl->ref); + TRACE("iface %p, ref %lu.\n", iface, ref); + + if(!ref) + { + ISpeechRecognitionConstraint_Release(impl->constraint); + WindowsDeleteString(impl->text); + free(impl); + } + + return ref; +} + +static HRESULT WINAPI recognition_result_GetIids( ISpeechRecognitionResult *iface, ULONG *iid_count, IID **iids ) +{ + FIXME("iface %p, iid_count %p, iids %p stub!\n", iface, iid_count, iids); + return E_NOTIMPL; +} + +static HRESULT WINAPI recognition_result_GetRuntimeClassName( ISpeechRecognitionResult *iface, HSTRING *class_name ) +{ + FIXME("iface %p, class_name %p stub!\n", iface, class_name); + return E_NOTIMPL; +} + +static HRESULT WINAPI recognition_result_GetTrustLevel( ISpeechRecognitionResult *iface, TrustLevel *trust_level ) +{ + FIXME("iface %p, trust_level %p stub!\n", iface, trust_level); + return E_NOTIMPL; +} + +static HRESULT WINAPI recognition_result_get_Status( ISpeechRecognitionResult *iface, SpeechRecognitionResultStatus *value ) +{ + FIXME("iface %p, operation %p stub!\n", iface, value); + *value = SpeechRecognitionResultStatus_Success; + return S_OK; +} + +static HRESULT WINAPI recognition_result_get_Text( ISpeechRecognitionResult *iface, HSTRING *value ) +{ + struct recognition_result *impl = impl_from_ISpeechRecognitionResult(iface); + TRACE("iface %p, operation %p, text: %s.\n", iface, value, debugstr_hstring(impl->text)); + return WindowsDuplicateString(impl->text, value); +} + +static HRESULT WINAPI recognition_result_get_Confidence( ISpeechRecognitionResult *iface, SpeechRecognitionConfidence *value ) +{ + FIXME("iface %p, operation %p semi stub!\n", iface, value); + *value = SpeechRecognitionConfidence_High; + return S_OK; +} + +static HRESULT WINAPI recognition_result_get_SemanticInterpretation( ISpeechRecognitionResult *iface, + ISpeechRecognitionSemanticInterpretation **value ) +{ + FIXME("iface %p, operation %p stub!\n", iface, value); + return E_NOTIMPL; +} + +static HRESULT WINAPI recognition_result_GetAlternates( ISpeechRecognitionResult *iface, + UINT32 max_amount, + IVectorView_SpeechRecognitionResult **results ) +{ + IVector_IInspectable *vector; + struct vector_iids constraints_iids = + { + .iterable = &IID_IVectorView_SpeechRecognitionResult, + .iterator = &IID_IVectorView_SpeechRecognitionResult, + .vector = &IID_IVector_IInspectable, + .view = &IID_IVectorView_SpeechRecognitionResult, + }; + + FIXME("iface %p, max_amount %u, results %p stub!\n", iface, max_amount, results); + + vector_inspectable_create(&constraints_iids, (IVector_IInspectable **)&vector); + IVector_IInspectable_GetView(vector, (IVectorView_IInspectable **)results); + IVector_IInspectable_Release(vector); + return S_OK; +} + +static HRESULT WINAPI recognition_result_get_Constraint( ISpeechRecognitionResult *iface, ISpeechRecognitionConstraint **value ) +{ + struct recognition_result *impl = impl_from_ISpeechRecognitionResult(iface); + TRACE("iface %p, operation %p.\n", iface, value); + ISpeechRecognitionConstraint_AddRef((*value = impl->constraint)); + return S_OK; +} + +static HRESULT WINAPI recognition_result_get_RulePath( ISpeechRecognitionResult *iface, IVectorView_HSTRING **value ) +{ + FIXME("iface %p stub!\n", iface); + return E_NOTIMPL; +} + +static HRESULT WINAPI recognition_result_get_RawConfidence( ISpeechRecognitionResult *iface, DOUBLE *value ) +{ + FIXME("iface %p stub!\n", iface); + return E_NOTIMPL; +} + +static const struct ISpeechRecognitionResultVtbl recognition_result_vtbl = +{ + /* IUnknown methods */ + recognition_result_QueryInterface, + recognition_result_AddRef, + recognition_result_Release, + /* IInspectable methods */ + recognition_result_GetIids, + recognition_result_GetRuntimeClassName, + recognition_result_GetTrustLevel, + /* ISpeechRecognitionResult methods */ + recognition_result_get_Status, + recognition_result_get_Text, + recognition_result_get_Confidence, + recognition_result_get_SemanticInterpretation, + recognition_result_GetAlternates, + recognition_result_get_Constraint, + recognition_result_get_RulePath, + recognition_result_get_RawConfidence +}; + +DEFINE_IINSPECTABLE(recognition_result2, ISpeechRecognitionResult2, struct recognition_result, ISpeechRecognitionResult_iface) + +static HRESULT WINAPI recognition_result2_get_PhraseStartTime( ISpeechRecognitionResult2 *iface, DateTime *value ) +{ + DateTime dt = { .UniversalTime = 0 }; + FIXME("iface %p, value %p stub!\n", iface, value); + *value = dt; + return S_OK; +} + + +static HRESULT WINAPI recognition_result2_get_PhraseDuration( ISpeechRecognitionResult2 *iface, TimeSpan *value ) +{ + TimeSpan ts = { .Duration = 50000000LL }; /* Use 5 seconds as stub value. */ + FIXME("iface %p, value %p stub!\n", iface, value); + *value = ts; + return S_OK; +} + +static const struct ISpeechRecognitionResult2Vtbl recognition_result2_vtbl = +{ + /* IUnknown methods */ + recognition_result2_QueryInterface, + recognition_result2_AddRef, + recognition_result2_Release, + /* IInspectable methods */ + recognition_result2_GetIids, + recognition_result2_GetRuntimeClassName, + recognition_result2_GetTrustLevel, + /* ISpeechRecognitionResult2 methods */ + recognition_result2_get_PhraseStartTime, + recognition_result2_get_PhraseDuration +}; + +static HRESULT WINAPI recognition_result_create( ISpeechRecognitionConstraint *constraint, + HSTRING result_text, + ISpeechRecognitionResult **out ) +{ + struct recognition_result *impl; + + TRACE("out %p.\n", out); + + if (!(impl = calloc(1, sizeof(*impl)))) + { + *out = NULL; + return E_OUTOFMEMORY; + } + + impl->ISpeechRecognitionResult_iface.lpVtbl = &recognition_result_vtbl; + impl->ISpeechRecognitionResult2_iface.lpVtbl = &recognition_result2_vtbl; + impl->ref = 1; + + if (constraint) ISpeechRecognitionConstraint_AddRef((impl->constraint = constraint)); + WindowsDuplicateString(result_text, &impl->text); + + *out = &impl->ISpeechRecognitionResult_iface; + + TRACE("created %p.\n", *out); + + return S_OK; +} + +struct recognition_result_event_args +{ + ISpeechContinuousRecognitionResultGeneratedEventArgs ISpeechContinuousRecognitionResultGeneratedEventArgs_iface; + LONG ref; + + ISpeechRecognitionResult *result; +}; + +static inline struct recognition_result_event_args *impl_from_ISpeechContinuousRecognitionResultGeneratedEventArgs( ISpeechContinuousRecognitionResultGeneratedEventArgs *iface ) +{ + return CONTAINING_RECORD(iface, struct recognition_result_event_args, ISpeechContinuousRecognitionResultGeneratedEventArgs_iface); +} + +static HRESULT WINAPI recognition_result_event_args_QueryInterface( ISpeechContinuousRecognitionResultGeneratedEventArgs *iface, REFIID iid, void **out ) +{ + struct recognition_result_event_args *impl = impl_from_ISpeechContinuousRecognitionResultGeneratedEventArgs(iface); + + TRACE("iface %p, iid %s, out %p.\n", iface, debugstr_guid(iid), out); + + if (IsEqualGUID(iid, &IID_IUnknown) || + IsEqualGUID(iid, &IID_IInspectable) || + IsEqualGUID(iid, &IID_IAgileObject) || + IsEqualGUID(iid, &IID_ISpeechContinuousRecognitionResultGeneratedEventArgs)) + { + IInspectable_AddRef((*out = &impl->ISpeechContinuousRecognitionResultGeneratedEventArgs_iface)); + return S_OK; + } + + WARN("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(iid)); + *out = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI recognition_result_event_args_AddRef( ISpeechContinuousRecognitionResultGeneratedEventArgs *iface ) +{ + struct recognition_result_event_args *impl = impl_from_ISpeechContinuousRecognitionResultGeneratedEventArgs(iface); + ULONG ref = InterlockedIncrement(&impl->ref); + TRACE("iface %p, ref %lu.\n", iface, ref); + return ref; +} + +static ULONG WINAPI recognition_result_event_args_Release( ISpeechContinuousRecognitionResultGeneratedEventArgs *iface ) +{ + struct recognition_result_event_args *impl = impl_from_ISpeechContinuousRecognitionResultGeneratedEventArgs(iface); + + ULONG ref = InterlockedDecrement(&impl->ref); + TRACE("iface %p, ref %lu.\n", iface, ref); + + if (!ref) + { + if (impl->result) ISpeechRecognitionResult_Release(impl->result); + free(impl); + } + + return ref; +} + +static HRESULT WINAPI recognition_result_event_args_GetIids( ISpeechContinuousRecognitionResultGeneratedEventArgs *iface, ULONG *iid_count, IID **iids ) +{ + FIXME("iface %p, iid_count %p, iids %p stub!\n", iface, iid_count, iids); + return E_NOTIMPL; +} + +static HRESULT WINAPI recognition_result_event_args_GetRuntimeClassName( ISpeechContinuousRecognitionResultGeneratedEventArgs *iface, HSTRING *class_name ) +{ + FIXME("iface %p, class_name %p stub!\n", iface, class_name); + return E_NOTIMPL; +} + +static HRESULT WINAPI recognition_result_event_args_GetTrustLevel( ISpeechContinuousRecognitionResultGeneratedEventArgs *iface, TrustLevel *trust_level ) +{ + FIXME("iface %p, trust_level %p stub!\n", iface, trust_level); + return E_NOTIMPL; +} + +static HRESULT WINAPI recognition_result_event_args_get_Result( ISpeechContinuousRecognitionResultGeneratedEventArgs *iface, + ISpeechRecognitionResult **value ) +{ + struct recognition_result_event_args *impl = impl_from_ISpeechContinuousRecognitionResultGeneratedEventArgs(iface); + FIXME("iface %p value %p stub!\n", iface, value); + ISpeechRecognitionResult_AddRef((*value = impl->result)); + return S_OK; +} + +static const struct ISpeechContinuousRecognitionResultGeneratedEventArgsVtbl recognition_result_event_args_vtbl = +{ + /* IUnknown methods */ + recognition_result_event_args_QueryInterface, + recognition_result_event_args_AddRef, + recognition_result_event_args_Release, + /* IInspectable methods */ + recognition_result_event_args_GetIids, + recognition_result_event_args_GetRuntimeClassName, + recognition_result_event_args_GetTrustLevel, + /* ISpeechContinuousRecognitionResultGeneratedEventArgs methods */ + recognition_result_event_args_get_Result +}; + +static HRESULT WINAPI recognition_result_event_args_create( ISpeechRecognitionResult *result, + ISpeechContinuousRecognitionResultGeneratedEventArgs **out ) +{ + struct recognition_result_event_args *impl; + + TRACE("out %p.\n", out); + + if (!(impl = calloc(1, sizeof(*impl)))) + { + *out = NULL; + return E_OUTOFMEMORY; + } + + impl->ISpeechContinuousRecognitionResultGeneratedEventArgs_iface.lpVtbl = &recognition_result_event_args_vtbl; + impl->ref = 1; + if (result) ISpeechRecognitionResult_AddRef((impl->result = result)); + + *out = &impl->ISpeechContinuousRecognitionResultGeneratedEventArgs_iface; + + TRACE("created %p.\n", *out); + return S_OK; +} + /* * * ISpeechRecognitionCompilationResult @@ -282,7 +639,9 @@ static DWORD CALLBACK session_worker_thread_cb( void *args ) struct session *impl = impl_from_ISpeechContinuousRecognitionSession(iface); struct speech_get_recognition_result_params recognition_result_params; struct speech_recognize_audio_params recognize_audio_params; + ISpeechContinuousRecognitionResultGeneratedEventArgs *event_args; ISpeechRecognitionConstraint *constraint; + ISpeechRecognitionResult *result; BOOLEAN running = TRUE, paused = FALSE; UINT32 frame_count, tmp_buf_size; BYTE *audio_buf, *tmp_buf = NULL; @@ -408,8 +767,15 @@ static DWORD CALLBACK session_worker_thread_cb( void *args ) if (SUCCEEDED(hr = session_find_constraint_by_string(impl, recognized_text, &hstring, &constraint))) { - /* TODO: Send event. */ + recognition_result_create(constraint, hstring, &result); + recognition_result_event_args_create(result, &event_args); + + typed_event_handlers_notify(&impl->result_handlers, + (IInspectable *)&impl->ISpeechContinuousRecognitionSession_iface, + (IInspectable *)event_args); + ISpeechContinuousRecognitionResultGeneratedEventArgs_Release(event_args); + ISpeechRecognitionResult_Release(result); WindowsDeleteString(hstring); ISpeechRecognitionConstraint_Release(constraint); } From 1cb8e3da5210db0b9c2598399a6c9f31ed71fda4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20K=C3=B6lbl?= Date: Mon, 30 May 2022 18:37:41 +0200 Subject: [PATCH 113/454] windows.media.speech: Add ISpeechRecognitionSemanticInterpretation stub. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Bernhard Kölbl CW-Bug-Id: #20134 --- dlls/windows.media.speech/recognizer.c | 112 ++++++++++++++++++++++++- 1 file changed, 111 insertions(+), 1 deletion(-) diff --git a/dlls/windows.media.speech/recognizer.c b/dlls/windows.media.speech/recognizer.c index 1a07915eac8c..8e935d501a30 100644 --- a/dlls/windows.media.speech/recognizer.c +++ b/dlls/windows.media.speech/recognizer.c @@ -30,6 +30,116 @@ WINE_DEFAULT_DEBUG_CHANNEL(speech); +struct semantic_interpretation +{ + ISpeechRecognitionSemanticInterpretation ISpeechRecognitionSemanticInterpretation_iface; + LONG ref; +}; + +static inline struct semantic_interpretation *impl_from_ISpeechRecognitionSemanticInterpretation( ISpeechRecognitionSemanticInterpretation *iface ) +{ + return CONTAINING_RECORD(iface, struct semantic_interpretation, ISpeechRecognitionSemanticInterpretation_iface); +} + +HRESULT WINAPI semantic_interpretation_QueryInterface( ISpeechRecognitionSemanticInterpretation *iface, REFIID iid, void **out ) +{ + struct semantic_interpretation *impl = impl_from_ISpeechRecognitionSemanticInterpretation(iface); + + TRACE("iface %p, iid %s, out %p.\n", iface, debugstr_guid(iid), out); + + if (IsEqualGUID(iid, &IID_IUnknown) || + IsEqualGUID(iid, &IID_IInspectable) || + IsEqualGUID(iid, &IID_ISpeechRecognitionSemanticInterpretation)) + { + IInspectable_AddRef((*out = &impl->ISpeechRecognitionSemanticInterpretation_iface)); + return S_OK; + } + + WARN("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(iid)); + *out = NULL; + return E_NOINTERFACE; +} + +ULONG WINAPI semantic_interpretation_AddRef( ISpeechRecognitionSemanticInterpretation *iface ) +{ + struct semantic_interpretation *impl = impl_from_ISpeechRecognitionSemanticInterpretation(iface); + ULONG ref = InterlockedIncrement(&impl->ref); + TRACE("iface %p, ref %lu.\n", iface, ref); + return ref; +} + +ULONG WINAPI semantic_interpretation_Release( ISpeechRecognitionSemanticInterpretation *iface ) +{ + struct semantic_interpretation *impl = impl_from_ISpeechRecognitionSemanticInterpretation(iface); + + ULONG ref = InterlockedDecrement(&impl->ref); + TRACE("iface %p, ref %lu.\n", iface, ref); + + if(!ref) + free(impl); + + return ref; +} + +HRESULT WINAPI semantic_interpretation_GetIids( ISpeechRecognitionSemanticInterpretation *iface, ULONG *iid_count, IID **iids ) +{ + FIXME("iface %p, iid_count %p, iids %p stub!\n", iface, iid_count, iids); + return E_NOTIMPL; +} + +HRESULT WINAPI semantic_interpretation_GetRuntimeClassName( ISpeechRecognitionSemanticInterpretation *iface, HSTRING *class_name ) +{ + FIXME("iface %p, class_name %p stub!\n", iface, class_name); + return E_NOTIMPL; +} + +HRESULT WINAPI semantic_interpretation_GetTrustLevel( ISpeechRecognitionSemanticInterpretation *iface, TrustLevel *trust_level ) +{ + FIXME("iface %p, trust_level %p stub!\n", iface, trust_level); + return E_NOTIMPL; +} + +HRESULT WINAPI semantic_interpretation_get_Properties( ISpeechRecognitionSemanticInterpretation *iface, IMapView_HSTRING_IVectorView_HSTRING **value ) +{ + FIXME("iface %p stub!\n", iface); + return E_NOTIMPL; +} + +static const struct ISpeechRecognitionSemanticInterpretationVtbl semantic_interpretation_vtbl = +{ + /* IUnknown methods */ + semantic_interpretation_QueryInterface, + semantic_interpretation_AddRef, + semantic_interpretation_Release, + /* IInspectable methods */ + semantic_interpretation_GetIids, + semantic_interpretation_GetRuntimeClassName, + semantic_interpretation_GetTrustLevel, + /* ISpeechRecognitionSemanticInterpretation methods */ + semantic_interpretation_get_Properties +}; + + +static HRESULT semantic_interpretation_create( ISpeechRecognitionSemanticInterpretation **out ) +{ + struct semantic_interpretation *impl; + + TRACE("out %p.\n", out); + + if (!(impl = calloc(1, sizeof(*impl)))) + { + *out = NULL; + return E_OUTOFMEMORY; + } + + impl->ISpeechRecognitionSemanticInterpretation_iface.lpVtbl = &semantic_interpretation_vtbl; + impl->ref = 1; + + *out = &impl->ISpeechRecognitionSemanticInterpretation_iface; + TRACE("created %p\n", *out); + return S_OK; +} + struct recognition_result { ISpeechRecognitionResult ISpeechRecognitionResult_iface; @@ -139,7 +249,7 @@ static HRESULT WINAPI recognition_result_get_SemanticInterpretation( ISpeechReco ISpeechRecognitionSemanticInterpretation **value ) { FIXME("iface %p, operation %p stub!\n", iface, value); - return E_NOTIMPL; + return semantic_interpretation_create(value); } static HRESULT WINAPI recognition_result_GetAlternates( ISpeechRecognitionResult *iface, From 52b17badb7aaa8514cd83c4e26b99b1462247fab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 20 Feb 2024 09:40:32 +0100 Subject: [PATCH 114/454] HACK: windows.media.speech: Stub semantic_interpretation_get_Properties. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Bernhard Kölbl CW-Bug-Id: #20134 --- dlls/windows.media.speech/recognizer.c | 134 ++++++++++++++++++++++++- 1 file changed, 133 insertions(+), 1 deletion(-) diff --git a/dlls/windows.media.speech/recognizer.c b/dlls/windows.media.speech/recognizer.c index 8e935d501a30..31c537973a3f 100644 --- a/dlls/windows.media.speech/recognizer.c +++ b/dlls/windows.media.speech/recognizer.c @@ -30,6 +30,138 @@ WINE_DEFAULT_DEBUG_CHANNEL(speech); +struct map_view_hstring_vector_view_hstring +{ + IMapView_HSTRING_IVectorView_HSTRING IMapView_HSTRING_IVectorView_HSTRING_iface; + LONG ref; +}; + +static inline struct map_view_hstring_vector_view_hstring *impl_from_IMapView_HSTRING_IVectorView_HSTRING( IMapView_HSTRING_IVectorView_HSTRING *iface ) +{ + return CONTAINING_RECORD(iface, struct map_view_hstring_vector_view_hstring, IMapView_HSTRING_IVectorView_HSTRING_iface); +} + +HRESULT WINAPI map_view_hstring_vector_view_hstring_QueryInterface( IMapView_HSTRING_IVectorView_HSTRING *iface, REFIID iid, void **out ) +{ + struct map_view_hstring_vector_view_hstring *impl = impl_from_IMapView_HSTRING_IVectorView_HSTRING(iface); + + TRACE("iface %p, iid %s, out %p.\n", iface, debugstr_guid(iid), out); + + if (IsEqualGUID(iid, &IID_IUnknown) || + IsEqualGUID(iid, &IID_IInspectable) || + IsEqualGUID(iid, &IID_IMapView_HSTRING_IVectorView_HSTRING)) + { + IInspectable_AddRef((*out = &impl->IMapView_HSTRING_IVectorView_HSTRING_iface)); + return S_OK; + } + + WARN("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(iid)); + *out = NULL; + return E_NOINTERFACE; +} + +ULONG WINAPI map_view_hstring_vector_view_hstring_AddRef( IMapView_HSTRING_IVectorView_HSTRING *iface ) +{ + struct map_view_hstring_vector_view_hstring *impl = impl_from_IMapView_HSTRING_IVectorView_HSTRING(iface); + ULONG ref = InterlockedIncrement(&impl->ref); + TRACE("iface %p, ref %lu.\n", iface, ref); + return ref; +} + +ULONG WINAPI map_view_hstring_vector_view_hstring_Release( IMapView_HSTRING_IVectorView_HSTRING *iface ) +{ + struct map_view_hstring_vector_view_hstring *impl = impl_from_IMapView_HSTRING_IVectorView_HSTRING(iface); + + ULONG ref = InterlockedDecrement(&impl->ref); + TRACE("iface %p, ref %lu.\n", iface, ref); + + if(!ref) + free(impl); + + return ref; +} + +HRESULT WINAPI map_view_hstring_vector_view_hstring_GetIids( IMapView_HSTRING_IVectorView_HSTRING *iface, ULONG *iidCount, IID **iids ) +{ + FIXME("iface %p stub!\n", iface); + return E_NOTIMPL; +} + +HRESULT WINAPI map_view_hstring_vector_view_hstring_GetRuntimeClassName( IMapView_HSTRING_IVectorView_HSTRING *iface, HSTRING *className ) +{ + FIXME("iface %p stub!\n", iface); + return E_NOTIMPL; +} + +HRESULT WINAPI map_view_hstring_vector_view_hstring_GetTrustLevel( IMapView_HSTRING_IVectorView_HSTRING *iface, TrustLevel *trustLevel ) +{ + FIXME("iface %p stub!\n", iface); + return E_NOTIMPL; +} + +HRESULT WINAPI map_view_hstring_vector_view_hstring_Lookup( IMapView_HSTRING_IVectorView_HSTRING *iface, HSTRING key, IVectorView_HSTRING **value ) +{ + FIXME("iface %p stub!\n", iface); + return E_NOTIMPL; +} + +HRESULT WINAPI map_view_hstring_vector_view_hstring_get_Size( IMapView_HSTRING_IVectorView_HSTRING *iface, unsigned int *size ) +{ + FIXME("iface %p stub!\n", iface); + *size = 0; + return S_OK; +} + +HRESULT WINAPI map_view_hstring_vector_view_hstring_HasKey( IMapView_HSTRING_IVectorView_HSTRING *iface, HSTRING key, boolean *found ) +{ + FIXME("iface %p stub!\n", iface); + return E_NOTIMPL; +} + +HRESULT WINAPI map_view_hstring_vector_view_hstring_Split( IMapView_HSTRING_IVectorView_HSTRING *iface, IMapView_HSTRING_IVectorView_HSTRING **first, IMapView_HSTRING_IVectorView_HSTRING **second ) +{ + FIXME("iface %p stub!\n", iface); + return E_NOTIMPL; +} + +static const struct IMapView_HSTRING_IVectorView_HSTRINGVtbl map_view_hstring_vector_view_hstring_vtbl = +{ + /* IUnknown methods */ + map_view_hstring_vector_view_hstring_QueryInterface, + map_view_hstring_vector_view_hstring_AddRef, + map_view_hstring_vector_view_hstring_Release, + /* IInspectable methods */ + map_view_hstring_vector_view_hstring_GetIids, + map_view_hstring_vector_view_hstring_GetRuntimeClassName, + map_view_hstring_vector_view_hstring_GetTrustLevel, + /* IMapView* > methods */ + map_view_hstring_vector_view_hstring_Lookup, + map_view_hstring_vector_view_hstring_get_Size, + map_view_hstring_vector_view_hstring_HasKey, + map_view_hstring_vector_view_hstring_Split +}; + + +static HRESULT map_view_hstring_vector_view_hstring_create( IMapView_HSTRING_IVectorView_HSTRING **out ) +{ + struct map_view_hstring_vector_view_hstring *impl; + + TRACE("out %p.\n", out); + + if (!(impl = calloc(1, sizeof(*impl)))) + { + *out = NULL; + return E_OUTOFMEMORY; + } + + impl->IMapView_HSTRING_IVectorView_HSTRING_iface.lpVtbl = &map_view_hstring_vector_view_hstring_vtbl; + impl->ref = 1; + + *out = &impl->IMapView_HSTRING_IVectorView_HSTRING_iface; + TRACE("created %p\n", *out); + return S_OK; +} + struct semantic_interpretation { ISpeechRecognitionSemanticInterpretation ISpeechRecognitionSemanticInterpretation_iface; @@ -102,7 +234,7 @@ HRESULT WINAPI semantic_interpretation_GetTrustLevel( ISpeechRecognitionSemantic HRESULT WINAPI semantic_interpretation_get_Properties( ISpeechRecognitionSemanticInterpretation *iface, IMapView_HSTRING_IVectorView_HSTRING **value ) { FIXME("iface %p stub!\n", iface); - return E_NOTIMPL; + return map_view_hstring_vector_view_hstring_create(value); } static const struct ISpeechRecognitionSemanticInterpretationVtbl semantic_interpretation_vtbl = From abf39a8dd7916fc13b6f6b4cda2dd3c0a7639665 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20K=C3=B6lbl?= Date: Tue, 28 Feb 2023 16:11:36 +0100 Subject: [PATCH 115/454] WIP: windows.media.speech: Implement grammar. CW-Bug-Id: #20134 --- dlls/windows.media.speech/recognizer.c | 106 ++++++++++++++++++++++++- dlls/windows.media.speech/unixlib.c | 61 +++++++++++++- dlls/windows.media.speech/unixlib.h | 2 + 3 files changed, 164 insertions(+), 5 deletions(-) diff --git a/dlls/windows.media.speech/recognizer.c b/dlls/windows.media.speech/recognizer.c index 31c537973a3f..f97fa5bc0b41 100644 --- a/dlls/windows.media.speech/recognizer.c +++ b/dlls/windows.media.speech/recognizer.c @@ -1489,7 +1489,7 @@ static HRESULT WINAPI recognizer_get_UIOptions( ISpeechRecognizer *iface, ISpeec return E_NOTIMPL; } -static HRESULT recognizer_create_unix_instance( struct session *session ) +static HRESULT recognizer_create_unix_instance( struct session *session, const char **grammar, UINT32 grammar_size ) { struct speech_create_recognizer_params create_params = { 0 }; WCHAR locale[LOCALE_NAME_MAX_LENGTH]; @@ -1506,6 +1506,8 @@ static HRESULT recognizer_create_unix_instance( struct session *session ) return HRESULT_FROM_WIN32(GetLastError()); create_params.sample_rate = (FLOAT)session->capture_wfx.nSamplesPerSec; + create_params.grammar = grammar; + create_params.grammar_size = grammar_size; if ((status = WINE_UNIX_CALL(unix_speech_create_recognizer, &create_params))) { @@ -1522,11 +1524,109 @@ static HRESULT recognizer_compile_constraints_async( IInspectable *invoker, IIns { struct recognizer *impl = impl_from_ISpeechRecognizer((ISpeechRecognizer *)invoker); struct session *session = impl_from_ISpeechContinuousRecognitionSession(impl->session); + struct speech_release_recognizer_params release_params; + ISpeechRecognitionListConstraint *list_constraint; + IIterable_IInspectable *constraints_iterable; + IIterator_IInspectable *constraints_iterator; + ISpeechRecognitionConstraint *constraint; + IIterable_HSTRING *commands_iterable; + IIterator_HSTRING *commands_iterator; + BOOLEAN has_constraint, has_command; + IVector_HSTRING *commands; + const WCHAR *command_str; + UINT32 grammar_size = 0, i = 0; + char **grammar = NULL; + HSTRING command; + UINT32 size = 0; HRESULT hr; - if (FAILED(hr = recognizer_create_unix_instance(session))) + if (FAILED(hr = IVector_ISpeechRecognitionConstraint_QueryInterface(session->constraints, &IID_IIterable_ISpeechRecognitionConstraint, (void **)&constraints_iterable))) + return hr; + + if (FAILED(hr = IIterable_IInspectable_First(constraints_iterable, &constraints_iterator))) + { + IIterable_IInspectable_Release(constraints_iterable); + return hr; + } + + for (hr = IIterator_IInspectable_get_HasCurrent(constraints_iterator, &has_constraint); SUCCEEDED(hr) && has_constraint; hr = IIterator_IInspectable_MoveNext(constraints_iterator, &has_constraint)) + { + list_constraint = NULL; + commands_iterable = NULL; + commands_iterator = NULL; + commands = NULL; + + if (FAILED(IIterator_IInspectable_get_Current(constraints_iterator, (IInspectable **)&constraint))) + goto skip; + + if (FAILED(ISpeechRecognitionConstraint_QueryInterface(constraint, &IID_ISpeechRecognitionListConstraint, (void**)&list_constraint))) + goto skip; + + if (FAILED(ISpeechRecognitionListConstraint_get_Commands(list_constraint, &commands))) + goto skip; + + if (FAILED(IVector_HSTRING_QueryInterface(commands, &IID_IIterable_HSTRING, (void **)&commands_iterable))) + goto skip; + + if (FAILED(IIterable_HSTRING_First(commands_iterable, &commands_iterator))) + goto skip; + + if (FAILED(IVector_HSTRING_get_Size(commands, &size))) + goto skip; + + grammar_size += size; + grammar = realloc(grammar, grammar_size * sizeof(char *)); + + for (hr = IIterator_HSTRING_get_HasCurrent(commands_iterator, &has_command); SUCCEEDED(hr) && has_command; hr = IIterator_HSTRING_MoveNext(commands_iterator, &has_command)) + { + if (FAILED(IIterator_HSTRING_get_Current(commands_iterator, &command))) + continue; + + command_str = WindowsGetStringRawBuffer(command, NULL); + + if (command_str) + { + WCHAR *wstr = wcsdup(command_str); + size_t len = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, grammar[i], 0, NULL, NULL); + grammar[i] = malloc(len * sizeof(char)); + + CharLowerW(wstr); + WideCharToMultiByte(CP_UTF8, 0, wstr, -1, grammar[i], len, NULL, NULL); + free(wstr); + i++; + } + + WindowsDeleteString(command); + } + +skip: + if (commands_iterator) IIterator_HSTRING_Release(commands_iterator); + if (commands_iterable) IIterable_HSTRING_Release(commands_iterable); + if (commands) IVector_HSTRING_Release(commands); + + if (list_constraint) ISpeechRecognitionListConstraint_Release(list_constraint); + if (constraint) ISpeechRecognitionConstraint_Release(constraint); + } + + IIterator_IInspectable_Release(constraints_iterator); + IIterable_IInspectable_Release(constraints_iterable); + + if (session->unix_handle) + { + release_params.handle = session->unix_handle; + WINE_UNIX_CALL(unix_speech_release_recognizer, &release_params); + session->unix_handle = 0; + } + + hr = recognizer_create_unix_instance(session, (const char **)grammar, grammar_size); + + for(i = 0; i < grammar_size; ++i) + free(grammar[i]); + free(grammar); + + if (FAILED(hr)) { - WARN("Failed to created recognizer instance.\n"); + WARN("Failed to created recognizer instance with grammar.\n"); return compilation_result_create(SpeechRecognitionResultStatus_GrammarCompilationFailure, (ISpeechRecognitionCompilationResult **) result); } else return compilation_result_create(SpeechRecognitionResultStatus_Success, (ISpeechRecognitionCompilationResult **) result); diff --git a/dlls/windows.media.speech/unixlib.c b/dlls/windows.media.speech/unixlib.c index c41bb4cc445a..d8f59b99b761 100644 --- a/dlls/windows.media.speech/unixlib.c +++ b/dlls/windows.media.speech/unixlib.c @@ -58,6 +58,7 @@ static void *vosk_handle; MAKE_FUNCPTR(vosk_model_new); MAKE_FUNCPTR(vosk_model_free); MAKE_FUNCPTR(vosk_recognizer_new); +MAKE_FUNCPTR(vosk_recognizer_new_grm); MAKE_FUNCPTR(vosk_recognizer_free); MAKE_FUNCPTR(vosk_recognizer_accept_waveform); MAKE_FUNCPTR(vosk_recognizer_final_result); @@ -80,6 +81,8 @@ static NTSTATUS process_attach( void *args ) goto error; \ } LOAD_FUNCPTR(vosk_model_new) + LOAD_FUNCPTR(vosk_recognizer_new) + LOAD_FUNCPTR(vosk_recognizer_new_grm) LOAD_FUNCPTR(vosk_model_free) LOAD_FUNCPTR(vosk_recognizer_new) LOAD_FUNCPTR(vosk_recognizer_free) @@ -225,12 +228,58 @@ static NTSTATUS find_model_by_locale( const char *locale, VoskModel **model ) return status; } +static NTSTATUS grammar_to_json_array(const char **grammar, UINT32 grammar_size, const char **array) +{ + size_t buf_size = strlen("[]") + 1, len; + char *buf; + UINT32 i; + + for (i = 0; i < grammar_size; ++i) + { + buf_size += strlen(grammar[i]) + 4; /* (4) - two double quotes, a comma and a space */ + } + + if (!(buf = malloc(buf_size))) + return STATUS_NO_MEMORY; + + *array = buf; + + *buf = '['; + buf++; + + for (i = 0; i < grammar_size; ++i) + { + *buf = '\"'; + buf++; + len = strlen(grammar[i]); + memcpy(buf, grammar[i], len); + buf += len; + *buf = '\"'; + buf++; + if (i < (grammar_size - 1)) + { + *buf = ','; + buf++; + *buf = ' '; + buf++; + } + } + + *buf = ']'; + buf++; + *buf = '\0'; + + TRACE("created json array %s.\n", debugstr_a(*array)); + return STATUS_SUCCESS; +} + static NTSTATUS speech_create_recognizer( void *args ) { struct speech_create_recognizer_params *params = args; VoskRecognizer *recognizer = NULL; VoskModel *model = NULL; NTSTATUS status = STATUS_SUCCESS; + const char *grammar_json; TRACE("args %p.\n", args); @@ -240,8 +289,16 @@ static NTSTATUS speech_create_recognizer( void *args ) if ((status = find_model_by_locale(params->locale, &model))) return status; - if (!(recognizer = p_vosk_recognizer_new(model, params->sample_rate))) - status = STATUS_UNSUCCESSFUL; + if (params->grammar && grammar_to_json_array(params->grammar, params->grammar_size, &grammar_json) == STATUS_SUCCESS) + { + if (!(recognizer = p_vosk_recognizer_new_grm(model, params->sample_rate, grammar_json))) + status = STATUS_UNSUCCESSFUL; + } + else + { + if (!(recognizer = p_vosk_recognizer_new(model, params->sample_rate))) + status = STATUS_UNSUCCESSFUL; + } /* VoskModel is reference-counted. A VoskRecognizer keeps a reference to its model. */ p_vosk_model_free(model); diff --git a/dlls/windows.media.speech/unixlib.h b/dlls/windows.media.speech/unixlib.h index a07d76705d53..ad2fab738b99 100644 --- a/dlls/windows.media.speech/unixlib.h +++ b/dlls/windows.media.speech/unixlib.h @@ -37,6 +37,8 @@ struct speech_create_recognizer_params speech_recognizer_handle handle; CHAR locale[LOCALE_NAME_MAX_LENGTH]; FLOAT sample_rate; + const char **grammar; + unsigned int grammar_size; }; struct speech_release_recognizer_params From cc055ec804b3fb00753cd47773cc3d0ebcb726ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20K=C3=B6lbl?= Date: Tue, 28 Feb 2023 22:06:23 +0100 Subject: [PATCH 116/454] HACK: windows.media.speech/tests: Add tests for manual recognition testing. Speak during the delay, to test the code. CW-Bug-Id: #20134 --- dlls/windows.media.speech/tests/speech.c | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/dlls/windows.media.speech/tests/speech.c b/dlls/windows.media.speech/tests/speech.c index 743955ab9bd9..a6c7d56e1e13 100644 --- a/dlls/windows.media.speech/tests/speech.c +++ b/dlls/windows.media.speech/tests/speech.c @@ -202,7 +202,23 @@ HRESULT WINAPI recognition_result_handler_Invoke( IHandler_RecognitionResult *if ISpeechContinuousRecognitionSession *sender, ISpeechContinuousRecognitionResultGeneratedEventArgs *args ) { - trace("iface %p, sender %p, args %p.\n", iface, sender, args); + ISpeechRecognitionResult *result; + HSTRING hstring; + HRESULT hr; + + if (!args) return S_OK; + + hr = ISpeechContinuousRecognitionResultGeneratedEventArgs_get_Result(args, &result); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + + hr = ISpeechRecognitionResult_get_Text(result, &hstring); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + + trace("iface %p, sender %p, args %p, text %s.\n", iface, sender, args, debugstr_w(WindowsGetStringRawBuffer(hstring, NULL))); + + WindowsDeleteString(hstring); + ISpeechRecognitionResult_Release(result); + return S_OK; } @@ -1702,7 +1718,7 @@ static void test_Recognition(void) static const WCHAR *list_constraint_name = L"Windows.Media.SpeechRecognition.SpeechRecognitionListConstraint"; static const WCHAR *recognizer_name = L"Windows.Media.SpeechRecognition.SpeechRecognizer"; static const WCHAR *speech_constraint_tag = L"test_message"; - static const WCHAR *speech_constraints[] = { L"This is a test.", L"Number 5!", L"What time is it?" }; + static const WCHAR *speech_constraints[] = { L"This is a test", L"Number 5", L"What time is it" }; ISpeechRecognitionListConstraintFactory *listconstraint_factory = NULL; IAsyncOperation_SpeechRecognitionCompilationResult *operation = NULL; IVector_ISpeechRecognitionConstraint *constraints = NULL; @@ -1865,6 +1881,8 @@ static void test_Recognition(void) ok(hr == S_OK, "ISpeechRecognizer2_get_State failed, hr %#lx.\n", hr); ok(recog_state == SpeechRecognizerState_Capturing || broken(recog_state == SpeechRecognizerState_Idle), "recog_state was %u.\n", recog_state); + + Sleep(10000); /* * TODO: Use a loopback device together with prerecorded audio files to test the recognizer's functionality. */ From 7caaea1611496b5d7b91a27b25ce595e60d2cce8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20K=C3=B6lbl?= Date: Tue, 7 Mar 2023 15:31:08 +0100 Subject: [PATCH 117/454] HACK: windows.media.speech: Load Vosk models from Phasmophobia. CW-Bug-Id: #20134 --- dlls/windows.media.speech/unixlib.c | 67 +++++++++++++++++++++++++++-- 1 file changed, 64 insertions(+), 3 deletions(-) diff --git a/dlls/windows.media.speech/unixlib.c b/dlls/windows.media.speech/unixlib.c index d8f59b99b761..33f16e652e83 100644 --- a/dlls/windows.media.speech/unixlib.c +++ b/dlls/windows.media.speech/unixlib.c @@ -119,13 +119,55 @@ static inline VoskRecognizer *vosk_recognizer_from_handle( speech_recognizer_han return (VoskRecognizer *)(UINT_PTR)handle; } +static const char* map_lang_to_phasmophobia_dir(const char* lang, size_t len) +{ + if (!strncmp(lang, "ar", len)) + return "Arabic"; + if (!strncmp(lang, "ca", len)) + return "Catalan"; + if (!strncmp(lang, "zn", len)) + return "Chinese"; + if (!strncmp(lang, "cs", len)) + return "Czech"; + if (!strncmp(lang, "nl", len)) + return "Dutch"; + if (!strncmp(lang, "en", len)) + return "English"; + if (!strncmp(lang, "fr", len)) + return "French"; + if (!strncmp(lang, "de", len)) + return "German"; + if (!strncmp(lang, "de", len)) + return "German"; + if (!strncmp(lang, "el", len)) + return "Greek"; + if (!strncmp(lang, "it", len)) + return "Italian"; + if (!strncmp(lang, "ja", len)) + return "Japanese"; + if (!strncmp(lang, "pt", len)) + return "Portuguese"; + if (!strncmp(lang, "ru", len)) + return "Russian"; + if (!strncmp(lang, "es", len)) + return "Spanish"; + if (!strncmp(lang, "sw", len)) + return "Swedish"; + if (!strncmp(lang, "tr", len)) + return "Turkish"; + if (!strncmp(lang, "uk", len)) + return "Ukrainian"; + + return ""; +} + static NTSTATUS find_model_by_locale_and_path( const char *path, const char *locale, VoskModel **model ) { static const char *vosk_model_identifier_small = "vosk-model-small-"; static const char *vosk_model_identifier = "vosk-model-"; size_t ident_small_len = strlen(vosk_model_identifier_small); size_t ident_len = strlen(vosk_model_identifier); - char *ent_name, *model_path, *best_match, *delim; + char *ent_name, *model_path, *best_match, *delim, *appid = getenv("SteamAppId"), *str = NULL; NTSTATUS status = STATUS_UNSUCCESSFUL; struct dirent *dirent; size_t path_len, len; @@ -152,7 +194,7 @@ static NTSTATUS find_model_by_locale_and_path( const char *path, const char *loc ent_name += ident_small_len; else if (!strncmp(ent_name, vosk_model_identifier, ident_len)) ent_name += ident_len; - else + else if (strcmp(appid, "739630") != 0) continue; /* @@ -168,6 +210,12 @@ static NTSTATUS find_model_by_locale_and_path( const char *path, const char *loc if (!best_match && !strncmp(ent_name, locale, delim - locale)) best_match = strdup(dirent->d_name); + + if (!best_match && !strcmp(appid, "739630")) + { + if ((str = (char *)map_lang_to_phasmophobia_dir(locale, delim - locale))) + best_match = strdup(str); + } } closedir(dir); @@ -198,7 +246,7 @@ static NTSTATUS find_model_by_locale_and_path( const char *path, const char *loc static NTSTATUS find_model_by_locale( const char *locale, VoskModel **model ) { const char *suffix = NULL; - char *env, *path = NULL; + char *env, *path = NULL, *appid = getenv("SteamAppId"); NTSTATUS status; TRACE("locale %s, model %p.\n", debugstr_a(locale), model); @@ -225,6 +273,19 @@ static NTSTATUS find_model_by_locale( const char *locale, VoskModel **model ) status = find_model_by_locale_and_path(path, locale, model); free(path); + /* Hack to load Vosk models from Phasmophobia, so they don't need to be downloaded separately.*/ + if (status && appid && !strcmp(appid, "739630") && (env = getenv("PWD"))) + { + suffix = "/Phasmophobia_Data/StreamingAssets/LanguageModels"; + + if (!(path = malloc(strlen(env) + strlen(suffix) + 1))) + return STATUS_NO_MEMORY; + + sprintf(path, "%s%s", env, suffix); + status = find_model_by_locale_and_path(path, locale, model); + free(path); + } + return status; } From 7f6ab1d69da3e32ebcf5d8f28a424399e82941a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20K=C3=B6lbl?= Date: Tue, 7 Mar 2023 16:36:36 +0100 Subject: [PATCH 118/454] windows.media.speech: Suppress verbose Unixlib traces. CW-Bug-Id: #20134 --- dlls/windows.media.speech/unixlib.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/dlls/windows.media.speech/unixlib.c b/dlls/windows.media.speech/unixlib.c index 33f16e652e83..55e48a550ff1 100644 --- a/dlls/windows.media.speech/unixlib.c +++ b/dlls/windows.media.speech/unixlib.c @@ -330,7 +330,6 @@ static NTSTATUS grammar_to_json_array(const char **grammar, UINT32 grammar_size, buf++; *buf = '\0'; - TRACE("created json array %s.\n", debugstr_a(*array)); return STATUS_SUCCESS; } @@ -408,8 +407,6 @@ static NTSTATUS speech_get_recognition_result( void* args ) static char *last_result = NULL; const char *tmp = NULL; - TRACE("args %p.\n", args); - if (!vosk_handle) return STATUS_NOT_SUPPORTED; @@ -430,7 +427,6 @@ static NTSTATUS speech_get_recognition_result( void* args ) last_result = (char *)tmp; last_result_len = strlen(last_result); - TRACE("last_result %s.\n", debugstr_a(last_result)); } else return STATUS_NOT_FOUND; } From c8cdf7ebd012126cf26d568bf1bae5dd5786e372 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 21 Feb 2025 09:53:34 -0600 Subject: [PATCH 119/454] winevulkan: Filter out duplicate structures in VkDeviceCreateInfo chain. CW-Bug-Id: #24917 --- dlls/winevulkan/vulkan.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/dlls/winevulkan/vulkan.c b/dlls/winevulkan/vulkan.c index 3ade38428c3b..ba8359226a41 100644 --- a/dlls/winevulkan/vulkan.c +++ b/dlls/winevulkan/vulkan.c @@ -1111,6 +1111,29 @@ VkResult wine_vkAllocateCommandBuffers(VkDevice client_device, const VkCommandBu return res; } +static void filter_duplicate_structures(const VkBaseInStructure **in) +{ + const VkBaseInStructure *h; + VkBaseInStructure **h2; + + for (h = *in; h; h = h->pNext) + { + if (h->sType == 1000284001 /*VK_STRUCTURE_TYPE_DEVICE_DEVICE_MEMORY_REPORT_CREATE_INFO_EXT*/ + || h->sType == VK_STRUCTURE_TYPE_DEVICE_PRIVATE_DATA_CREATE_INFO) + continue; + + for (h2 = (VkBaseInStructure **)in; *h2 != h; h2 = (VkBaseInStructure **)&(*h2)->pNext) + { + if ((*h2)->sType == h->sType) + { + ERR("Duplicate sType %d in the chain, keeping the last.\n", h->sType); + *h2 = (VkBaseInStructure *)(*h2)->pNext; + break; + } + } + } +} + VkResult wine_vkCreateDevice(VkPhysicalDevice client_physical_device, const VkDeviceCreateInfo *create_info, const VkAllocationCallbacks *allocator, VkDevice *ret, void *client_ptr) { @@ -1129,6 +1152,8 @@ VkResult wine_vkCreateDevice(VkPhysicalDevice client_physical_device, const VkDe void *native_create_device_context = NULL; VkCreateInfoWineDeviceCallback *callback; + filter_duplicate_structures((const VkBaseInStructure **)&create_info); + if (allocator) FIXME("Support for allocation callbacks not implemented yet\n"); From e199197d52a7f4282b226c062f8af69e7eb83fd5 Mon Sep 17 00:00:00 2001 From: Brendan McGrath Date: Fri, 10 May 2024 15:57:19 +1000 Subject: [PATCH 120/454] winegstreamer: Don't send stream_start in PULL mode. This fixes a deadlock that can occur when the stream-start is dropped by the demuxer when it is in PULL mode. CW-Bug-Id: #22403 --- dlls/winegstreamer/media-converter/videoconv.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/dlls/winegstreamer/media-converter/videoconv.c b/dlls/winegstreamer/media-converter/videoconv.c index d8b77e5e1726..4fe0805db864 100644 --- a/dlls/winegstreamer/media-converter/videoconv.c +++ b/dlls/winegstreamer/media-converter/videoconv.c @@ -725,7 +725,9 @@ static gboolean video_conv_push_stream_start(VideoConv *conv, struct fozdb_hash { struct video_conv_state *state; - push_event(conv->src_pad, gst_event_new_stream_start(format_hash(hash))); + /* we don't send a stream-start in pull mode, as it can cause a deadlock */ + if (conv->active_mode == GST_PAD_MODE_PUSH) + push_event(conv->src_pad, gst_event_new_stream_start(format_hash(hash))); if (!(state = video_conv_lock_state(conv))) { @@ -1165,6 +1167,19 @@ static gboolean video_conv_src_query(GstPad *pad, GstObject *parent, GstQuery *q gst_query_set_duration(query, GST_FORMAT_BYTES, duration); return true; + case GST_QUERY_URI: + if (!gst_pad_query_default(pad, parent, query)) + { + if (!(state = video_conv_lock_state(conv))) + return false; + /* if we don't already have a uri, we will use the hash. This is to ensure + * downstream will use a consistent stream-id */ + gst_query_set_uri(query, format_hash(&state->transcode_hash)); + pthread_mutex_unlock(&conv->state_mutex); + GST_LOG_OBJECT(pad, "Responding with %" GST_PTR_FORMAT, query); + } + return true; + default: return gst_pad_query_default(pad, parent, query); } From 5004c6f8bd8aafe4e4d062124b1372e7c2ba5e24 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 10 Jul 2024 16:41:20 -0600 Subject: [PATCH 121/454] ntoskrnl.exe: Open directory object with nonzero access in test_permanent(). CW-Bug-Id: #24016 --- dlls/ntoskrnl.exe/tests/driver.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dlls/ntoskrnl.exe/tests/driver.c b/dlls/ntoskrnl.exe/tests/driver.c index f82e34fca3bd..d3e6fea2debb 100644 --- a/dlls/ntoskrnl.exe/tests/driver.c +++ b/dlls/ntoskrnl.exe/tests/driver.c @@ -2289,7 +2289,7 @@ static void test_permanence(void) ok(!status, "got %#lx\n", status); attr.Attributes = 0; - status = ZwOpenDirectoryObject( &handle, 0, &attr ); + status = ZwOpenDirectoryObject( &handle, GENERIC_ALL, &attr ); ok(!status, "got %#lx\n", status); status = ZwMakeTemporaryObject( handle ); todo_wine @@ -2307,7 +2307,7 @@ static void test_permanence(void) todo_wine ok(!status, "got %#lx\n", status); attr.Attributes = OBJ_PERMANENT; - status = ZwOpenDirectoryObject( &handle2, 0, &attr ); + status = ZwOpenDirectoryObject( &handle2, GENERIC_ALL, &attr ); ok(status == STATUS_SUCCESS, "got %#lx\n", status); status = ZwClose( handle2 ); ok(!status, "got %#lx\n", status); From fff2ad8683a813f6286f302ee609dd19f47c6158 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 12 Jul 2024 14:02:54 -0600 Subject: [PATCH 122/454] mshtml: Zero output iface on failure in get_document_node(). MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Suggested by Gabriel Ivăncescu. CW-Bug-Id: #24032 --- dlls/mshtml/htmldoc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/dlls/mshtml/htmldoc.c b/dlls/mshtml/htmldoc.c index 7152c1eeb7fd..261eaecb332a 100644 --- a/dlls/mshtml/htmldoc.c +++ b/dlls/mshtml/htmldoc.c @@ -5973,6 +5973,7 @@ HRESULT get_document_node(nsIDOMDocument *dom_document, HTMLDocumentNode **ret) if(!node) { ERR("document not initialized\n"); + *ret = NULL; return E_FAIL; } From 7e509dd7f809978e28d56b54762ac327289f2b4e Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Mon, 4 Nov 2024 11:22:18 -0600 Subject: [PATCH 123/454] kernelbase: Handle missing PROCESS_QUERY_INFORMATION access right in WriteProcessMemory(). From a diff attached to https://gitlab.winehq.org/wine/wine/-/merge_requests/6761. CW-Bug-Id: #24430 --- dlls/kernelbase/memory.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/dlls/kernelbase/memory.c b/dlls/kernelbase/memory.c index e102345119ab..ad108132e50e 100644 --- a/dlls/kernelbase/memory.c +++ b/dlls/kernelbase/memory.c @@ -640,8 +640,21 @@ BOOL WINAPI DECLSPEC_HOTPATCH WriteProcessMemory( HANDLE process, void *addr, co if (!VirtualQueryEx( process, addr, &info, sizeof(info) )) { - close_cross_process_connection( list ); - return FALSE; + HANDLE process_alt; + BOOL alt_ok = FALSE; + + if (GetLastError() == ERROR_ACCESS_DENIED && + DuplicateHandle( GetCurrentProcess(), process, GetCurrentProcess(), &process_alt, + PROCESS_QUERY_INFORMATION, FALSE, 0 )) + { + alt_ok = VirtualQueryEx( process_alt, addr, &info, sizeof(info) ); + CloseHandle( process_alt ); + } + if (!alt_ok) + { + close_cross_process_connection( list ); + return FALSE; + } } switch (info.Protect & ~(PAGE_GUARD | PAGE_NOCACHE)) From 92a11eb7662107d169e28289fbd60d41d9617988 Mon Sep 17 00:00:00 2001 From: Brendan McGrath Date: Mon, 2 Dec 2024 18:40:52 +1100 Subject: [PATCH 124/454] winegstreamer: Avoid pre-condition assertion failures. Both gst_audio_info_from_caps and gst_video_info_from_caps perform the following pre-condition assertion: g_return_val_if_fail (gst_caps_is_fixed (caps), FALSE); The documentation for this assertion states: Any failure of such a pre-condition assertion is considered a programming error on the part of the caller of the public API, and the program is considered to be in an undefined state afterwards. --- dlls/winegstreamer/wg_format.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/dlls/winegstreamer/wg_format.c b/dlls/winegstreamer/wg_format.c index 2134fa7cf40a..ce8878f3f4d4 100644 --- a/dlls/winegstreamer/wg_format.c +++ b/dlls/winegstreamer/wg_format.c @@ -587,15 +587,19 @@ void wg_format_from_caps(struct wg_format *format, const GstCaps *caps) { GstAudioInfo info; - if (gst_audio_info_from_caps(&info, caps)) + if (gst_caps_is_fixed(caps) && gst_audio_info_from_caps(&info, caps)) wg_format_from_audio_info(format, &info); + else + GST_WARNING("Unable to get audio info from caps"); } else if (!strcmp(name, "video/x-raw")) { GstVideoInfo info; - if (gst_video_info_from_caps(&info, caps)) + if (gst_caps_is_fixed(caps) && gst_video_info_from_caps(&info, caps)) wg_format_from_video_info(format, &info); + else + GST_WARNING("Unable to get video info from caps"); } else if (!strcmp(name, "audio/mpeg") && gst_structure_get_boolean(structure, "parsed", &parsed) && parsed) { From b65b99fbf9fef534a96e418dfee044c5c80d4943 Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Thu, 30 Jan 2025 16:14:07 +0000 Subject: [PATCH 125/454] dmime: Load soundfont from environment variable. --- dlls/dmloader/loader.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/dlls/dmloader/loader.c b/dlls/dmloader/loader.c index 3798a0851a0d..2e663061c25d 100644 --- a/dlls/dmloader/loader.c +++ b/dlls/dmloader/loader.c @@ -888,10 +888,17 @@ static const IDirectMusicLoader8Vtbl loader_vtbl = static HRESULT get_default_gm_path(WCHAR *path, DWORD max_len) { - DWORD ret; - HKEY hkey; + DWORD ret; + HKEY hkey; + static const WCHAR PROTON_SOUNDFILES_FILES_W[] = {'P','R','O','T','O','N','_','S','O','U','N','D','F','O','N','T','_','F','I','L','E','S',0}; + if (GetEnvironmentVariableW(PROTON_SOUNDFILES_FILES_W, path, max_len)) + { + TRACE("Found soundfont files from environment %s\n", debugstr_w(path)); + if (GetFileAttributesW(path) != INVALID_FILE_ATTRIBUTES) return S_OK; + WARN("Counldn't find %s\n", debugstr_w(path)); + } - if (!(ret = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\DirectMusic" , 0, KEY_READ, &hkey))) + if (!(ret = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\DirectMusic" , 0, KEY_READ, &hkey))) { DWORD type, size = max_len * sizeof(WCHAR); ret = RegQueryValueExW(hkey, L"GMFilePath", NULL, &type, (BYTE *)path, &size); From 591fb846d77837bd68ab8dd79813179442732cb1 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 21 Feb 2025 15:57:23 -0600 Subject: [PATCH 126/454] kernelbase: HACK: Don't attempt to run powershell for Marvel Rivals. CW-Bug-Id: #24920 --- dlls/kernelbase/process.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/dlls/kernelbase/process.c b/dlls/kernelbase/process.c index 7a51dfd231d8..b806add11c5f 100644 --- a/dlls/kernelbase/process.c +++ b/dlls/kernelbase/process.c @@ -686,6 +686,18 @@ BOOL WINAPI DECLSPEC_HOTPATCH CreateProcessInternalW( HANDLE token, const WCHAR { WCHAR *cmdline_new = NULL; + { + char sgi[64]; + + if (cmd_line && !wcsncmp( cmd_line, L"powershell", 10 ) + && GetEnvironmentVariableA( "SteamGameId", sgi, sizeof(sgi) ) < sizeof(sgi) && !strcmp( sgi, "2767030" )) + { + FIXME("HACK: not starting powershell.exe.\n"); + SetLastError( ERROR_FILE_NOT_FOUND ); + return FALSE; + } + } + if ((append = hack_append_command_line( cmd_line ))) { cmdline_new = RtlAllocateHeap( GetProcessHeap(), 0, sizeof(WCHAR) From ece7b113e69fac91fe09b50d5bac806c2e52f06a Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 6 Mar 2025 19:47:57 -0600 Subject: [PATCH 127/454] ntdll: 'popf' right before 'ret' in __wine_syscall_dispatcher(). CW-Bug-Id: #24983 --- dlls/ntdll/unix/signal_x86_64.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dlls/ntdll/unix/signal_x86_64.c b/dlls/ntdll/unix/signal_x86_64.c index 473c4b994ad1..f837a7551f9c 100644 --- a/dlls/ntdll/unix/signal_x86_64.c +++ b/dlls/ntdll/unix/signal_x86_64.c @@ -3302,12 +3302,12 @@ __ASM_GLOBAL_FUNC( __wine_syscall_dispatcher, __ASM_CFI(".cfi_same_value %rsi\n\t") "movq 0x70(%rcx),%rcx\n\t" /* frame->rip */ __ASM_CFI(".cfi_register rip, rcx\n\t") + "pushq %rcx\n\t" + __ASM_CFI(".cfi_adjust_cfa_offset 8\n\t") "pushq %r11\n\t" __ASM_CFI(".cfi_adjust_cfa_offset 8\n\t") "popfq\n\t" __ASM_CFI(".cfi_adjust_cfa_offset -8\n\t") - "pushq %rcx\n\t" - __ASM_CFI(".cfi_adjust_cfa_offset 8\n\t") "ret\n\t" /* pop rcx-based kernel stack cfi */ __ASM_CFI(".cfi_restore_state\n") From fba15cc2f55a85d850ce97136dc3b09e777a8770 Mon Sep 17 00:00:00 2001 From: Brendan McGrath Date: Thu, 17 Apr 2025 08:55:26 +1000 Subject: [PATCH 128/454] amend! winegstreamer: Push flush event when flushing. winegstreamer: Push flush event when flushing. This ensures the gstreamer pipeline is empty whilst we flush our queues. (cherry picked from commit 9015585da9b3374a1e7b92e747435dde11ed2001) CW-Bug-Id: #25177 From 8d434ebff4d7d0aca143633fa52ef00f2818b316 Mon Sep 17 00:00:00 2001 From: Arkadiusz Hiler Date: Mon, 21 Apr 2025 09:21:03 +0300 Subject: [PATCH 129/454] Revert "winevulkan: Filter out duplicate structures in VkDeviceCreateInfo chain." This reverts commit b63f6a87e6602c7d29f3e89b5bf2d70ab08b2a80. Not needed anymore. --- dlls/winevulkan/vulkan.c | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/dlls/winevulkan/vulkan.c b/dlls/winevulkan/vulkan.c index ba8359226a41..3ade38428c3b 100644 --- a/dlls/winevulkan/vulkan.c +++ b/dlls/winevulkan/vulkan.c @@ -1111,29 +1111,6 @@ VkResult wine_vkAllocateCommandBuffers(VkDevice client_device, const VkCommandBu return res; } -static void filter_duplicate_structures(const VkBaseInStructure **in) -{ - const VkBaseInStructure *h; - VkBaseInStructure **h2; - - for (h = *in; h; h = h->pNext) - { - if (h->sType == 1000284001 /*VK_STRUCTURE_TYPE_DEVICE_DEVICE_MEMORY_REPORT_CREATE_INFO_EXT*/ - || h->sType == VK_STRUCTURE_TYPE_DEVICE_PRIVATE_DATA_CREATE_INFO) - continue; - - for (h2 = (VkBaseInStructure **)in; *h2 != h; h2 = (VkBaseInStructure **)&(*h2)->pNext) - { - if ((*h2)->sType == h->sType) - { - ERR("Duplicate sType %d in the chain, keeping the last.\n", h->sType); - *h2 = (VkBaseInStructure *)(*h2)->pNext; - break; - } - } - } -} - VkResult wine_vkCreateDevice(VkPhysicalDevice client_physical_device, const VkDeviceCreateInfo *create_info, const VkAllocationCallbacks *allocator, VkDevice *ret, void *client_ptr) { @@ -1152,8 +1129,6 @@ VkResult wine_vkCreateDevice(VkPhysicalDevice client_physical_device, const VkDe void *native_create_device_context = NULL; VkCreateInfoWineDeviceCallback *callback; - filter_duplicate_structures((const VkBaseInStructure **)&create_info); - if (allocator) FIXME("Support for allocation callbacks not implemented yet\n"); From 43a2238da82840b4f80d129ee3cee91eafb1630f Mon Sep 17 00:00:00 2001 From: Arkadiusz Hiler Date: Mon, 21 Apr 2025 09:26:16 +0300 Subject: [PATCH 130/454] Revert "kernelbase: HACK: Don't attempt to run powershell for Marvel Rivals." This reverts commit 42c44d41981b8414e478519adb5ba81042714b33. Not needed anymore. --- dlls/kernelbase/process.c | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/dlls/kernelbase/process.c b/dlls/kernelbase/process.c index b806add11c5f..7a51dfd231d8 100644 --- a/dlls/kernelbase/process.c +++ b/dlls/kernelbase/process.c @@ -686,18 +686,6 @@ BOOL WINAPI DECLSPEC_HOTPATCH CreateProcessInternalW( HANDLE token, const WCHAR { WCHAR *cmdline_new = NULL; - { - char sgi[64]; - - if (cmd_line && !wcsncmp( cmd_line, L"powershell", 10 ) - && GetEnvironmentVariableA( "SteamGameId", sgi, sizeof(sgi) ) < sizeof(sgi) && !strcmp( sgi, "2767030" )) - { - FIXME("HACK: not starting powershell.exe.\n"); - SetLastError( ERROR_FILE_NOT_FOUND ); - return FALSE; - } - } - if ((append = hack_append_command_line( cmd_line ))) { cmdline_new = RtlAllocateHeap( GetProcessHeap(), 0, sizeof(WCHAR) From 1b1231ddd55dd2e00faf631228ccb01c62422ed5 Mon Sep 17 00:00:00 2001 From: Arkadiusz Hiler Date: Mon, 21 Apr 2025 18:26:16 +0300 Subject: [PATCH 131/454] Revert "mshtml: Zero output iface on failure in get_document_node()." This reverts commit eeac1d8fb1e3a3e81987ed99f03bc83a46be86da. Not needed anymore --- dlls/mshtml/htmldoc.c | 1 - 1 file changed, 1 deletion(-) diff --git a/dlls/mshtml/htmldoc.c b/dlls/mshtml/htmldoc.c index 261eaecb332a..7152c1eeb7fd 100644 --- a/dlls/mshtml/htmldoc.c +++ b/dlls/mshtml/htmldoc.c @@ -5973,7 +5973,6 @@ HRESULT get_document_node(nsIDOMDocument *dom_document, HTMLDocumentNode **ret) if(!node) { ERR("document not initialized\n"); - *ret = NULL; return E_FAIL; } From 1b49b8c5c2b37f63e77763866c1e707a1c1a10fa Mon Sep 17 00:00:00 2001 From: Arkadiusz Hiler Date: Mon, 21 Apr 2025 18:26:27 +0300 Subject: [PATCH 132/454] Revert "kernelbase: Handle missing PROCESS_QUERY_INFORMATION access right in WriteProcessMemory()." This reverts commit 311af292bb77f8084bde8cf906db3b418442c8b0. --- dlls/kernelbase/memory.c | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/dlls/kernelbase/memory.c b/dlls/kernelbase/memory.c index ad108132e50e..e102345119ab 100644 --- a/dlls/kernelbase/memory.c +++ b/dlls/kernelbase/memory.c @@ -640,21 +640,8 @@ BOOL WINAPI DECLSPEC_HOTPATCH WriteProcessMemory( HANDLE process, void *addr, co if (!VirtualQueryEx( process, addr, &info, sizeof(info) )) { - HANDLE process_alt; - BOOL alt_ok = FALSE; - - if (GetLastError() == ERROR_ACCESS_DENIED && - DuplicateHandle( GetCurrentProcess(), process, GetCurrentProcess(), &process_alt, - PROCESS_QUERY_INFORMATION, FALSE, 0 )) - { - alt_ok = VirtualQueryEx( process_alt, addr, &info, sizeof(info) ); - CloseHandle( process_alt ); - } - if (!alt_ok) - { - close_cross_process_connection( list ); - return FALSE; - } + close_cross_process_connection( list ); + return FALSE; } switch (info.Protect & ~(PAGE_GUARD | PAGE_NOCACHE)) From a34ad26ed186beb974074baa185d5d0faf1c1afd Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 21 Apr 2025 11:06:14 -0600 Subject: [PATCH 133/454] Revert "ntoskrnl.exe: Open directory object with nonzero access in test_permanent()." This reverts commit 08890f6408b54685833bb7269670aebaa64843a4. --- dlls/ntoskrnl.exe/tests/driver.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dlls/ntoskrnl.exe/tests/driver.c b/dlls/ntoskrnl.exe/tests/driver.c index d3e6fea2debb..f82e34fca3bd 100644 --- a/dlls/ntoskrnl.exe/tests/driver.c +++ b/dlls/ntoskrnl.exe/tests/driver.c @@ -2289,7 +2289,7 @@ static void test_permanence(void) ok(!status, "got %#lx\n", status); attr.Attributes = 0; - status = ZwOpenDirectoryObject( &handle, GENERIC_ALL, &attr ); + status = ZwOpenDirectoryObject( &handle, 0, &attr ); ok(!status, "got %#lx\n", status); status = ZwMakeTemporaryObject( handle ); todo_wine @@ -2307,7 +2307,7 @@ static void test_permanence(void) todo_wine ok(!status, "got %#lx\n", status); attr.Attributes = OBJ_PERMANENT; - status = ZwOpenDirectoryObject( &handle2, GENERIC_ALL, &attr ); + status = ZwOpenDirectoryObject( &handle2, 0, &attr ); ok(status == STATUS_SUCCESS, "got %#lx\n", status); status = ZwClose( handle2 ); ok(!status, "got %#lx\n", status); From 22009d71ee0f10bf56ef92772d006ddb8f3bde0d Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 21 Apr 2025 11:33:47 -0600 Subject: [PATCH 134/454] Revert "ntdll: 'popf' right before 'ret' in __wine_syscall_dispatcher()." This reverts commit 30d8cb9bb549afc880d2f7788e9ef734784a2fa3. CW-Bug-Id: #24983 --- dlls/ntdll/unix/signal_x86_64.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dlls/ntdll/unix/signal_x86_64.c b/dlls/ntdll/unix/signal_x86_64.c index f837a7551f9c..473c4b994ad1 100644 --- a/dlls/ntdll/unix/signal_x86_64.c +++ b/dlls/ntdll/unix/signal_x86_64.c @@ -3302,12 +3302,12 @@ __ASM_GLOBAL_FUNC( __wine_syscall_dispatcher, __ASM_CFI(".cfi_same_value %rsi\n\t") "movq 0x70(%rcx),%rcx\n\t" /* frame->rip */ __ASM_CFI(".cfi_register rip, rcx\n\t") - "pushq %rcx\n\t" - __ASM_CFI(".cfi_adjust_cfa_offset 8\n\t") "pushq %r11\n\t" __ASM_CFI(".cfi_adjust_cfa_offset 8\n\t") "popfq\n\t" __ASM_CFI(".cfi_adjust_cfa_offset -8\n\t") + "pushq %rcx\n\t" + __ASM_CFI(".cfi_adjust_cfa_offset 8\n\t") "ret\n\t" /* pop rcx-based kernel stack cfi */ __ASM_CFI(".cfi_restore_state\n") From 9146edd2020fff6a8ffa09216d6ef6bf74f1701e Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 22 Apr 2025 18:58:40 -0600 Subject: [PATCH 135/454] Revert "mshtml, jscript: HACK: Process sent messages and WM_TIMER often enough for FFXIV launcher." This reverts commit d0f94acc8a88a49446bf45d690a61c7b5a24951d. CW-Bug-Id: #25266 --- dlls/jscript/compile.c | 1 - dlls/jscript/function.c | 2 -- dlls/jscript/jscript.h | 2 -- dlls/jscript/jsutils.c | 27 --------------------------- dlls/mshtml/mutation.c | 29 ----------------------------- 5 files changed, 61 deletions(-) diff --git a/dlls/jscript/compile.c b/dlls/jscript/compile.c index fba796625714..2d2c2c9f40ad 100644 --- a/dlls/jscript/compile.c +++ b/dlls/jscript/compile.c @@ -2772,6 +2772,5 @@ HRESULT compile_script(script_ctx_t *ctx, const WCHAR *code, UINT64 source_conte } *ret = compiler.code; - hack_pump_messages(); return S_OK; } diff --git a/dlls/jscript/function.c b/dlls/jscript/function.c index 5fab24b03402..cc4927ef786b 100644 --- a/dlls/jscript/function.c +++ b/dlls/jscript/function.c @@ -971,8 +971,6 @@ static HRESULT InterpretedFunction_call(script_ctx_t *ctx, FunctionInstance *fun TRACE("%p\n", function); - hack_pump_messages(); - if(flags & DISPATCH_CONSTRUCT) { hres = create_object(ctx, &function->function.dispex, &new_obj); if(FAILED(hres)) diff --git a/dlls/jscript/jscript.h b/dlls/jscript/jscript.h index 0adf1ba5eb38..1d651dccd206 100644 --- a/dlls/jscript/jscript.h +++ b/dlls/jscript/jscript.h @@ -665,5 +665,3 @@ static inline void unlock_module(void) { InterlockedDecrement(&module_ref); } - -void hack_pump_messages(void); diff --git a/dlls/jscript/jsutils.c b/dlls/jscript/jsutils.c index 1b437814ff18..1d6fa06a993f 100644 --- a/dlls/jscript/jsutils.c +++ b/dlls/jscript/jsutils.c @@ -1105,30 +1105,3 @@ HRESULT create_jscaller(script_ctx_t *ctx) ctx->jscaller = ret; return S_OK; } - -void hack_pump_messages(void) -{ - static BOOL initialized; - static HWND hwnd; - static DWORD last; - DWORD tick; - MSG msg; - - if (!initialized) - { - const char *sgi = getenv("SteamGameId"); - if (sgi && !strcmp(sgi, "39210")) hwnd = FindWindowW(NULL, L"FFXIVLauncher"); - if (hwnd) ERR("HACK: injecting PeekMessage into mshtml / jscript processing.\n"); - initialized = TRUE; - } - if (!hwnd) return; - - tick = GetTickCount(); - if (tick - last < 50) return; - last = tick; - if (PeekMessageW(&msg, hwnd, WM_TIMER, WM_TIMER, PM_REMOVE)) - { - TRACE("dispatching WM_TIMER.\n"); - DispatchMessageW(&msg); - } -} diff --git a/dlls/mshtml/mutation.c b/dlls/mshtml/mutation.c index c25fcf0db049..11e31d38968d 100644 --- a/dlls/mshtml/mutation.c +++ b/dlls/mshtml/mutation.c @@ -891,33 +891,6 @@ static void NSAPI nsDocumentObserver_StyleRuleRemoved(nsIDocumentObserver *iface { } -static void hack_pump_messages(void) -{ - static BOOL initialized; - static HWND hwnd; - static DWORD last; - DWORD tick; - MSG msg; - - if (!initialized) - { - const char *sgi = getenv("SteamGameId"); - if (sgi && !strcmp(sgi, "39210")) hwnd = FindWindowW(NULL, L"FFXIVLauncher"); - if (hwnd) ERR("HACK: injecting PeekMessage into mshtml / jscript processing.\n"); - initialized = TRUE; - } - if (!hwnd) return; - - tick = GetTickCount(); - if (tick - last < 50) return; - last = tick; - if (PeekMessageW(&msg, hwnd, WM_TIMER, WM_TIMER, PM_REMOVE)) - { - TRACE("dispatching WM_TIMER.\n"); - DispatchMessageW(&msg); - } -} - static void NSAPI nsDocumentObserver_BindToDocument(nsIDocumentObserver *iface, nsIDocument *aDocument, nsIContent *aContent) { @@ -932,8 +905,6 @@ static void NSAPI nsDocumentObserver_BindToDocument(nsIDocumentObserver *iface, TRACE("(%p)->(%p %p)\n", This, aDocument, aContent); - hack_pump_messages(); - if(This->document_mode < COMPAT_MODE_IE10) { nsres = nsIContent_QueryInterface(aContent, &IID_nsIDOMComment, (void**)&nscomment); if(NS_SUCCEEDED(nsres)) { From b108341c61176648a9548505c7ec19f09cf9dd17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 9 May 2025 15:23:08 +0200 Subject: [PATCH 136/454] winex11: Focus the desktop window when _NET_ACTIVE_WINDOW is None. XWayland changes _NET_ACTIVE_WINDOW to None when focus switches to a Wayland window. This makes sure app window is deactivated in that case too and not just when focus changes to another X11 window. _NET_ACTIVE_WINDOW also changes None when alt-tab is initiated, but there's no easy way to tell the difference, so this will also trigger a focus loss as soon as alt-tab is initiated, while we were previously waiting for it to actually complete to make changes. CW-Bug-Id: #25109 --- dlls/winex11.drv/window.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index 992c1f1138f3..6b10ff87bfbd 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -317,12 +317,11 @@ static HWND hwnd_from_window( Display *display, Window window ) { unsigned long count, remaining; unsigned long *xhwnd; - HWND hwnd = (HWND)-1; + HWND hwnd = 0; int format; Atom type; - if (!window) return 0; - if (!XFindContext( display, window, winContext, (char **)&hwnd )) return hwnd; + if (!window || !XFindContext( display, window, winContext, (char **)&hwnd )) return hwnd; X11DRV_expect_error( display, host_window_error, NULL ); if (!XGetWindowProperty( display, window, x11drv_atom(_WINE_HWND), 0, 65536, False, XA_CARDINAL, @@ -331,7 +330,7 @@ static HWND hwnd_from_window( Display *display, Window window ) if (type == XA_CARDINAL && format == 32) hwnd = ULongToHandle(*xhwnd); XFree( xhwnd ); } - if (X11DRV_check_error()) return (HWND)-1; + if (X11DRV_check_error()) return 0; return hwnd; } @@ -1874,7 +1873,7 @@ BOOL X11DRV_GetWindowStateUpdates( HWND hwnd, UINT *state_cmd, UINT *config_cmd, !thread_data->net_active_window_serial && (window = thread_data->current_net_active_window)) { *foreground = hwnd_from_window( thread_data->display, window ); - if (*foreground == (HWND)-1) *foreground = NtUserGetDesktopWindow(); + if (*foreground == 0) *foreground = NtUserGetDesktopWindow(); if (*foreground == old_foreground) *foreground = 0; } @@ -2010,7 +2009,7 @@ void net_active_window_notify( unsigned long serial, Window value, Time time ) received = wine_dbg_sprintf( "_NET_ACTIVE_WINDOW %p/%lx serial %lu time %lu", hwnd, value, serial, time ); expected = *expect_serial ? wine_dbg_sprintf( ", expected %p/%lx serial %lu", expect_hwnd, *pending, *expect_serial ) : ""; - if (hwnd == (HWND)-1) value = root_window; + if (hwnd == 0) value = root_window; handle_state_change( serial, expect_serial, sizeof(value), &value, desired, pending, current, expected, "", received, NULL ); } @@ -2019,7 +2018,7 @@ void net_active_window_init( struct x11drv_thread_data *data ) { Window window = get_net_active_window( data->display, &data->active_window ); - if (hwnd_from_window( data->display, window ) == (HWND)-1) window = root_window; + if (hwnd_from_window( data->display, window ) == 0) window = root_window; data->desired_net_active_window = window; data->pending_net_active_window = window; data->current_net_active_window = window; From dc292b7e5dcf3b922a3c6455e9785777d5a8ad7b Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 14 May 2025 18:12:10 -0600 Subject: [PATCH 137/454] amend! winex11.drv: HACK: Try to ensure black background window is behind for Forza Horizon 4/5. winex11.drv: HACK: Try to ensure black bacjground window is behind for Forza Horizon 4/5. CW-Bug-Id: #19335 CW-Bug-Id: #25372 From 22b03afb465f2c1d4cb4d88a663cb9e7970961eb Mon Sep 17 00:00:00 2001 From: Brendan McGrath Date: Tue, 3 Jun 2025 07:08:35 +1000 Subject: [PATCH 138/454] amend! winegstreamer: Don't try protonvideoconvert if URI has a web scheme. winegstreamer: Don't try protonvideoconvert if URI has a web scheme. CW-Bug-Id: #25385 From d3d99ae93618e5fc7b38b43af4749d5ab2c2da32 Mon Sep 17 00:00:00 2001 From: Arkadiusz Hiler Date: Tue, 3 Jun 2025 20:34:58 +0300 Subject: [PATCH 139/454] amend! ntdll: HACK: Enable WINE_ALERT_SIMULATE_SCHED_QUANTUM option for other IdeaFactory games using Orochi 4 ntdll: HACK: Enable WINE_ALERT_SIMULATE_SCHED_QUANTUM option for more games. There are other IdeaFactory games using Orochi 4. This should help with massive frametime spikes problems in the following games, since they share the same game engine as Mary Skelter 2: - Mary Skelter Finale (2357400) - Death end re;Quest (Application.exe) (990050) - Death end re;Quest 2 (1266220) - Megadimension Neptunia VIIR (774511) - Neptunia Virtual Stars (1399840) - Dragon Star Varnir (1062040) Link: https://github.com/ValveSoftware/wine/pull/274 CW-Bug-Id: #24283 From 3a2b3682e7ee6482dbf560a624e54173263d8b0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 22 May 2025 21:52:36 +0200 Subject: [PATCH 140/454] fixup! winex11: Expose a _WINE_HWND property on toplevel windows. --- dlls/winex11.drv/window.c | 47 ++++++++++++++++------------------ dlls/winex11.drv/x11drv.h | 2 -- dlls/winex11.drv/x11drv_main.c | 1 - 3 files changed, 22 insertions(+), 28 deletions(-) diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index 6b10ff87bfbd..6870ee281cec 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -313,27 +313,6 @@ static void remove_startup_notification( struct x11drv_win_data *data ) } } -static HWND hwnd_from_window( Display *display, Window window ) -{ - unsigned long count, remaining; - unsigned long *xhwnd; - HWND hwnd = 0; - int format; - Atom type; - - if (!window || !XFindContext( display, window, winContext, (char **)&hwnd )) return hwnd; - - X11DRV_expect_error( display, host_window_error, NULL ); - if (!XGetWindowProperty( display, window, x11drv_atom(_WINE_HWND), 0, 65536, False, XA_CARDINAL, - &type, &format, &count, &remaining, (unsigned char **)&xhwnd )) - { - if (type == XA_CARDINAL && format == 32) hwnd = ULongToHandle(*xhwnd); - XFree( xhwnd ); - } - if (X11DRV_check_error()) return 0; - return hwnd; -} - static BOOL is_managed( HWND hwnd ) { struct x11drv_win_data *data = get_win_data( hwnd ); @@ -342,7 +321,7 @@ static BOOL is_managed( HWND hwnd ) return ret; } -HWND *build_hwnd_list(void) +static HWND *build_hwnd_list(void) { NTSTATUS status; HWND *list; @@ -377,6 +356,27 @@ static BOOL has_owned_popups( HWND hwnd ) return ret; } +/* returns the HWND for the X11 window, or the desktop window if it isn't a Wine window */ +static HWND hwnd_from_window( Display *display, Window window ) +{ + HWND hwnd, desktop = NtUserGetDesktopWindow(); + HWND *list; + UINT i; + + if (!window || window == root_window) return desktop; + if (!XFindContext( display, window, winContext, (char **)&hwnd )) return hwnd; + + if (!(list = build_hwnd_list())) return desktop; + + for (i = 0; list[i] != HWND_BOTTOM; i++) + if (window == X11DRV_get_whole_window( list[i] )) + break; + hwnd = list[i] == HWND_BOTTOM ? desktop : list[i]; + + free( list ); + + return hwnd; +} /*********************************************************************** * alloc_win_data @@ -2444,7 +2444,6 @@ void set_gamescope_overlay_prop( Display *display, Window window, HWND hwnd ) */ static void create_whole_window( struct x11drv_win_data *data ) { - unsigned long xhwnd = (UINT_PTR)data->hwnd; int cx, cy, mask; XSetWindowAttributes attr; WCHAR text[1024]; @@ -2479,8 +2478,6 @@ static void create_whole_window( struct x11drv_win_data *data ) cx, cy, 0, data->vis.depth, InputOutput, data->vis.visual, mask, &attr ); if (!data->whole_window) goto done; - XChangeProperty( data->display, data->whole_window, x11drv_atom(_WINE_HWND), XA_CARDINAL, 32, - PropModeReplace, (unsigned char *)&xhwnd, 1 ); set_wine_allow_flip( data->whole_window, 0 ); SetRect( &data->current_state.rect, pos.x, pos.y, pos.x + cx, pos.y + cy ); diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index 27d1af192d5f..429b6e48a8d8 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -534,7 +534,6 @@ enum x11drv_atoms XATOM__NET_WM_WINDOW_TYPE_UTILITY, XATOM__NET_WORKAREA, XATOM__GTK_WORKAREAS_D0, - XATOM__WINE_HWND, XATOM__XEMBED, XATOM__XEMBED_INFO, XATOM__WINE_ALLOW_FLIP, @@ -749,7 +748,6 @@ extern void move_resize_window( HWND hwnd, int dir, POINT pos ); extern void X11DRV_InitKeyboard( Display *display ); extern void X11DRV_InitMouse( Display *display ); extern BOOL X11DRV_ProcessEvents( DWORD mask ); -extern HWND *build_hwnd_list(void); typedef int (*x11drv_error_callback)( Display *display, XErrorEvent *event, void *arg ); diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c index 22a1e370b413..322cb33c1253 100644 --- a/dlls/winex11.drv/x11drv_main.c +++ b/dlls/winex11.drv/x11drv_main.c @@ -164,7 +164,6 @@ static const char * const atom_names[NB_XATOMS - FIRST_XATOM] = "_NET_WM_WINDOW_TYPE_UTILITY", "_NET_WORKAREA", "_GTK_WORKAREAS_D0", - "_WINE_HWND", "_XEMBED", "_XEMBED_INFO", "_WINE_ALLOW_FLIP", From 31441d8e241daa2a02842f70c8f0cb9ed8d9c47e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 22 May 2025 22:15:05 +0200 Subject: [PATCH 141/454] fixup! winex11: Use _NET_ACTIVE_WINDOW to request/track foreground window. --- dlls/winex11.drv/event.c | 46 ++++---------------------- dlls/winex11.drv/window.c | 68 +++++++++++++++++++++++++-------------- dlls/winex11.drv/x11drv.h | 17 ++++++---- 3 files changed, 62 insertions(+), 69 deletions(-) diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c index 7e736df98571..2d3f25072f9a 100644 --- a/dlls/winex11.drv/event.c +++ b/dlls/winex11.drv/event.c @@ -695,7 +695,7 @@ static void set_focus( Display *display, HWND focus, Time time ) TRACE( "setting foreground window to %p\n", focus ); - if (!is_netwm_supported( x11drv_atom(_NET_ACTIVE_WINDOW) )) + if (!is_net_supported( x11drv_atom(_NET_ACTIVE_WINDOW) )) { NtUserSetForegroundWindow( focus ); @@ -964,7 +964,7 @@ static void focus_out( Display *display , HWND hwnd ) /* don't reset the foreground window, if the window which is getting the focus is a Wine window */ - if (!is_netwm_supported( x11drv_atom(_NET_ACTIVE_WINDOW) ) && !is_current_process_focused()) + if (!is_net_supported( x11drv_atom(_NET_ACTIVE_WINDOW) ) && !is_current_process_focused()) { /* Abey : 6-Oct-99. Check again if the focus out window is the Foreground window, because in most cases the messages sent @@ -1307,24 +1307,6 @@ static void get_window_mwm_hints( Display *display, Window window, MwmHints *hin } } -Window get_net_active_window( Display *display, char **name ) -{ - unsigned long count, remaining; - Window window = None, *value; - int format; - Atom type; - - if (!XGetWindowProperty( display, DefaultRootWindow( display ), x11drv_atom(_NET_ACTIVE_WINDOW), 0, - 65536 / sizeof(Window), False, XA_WINDOW, &type, &format, &count, - &remaining, (unsigned char **)&value )) - { - if (type == XA_WINDOW && format == 32) window = *value; - XFree( value ); - } - - if (window) get_window_name( display, window, name ); - return window; -} /*********************************************************************** * handle_wm_state_notify @@ -1410,25 +1392,11 @@ static void handle_net_supporting_wm_check_notify( XPropertyEvent *event ) if (event->state == PropertyNewValue) net_supporting_wm_check_init( data ); } -static void handle_net_active_window( HWND hwnd, XPropertyEvent *event ) +static void handle_net_active_window( XPropertyEvent *event ) { - struct x11drv_thread_data *data = x11drv_thread_data(); - Window window = None; - HWND foreground; - - if (data->active_window) - { - XFree( data->active_window ); - data->active_window = NULL; - } - - if (event->state == PropertyNewValue) window = get_net_active_window( event->display, &data->active_window ); + Window window = 0; + if (event->state == PropertyNewValue) window = get_net_active_window( event->display ); net_active_window_notify( event->serial, window, event->time ); - - if (data->active_window) TRACE( "_NET_ACTIVE_WINDOW changed to %s\n", debugstr_a(data->active_window) ); - - if (!(foreground = NtUserGetForegroundWindow())) foreground = NtUserGetDesktopWindow(); - NtUserPostMessage( foreground, WM_WINE_WINDOW_STATE_CHANGED, 0, 0 ); } /*********************************************************************** @@ -1445,7 +1413,7 @@ static BOOL X11DRV_PropertyNotify( HWND hwnd, XEvent *xev ) if (event->atom == x11drv_atom(_MOTIF_WM_HINTS)) handle_mwm_hints_notify( hwnd, event ); if (event->atom == x11drv_atom(_NET_SUPPORTED)) handle_net_supported_notify( event ); if (event->atom == x11drv_atom(_NET_SUPPORTING_WM_CHECK)) handle_net_supporting_wm_check_notify( event ); - if (event->atom == x11drv_atom(_NET_ACTIVE_WINDOW)) handle_net_active_window( hwnd, event ); + if (event->atom == x11drv_atom(_NET_ACTIVE_WINDOW)) handle_net_active_window( event ); return TRUE; } @@ -1460,7 +1428,7 @@ void X11DRV_ActivateWindow( HWND hwnd, HWND previous ) { struct x11drv_win_data *data; - set_net_active_window( hwnd, previous ); + if (!is_virtual_desktop()) set_net_active_window( hwnd, previous ); if (!(data = get_win_data( hwnd ))) return; if (!data->managed || data->embedder) set_input_focus( data ); diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index 6870ee281cec..70c8231d75b4 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -1863,17 +1863,16 @@ BOOL X11DRV_GetWindowStateUpdates( HWND hwnd, UINT *state_cmd, UINT *config_cmd, struct x11drv_thread_data *thread_data = x11drv_thread_data(); struct x11drv_win_data *data; HWND old_foreground; - Window window; *state_cmd = *config_cmd = 0; *foreground = 0; if (!(old_foreground = NtUserGetForegroundWindow())) old_foreground = NtUserGetDesktopWindow(); - if (NtUserGetWindowThread( old_foreground, NULL ) == GetCurrentThreadId() && !window_has_pending_wm_state( old_foreground, NormalState ) && - !thread_data->net_active_window_serial && (window = thread_data->current_net_active_window)) + if (!is_virtual_desktop() && NtUserGetWindowThread( old_foreground, NULL ) == GetCurrentThreadId() && + !window_has_pending_wm_state( old_foreground, NormalState ) && + !thread_data->net_active_window_serial) { - *foreground = hwnd_from_window( thread_data->display, window ); - if (*foreground == 0) *foreground = NtUserGetDesktopWindow(); + *foreground = hwnd_from_window( thread_data->display, thread_data->current_state.net_active_window ); if (*foreground == old_foreground) *foreground = 0; } @@ -2001,27 +2000,48 @@ void window_configure_notify( struct x11drv_win_data *data, unsigned long serial void net_active_window_notify( unsigned long serial, Window value, Time time ) { struct x11drv_thread_data *data = x11drv_thread_data(); - Window *desired = &data->desired_net_active_window, *pending = &data->pending_net_active_window, *current = &data->current_net_active_window; - HWND hwnd = hwnd_from_window( data->display, value ), expect_hwnd = hwnd_from_window( data->display, *pending ); + Window *desired = &data->desired_state.net_active_window, *pending = &data->pending_state.net_active_window, *current = &data->current_state.net_active_window; unsigned long *expect_serial = &data->net_active_window_serial; const char *expected, *received; + HWND current_hwnd, pending_hwnd; + + current_hwnd = hwnd_from_window( data->display, value ); + pending_hwnd = hwnd_from_window( data->display, *pending ); + + received = wine_dbg_sprintf( "_NET_ACTIVE_WINDOW %p/%lx serial %lu time %lu", current_hwnd, value, serial, time ); + expected = *expect_serial ? wine_dbg_sprintf( ", expected %p/%lx serial %lu", pending_hwnd, *pending, *expect_serial ) : ""; + if (!handle_state_change( serial, expect_serial, sizeof(value), &value, desired, pending, + current, expected, "", received, NULL )) + return; + + NtUserPostMessage( NtUserGetForegroundWindow(), WM_WINE_WINDOW_STATE_CHANGED, 0, 0 ); +} - received = wine_dbg_sprintf( "_NET_ACTIVE_WINDOW %p/%lx serial %lu time %lu", hwnd, value, serial, time ); - expected = *expect_serial ? wine_dbg_sprintf( ", expected %p/%lx serial %lu", expect_hwnd, *pending, *expect_serial ) : ""; +Window get_net_active_window( Display *display ) +{ + unsigned long count, remaining; + Window window = None, *value; + int format; + Atom type; + + if (!XGetWindowProperty( display, DefaultRootWindow( display ), x11drv_atom(_NET_ACTIVE_WINDOW), 0, + 65536 / sizeof(Window), False, XA_WINDOW, &type, &format, &count, + &remaining, (unsigned char **)&value )) + { + if (type == XA_WINDOW && format == 32) window = *value; + XFree( value ); + } - if (hwnd == 0) value = root_window; - handle_state_change( serial, expect_serial, sizeof(value), &value, desired, pending, - current, expected, "", received, NULL ); + return window; } void net_active_window_init( struct x11drv_thread_data *data ) { - Window window = get_net_active_window( data->display, &data->active_window ); + Window window = get_net_active_window( data->display ); - if (hwnd_from_window( data->display, window ) == 0) window = root_window; - data->desired_net_active_window = window; - data->pending_net_active_window = window; - data->current_net_active_window = window; + data->desired_state.net_active_window = window; + data->pending_state.net_active_window = window; + data->current_state.net_active_window = window; } static BOOL window_set_pending_activate( HWND hwnd ) @@ -2042,9 +2062,9 @@ void set_net_active_window( HWND hwnd, HWND previous ) Window window; XEvent xev; - if (!is_netwm_supported( x11drv_atom(_NET_ACTIVE_WINDOW) )) return; + if (!is_net_supported( x11drv_atom(_NET_ACTIVE_WINDOW) )) return; if (!(window = X11DRV_get_whole_window( hwnd ))) return; - if (data->pending_net_active_window == window) return; + if (data->pending_state.net_active_window == window) return; if (window_set_pending_activate( hwnd )) return; xev.xclient.type = ClientMessage; @@ -2060,7 +2080,7 @@ void set_net_active_window( HWND hwnd, HWND previous ) xev.xclient.data.l[3] = 0; xev.xclient.data.l[4] = 0; - data->pending_net_active_window = window; + data->pending_state.net_active_window = window; data->net_active_window_serial = NextRequest( data->display ); TRACE( "requesting _NET_ACTIVE_WINDOW %p/%lx serial %lu\n", hwnd, window, data->net_active_window_serial ); XSendEvent( data->display, DefaultRootWindow( data->display ), False, @@ -3661,9 +3681,9 @@ LRESULT X11DRV_WindowMessage( HWND hwnd, UINT msg, WPARAM wp, LPARAM lp ) /*********************************************************************** - * is_netwm_supported + * is_net_supported */ -BOOL is_netwm_supported( Atom atom ) +BOOL is_net_supported( Atom atom ) { struct x11drv_thread_data *data = x11drv_thread_data(); BOOL supported; @@ -3755,7 +3775,7 @@ LRESULT X11DRV_SysCommand( HWND hwnd, WPARAM wparam, LPARAM lparam, const POINT if (NtUserGetWindowLongW( hwnd, GWL_STYLE ) & WS_MAXIMIZE) goto failed; - if (!is_netwm_supported( x11drv_atom(_NET_WM_MOVERESIZE) )) + if (!is_net_supported( x11drv_atom(_NET_WM_MOVERESIZE) )) { TRACE( "_NET_WM_MOVERESIZE not supported\n" ); goto failed; @@ -3812,7 +3832,7 @@ void net_supported_init( struct x11drv_thread_data *data ) for (i = 0; i < NB_NET_WM_STATES; i++) { Atom atom = X11DRV_Atoms[net_wm_state_atoms[i] - FIRST_XATOM]; - if (is_netwm_supported( atom )) data->net_wm_state_mask |= (1 << i); + if (is_net_supported( atom )) data->net_wm_state_mask |= (1 << i); } } diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index 429b6e48a8d8..d2b2ca6a442d 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -385,6 +385,11 @@ extern RECT host_window_configure_child( struct host_window *win, Window window, extern POINT host_window_map_point( struct host_window *win, int x, int y ); extern struct host_window *get_host_window( Window window, BOOL create ); +struct display_state +{ + Window net_active_window; +}; + struct x11drv_thread_data { Display *display; @@ -411,10 +416,10 @@ struct x11drv_thread_data int xinput2_rawinput; /* XInput2 rawinput-only thread */ #endif /* HAVE_X11_EXTENSIONS_XINPUT2_H */ - Window desired_net_active_window; /* active window tracking the desired / win32 state */ - Window pending_net_active_window; /* active window tracking the pending / requested state */ - Window current_net_active_window; /* active window tracking the current X11 state */ - unsigned long net_active_window_serial; /* serial of last pending _NET_ACTIVE_WINDOW request */ + struct display_state desired_state; /* display state tracking the desired / win32 state */ + struct display_state pending_state; /* display state tracking the pending / requested state */ + struct display_state current_state; /* display state tracking the current X11 state */ + unsigned long net_active_window_serial; /* serial of last pending _NET_ACTIVE_WINDOW request */ }; extern struct x11drv_thread_data *x11drv_init_thread_data(void); @@ -704,12 +709,12 @@ extern void window_configure_notify( struct x11drv_win_data *data, unsigned long extern BOOL get_window_name( Display *display, Window window, char **name ); extern void set_net_active_window( HWND hwnd, HWND previous ); +extern Window get_net_active_window( Display *display ); extern void net_active_window_notify( unsigned long serial, Window window, Time time ); -extern Window get_net_active_window( Display *display, char **name ); extern void net_active_window_init( struct x11drv_thread_data *data ); extern void net_supported_init( struct x11drv_thread_data *data ); extern void net_supporting_wm_check_init( struct x11drv_thread_data *data ); -extern BOOL is_netwm_supported( Atom atom ); +extern BOOL is_net_supported( Atom atom ); extern Window init_clip_window(void); extern void update_user_time( struct x11drv_win_data *data, Time time, BOOL force ); From 17ef2f2d23a1eff7db7d1c99aec848e7bc8da833 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 22 May 2025 22:15:12 +0200 Subject: [PATCH 142/454] fixup! HACK: winex11: Try harder to take focus away from Steam. --- dlls/winex11.drv/event.c | 8 ++++++++ dlls/winex11.drv/window.c | 2 ++ 2 files changed, 10 insertions(+) diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c index 2d3f25072f9a..ec39fd3396c9 100644 --- a/dlls/winex11.drv/event.c +++ b/dlls/winex11.drv/event.c @@ -1394,7 +1394,15 @@ static void handle_net_supporting_wm_check_notify( XPropertyEvent *event ) static void handle_net_active_window( XPropertyEvent *event ) { + struct x11drv_thread_data *data = x11drv_thread_data(); Window window = 0; + + if (data->active_window) + { + XFree( data->active_window ); + data->active_window = NULL; + } + if (event->state == PropertyNewValue) window = get_net_active_window( event->display ); net_active_window_notify( event->serial, window, event->time ); } diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index 70c8231d75b4..e0294f72a982 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -2042,6 +2042,8 @@ void net_active_window_init( struct x11drv_thread_data *data ) data->desired_state.net_active_window = window; data->pending_state.net_active_window = window; data->current_state.net_active_window = window; + + if (window) get_window_name( data->display, window, &data->active_window ); } static BOOL window_set_pending_activate( HWND hwnd ) From 3438e2e3f91f475a9f13d69e9634810c58775034 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 22 May 2025 22:32:17 +0200 Subject: [PATCH 143/454] fixup! winex11: Set _NET_WM_USER_TIME to 0 to implement SWP_NOACTIVATE. --- dlls/winex11.drv/event.c | 2 +- dlls/winex11.drv/keyboard.c | 2 +- dlls/winex11.drv/mouse.c | 2 +- dlls/winex11.drv/window.c | 127 ++++++++++++++++++------------------ dlls/winex11.drv/x11drv.h | 4 +- 5 files changed, 69 insertions(+), 68 deletions(-) diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c index ec39fd3396c9..e9e7efd6965d 100644 --- a/dlls/winex11.drv/event.c +++ b/dlls/winex11.drv/event.c @@ -1322,7 +1322,7 @@ static void handle_wm_state_notify( HWND hwnd, XPropertyEvent *event ) if (!(data = get_win_data( hwnd ))) return; if (event->state == PropertyNewValue) value = get_window_wm_state( event->display, event->window ); window_wm_state_notify( data, event->serial, value, event->time ); - activate = value == NormalState && !data->wm_state_serial && !(data->current_state.swp_flags & SWP_NOACTIVATE); + activate = value == NormalState && !data->wm_state_serial && data->current_state.activate; release_win_data( data ); if (hwnd == NtUserGetForegroundWindow() && activate) set_net_active_window( hwnd, 0 ); diff --git a/dlls/winex11.drv/keyboard.c b/dlls/winex11.drv/keyboard.c index 0456d819bc8c..3907828201f5 100644 --- a/dlls/winex11.drv/keyboard.c +++ b/dlls/winex11.drv/keyboard.c @@ -1353,7 +1353,7 @@ BOOL X11DRV_KeyEvent( HWND hwnd, XEvent *xev ) if (event->type == KeyPress && (data = get_win_data( hwnd ))) { - update_user_time( data, event->time, FALSE ); + window_set_user_time( data, event->time, FALSE ); release_win_data( data ); } diff --git a/dlls/winex11.drv/mouse.c b/dlls/winex11.drv/mouse.c index bfffd51a0766..902776c49726 100644 --- a/dlls/winex11.drv/mouse.c +++ b/dlls/winex11.drv/mouse.c @@ -1614,7 +1614,7 @@ BOOL X11DRV_ButtonPress( HWND hwnd, XEvent *xev ) if ((data = get_win_data( hwnd ))) { - update_user_time( data, event->time, FALSE ); + window_set_user_time( data, event->time, FALSE ); release_win_data( data ); } diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index e0294f72a982..88ddafdbce24 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -272,10 +272,7 @@ static void remove_startup_notification( struct x11drv_win_data *data ) return; if (!(id = getenv( "DESKTOP_STARTUP_ID" )) || !id[0]) return; - - TRACE( "Using DESKTOP_STARTUP_ID %s\n", debugstr_a(id) ); - - if ((src = strstr( id, "_TIME" ))) update_user_time( data, atol( src + 5 ), FALSE ); + if ((src = strstr( id, "_TIME" ))) window_set_user_time( data, atol( src + 5 ), FALSE ); pos = snprintf(message, sizeof(message), "remove: ID="); message[pos++] = '"'; @@ -934,9 +931,9 @@ static void set_size_hints( struct x11drv_win_data *data, DWORD style ) } -static void window_set_wm_state( struct x11drv_win_data *data, UINT new_state, UINT swp_flags ); +static void window_set_wm_state( struct x11drv_win_data *data, UINT new_state, BOOL activate ); -static void window_set_mwm_hints( struct x11drv_win_data *data, const MwmHints *new_hints, UINT swp_flags ) +static void window_set_mwm_hints( struct x11drv_win_data *data, const MwmHints *new_hints ) { const MwmHints *old_hints = &data->pending_state.mwm_hints; @@ -956,7 +953,7 @@ static void window_set_mwm_hints( struct x11drv_win_data *data, const MwmHints * /*********************************************************************** * set_mwm_hints */ -static void set_mwm_hints( struct x11drv_win_data *data, UINT style, UINT ex_style, UINT swp_flags ) +static void set_mwm_hints( struct x11drv_win_data *data, UINT style, UINT ex_style ) { MwmHints mwm_hints; @@ -995,7 +992,7 @@ static void set_mwm_hints( struct x11drv_win_data *data, UINT style, UINT ex_sty mwm_hints.status = 0; TRACE( "%p setting mwm hints to %s (style %x exstyle %x)\n", data->hwnd, debugstr_mwm_hints(&mwm_hints), style, ex_style ); - window_set_mwm_hints( data, &mwm_hints, swp_flags ); + window_set_mwm_hints( data, &mwm_hints ); } @@ -1140,7 +1137,7 @@ static void make_owner_managed( HWND hwnd ) * * Set all the window manager hints for a window. */ -static void set_wm_hints( struct x11drv_win_data *data, UINT swp_flags ) +static void set_wm_hints( struct x11drv_win_data *data ) { DWORD style, ex_style; @@ -1157,7 +1154,7 @@ static void set_wm_hints( struct x11drv_win_data *data, UINT swp_flags ) } set_size_hints( data, style ); - set_mwm_hints( data, style, ex_style, swp_flags ); + set_mwm_hints( data, style, ex_style ); set_style_hints( data, style, ex_style ); } @@ -1179,19 +1176,20 @@ Window init_clip_window(void) /*********************************************************************** - * update_user_time + * window_set_user_time */ -void update_user_time( struct x11drv_win_data *data, Time time, BOOL force ) +void window_set_user_time( struct x11drv_win_data *data, Time time, BOOL init ) { - if (force) NtUserSetProp( data->hwnd, focus_time_prop, (HANDLE)time ); - else if (!time) time = 1; /* time == 0 has reserved semantics */ + if (init && data->managed) NtUserSetProp( data->hwnd, focus_time_prop, (HANDLE)time ); + else if (!init && !time) time = 1; /* time == 0 has reserved semantics */ - if (force ? !data->user_time == !time : data->user_time == time) return; + if (init && !data->user_time == !time) return; + if (!init && data->user_time == time) return; data->user_time = time; TRACE( "window %p/%lx, requesting _NET_WM_USER_TIME %ld serial %lu\n", data->hwnd, data->whole_window, data->user_time, NextRequest( data->display ) ); - if (force && time) XDeleteProperty( data->display, data->whole_window, x11drv_atom(_NET_WM_USER_TIME) ); + if (init && time) XDeleteProperty( data->display, data->whole_window, x11drv_atom(_NET_WM_USER_TIME) ); else XChangeProperty( data->display, data->whole_window, x11drv_atom(_NET_WM_USER_TIME), XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&time, 1 ); } @@ -1344,7 +1342,7 @@ static void window_set_net_wm_state( struct x11drv_win_data *data, UINT new_stat XFlush( data->display ); } -static void window_set_config( struct x11drv_win_data *data, const RECT *new_rect, BOOL above, UINT swp_flags ) +static void window_set_config( struct x11drv_win_data *data, const RECT *new_rect, BOOL above ) { static const UINT fullscreen_mask = (1 << NET_WM_STATE_MAXIMIZED) | (1 << NET_WM_STATE_FULLSCREEN); UINT style = NtUserGetWindowLongW( data->hwnd, GWL_STYLE ), mask = 0, net_wm_state = -1; @@ -1570,13 +1568,13 @@ static int skip_iconify(void) return cached; } -static void window_set_wm_state( struct x11drv_win_data *data, UINT new_state, UINT swp_flags ) +static void window_set_wm_state( struct x11drv_win_data *data, UINT new_state, BOOL activate ) { UINT old_state = data->pending_state.wm_state; HWND foreground = NtUserGetForegroundWindow(); data->desired_state.wm_state = new_state; - data->desired_state.swp_flags = swp_flags; + data->desired_state.activate = activate; if (!data->whole_window) return; /* no window, nothing to update */ if (data->wm_state_serial) return; /* another WM_STATE update is pending, wait for it to complete */ if (old_state == new_state) return; /* states are the same, nothing to update */ @@ -1592,8 +1590,8 @@ static void window_set_wm_state( struct x11drv_win_data *data, UINT new_state, U && MAKELONG(old_state, new_state) == MAKELONG(IconicState, NormalState)) { WARN( "window %p/%lx is iconic, remapping to workaround Mutter issues.\n", data->hwnd, data->whole_window ); - window_set_wm_state( data, WithdrawnState, 0 ); - window_set_wm_state( data, NormalState, swp_flags ); + window_set_wm_state( data, WithdrawnState, FALSE ); + window_set_wm_state( data, NormalState, activate ); return; } @@ -1602,22 +1600,22 @@ static void window_set_wm_state( struct x11drv_win_data *data, UINT new_state, U case MAKELONG(WithdrawnState, IconicState): case MAKELONG(WithdrawnState, NormalState): remove_startup_notification( data ); - set_wm_hints( data, swp_flags ); + set_wm_hints( data ); update_net_wm_states( data ); sync_window_style( data ); update_net_wm_fullscreen_monitors( data ); break; case MAKELONG(IconicState, NormalState): case MAKELONG(NormalState, IconicState): - set_wm_hints( data, swp_flags ); + set_wm_hints( data ); break; } if (new_state == NormalState) { /* try forcing activation if the window is supposed to be foreground or if it is fullscreen */ - if (data->hwnd == foreground || data->is_fullscreen) swp_flags = 0; - if (swp_flags & SWP_NOACTIVATE) update_user_time( data, 0, TRUE ); + if (data->hwnd == foreground || data->is_fullscreen) activate = TRUE; + if (!activate) window_set_user_time( data, 0, TRUE ); else { /* Some older Mutter versions get confused when mapping a window while another has focus @@ -1627,15 +1625,15 @@ static void window_set_wm_state( struct x11drv_win_data *data, UINT new_state, U * the window to force it to be focused instead. */ if (X11DRV_HasWindowManager( "Mutter" )) XSetInputFocus( data->display, None, RevertToNone, CurrentTime ); - update_user_time( data, -1, TRUE ); + window_set_user_time( data, -1, TRUE ); } } data->pending_state.wm_state = new_state; - data->pending_state.swp_flags = swp_flags; + data->pending_state.activate = activate; data->wm_state_serial = NextRequest( data->display ); - TRACE( "window %p/%lx, requesting WM_STATE %#x -> %#x serial %lu, foreground %p\n", data->hwnd, data->whole_window, - old_state, new_state, data->wm_state_serial, NtUserGetForegroundWindow() ); + TRACE( "window %p/%lx, requesting WM_STATE %#x -> %#x serial %lu, foreground %p, activate %u\n", data->hwnd, data->whole_window, + old_state, new_state, data->wm_state_serial, NtUserGetForegroundWindow(), activate ); if (new_state == IconicState && X11DRV_HasWindowManager( "steamcompmgr" ) && skip_iconify()) { @@ -1686,31 +1684,30 @@ static void window_set_wm_state( struct x11drv_win_data *data, UINT new_state, U XFlush( data->display ); } -static void window_set_managed( struct x11drv_win_data *data, BOOL new_managed, BOOL new_embedded ) +static void window_set_managed( struct x11drv_win_data *data, BOOL new_managed ) { - UINT wm_state = data->desired_state.wm_state, swp_flags = data->desired_state.swp_flags; XSetWindowAttributes attr = {.override_redirect = !new_managed}; - BOOL old_managed = data->managed, old_embedded = data->embedded; + UINT wm_state = data->desired_state.wm_state, activate = data->desired_state.activate; + BOOL old_managed = data->managed; if (!data->whole_window) return; /* no window, nothing to update */ - if (old_managed == new_managed && old_embedded == new_embedded) return; /* states are the same, nothing to update */ + if (old_managed == new_managed) return; /* states are the same, nothing to update */ - window_set_wm_state( data, WithdrawnState, 0 ); /* no WM_STATE is pending, requested immediately */ + window_set_wm_state( data, WithdrawnState, FALSE ); /* no WM_STATE is pending, requested immediately */ data->managed = new_managed; - data->embedded = new_embedded; TRACE( "window %p/%lx, requesting override-redirect %u -> %u serial %lu\n", data->hwnd, data->whole_window, !old_managed, !new_managed, NextRequest( data->display ) ); XChangeWindowAttributes( data->display, data->whole_window, CWOverrideRedirect, &attr ); - window_set_wm_state( data, wm_state, swp_flags ); /* queue another WM_STATE request with the desired state */ + window_set_wm_state( data, wm_state, activate ); /* queue another WM_STATE request with the desired state */ } /*********************************************************************** * map_window */ -static void map_window( HWND hwnd, DWORD new_style, BOOL swp_flags ) +static void map_window( HWND hwnd, DWORD new_style, BOOL activate ) { struct x11drv_win_data *data; @@ -1718,7 +1715,7 @@ static void map_window( HWND hwnd, DWORD new_style, BOOL swp_flags ) if (!(data = get_win_data( hwnd ))) return; TRACE( "win %p/%lx\n", data->hwnd, data->whole_window ); - window_set_wm_state( data, (new_style & WS_MINIMIZE) ? IconicState : NormalState, swp_flags ); + window_set_wm_state( data, (new_style & WS_MINIMIZE) ? IconicState : NormalState, activate ); release_win_data( data ); } @@ -1933,13 +1930,13 @@ void window_wm_state_notify( struct x11drv_win_data *data, unsigned long serial, if (!handle_state_change( serial, expect_serial, sizeof(value), &value, desired, pending, current, expected, prefix, received, reason )) return; - data->current_state.swp_flags = data->pending_state.swp_flags; + data->current_state.activate = data->pending_state.activate; /* send any pending changes from the desired state */ - window_set_wm_state( data, data->desired_state.wm_state, data->desired_state.swp_flags ); + window_set_wm_state( data, data->desired_state.wm_state, data->desired_state.activate ); window_set_net_wm_state( data, data->desired_state.net_wm_state ); - window_set_config( data, &data->desired_state.rect, FALSE, data->desired_state.swp_flags ); - window_set_mwm_hints( data, &data->desired_state.mwm_hints, data->desired_state.swp_flags ); + window_set_config( data, &data->desired_state.rect, FALSE ); + window_set_mwm_hints( data, &data->desired_state.mwm_hints ); if (data->current_state.wm_state == NormalState) NtUserSetProp( data->hwnd, focus_time_prop, (HANDLE)time ); else if (!data->wm_state_serial) NtUserRemoveProp( data->hwnd, focus_time_prop ); @@ -1963,10 +1960,10 @@ void window_net_wm_state_notify( struct x11drv_win_data *data, unsigned long ser data->net_wm_state_hack = 0; /* send any pending changes from the desired state */ - window_set_wm_state( data, data->desired_state.wm_state, data->desired_state.swp_flags ); + window_set_wm_state( data, data->desired_state.wm_state, data->desired_state.activate ); window_set_net_wm_state( data, data->desired_state.net_wm_state ); - window_set_config( data, &data->desired_state.rect, FALSE, data->desired_state.swp_flags ); - window_set_mwm_hints( data, &data->desired_state.mwm_hints, data->desired_state.swp_flags ); + window_set_config( data, &data->desired_state.rect, FALSE ); + window_set_mwm_hints( data, &data->desired_state.mwm_hints ); } void window_mwm_hints_notify( struct x11drv_win_data *data, unsigned long serial, const MwmHints *value ) @@ -2052,7 +2049,7 @@ static BOOL window_set_pending_activate( HWND hwnd ) BOOL pending; if (!(data = get_win_data( hwnd ))) return FALSE; - if ((pending = !!data->wm_state_serial)) data->pending_state.swp_flags &= ~SWP_NOACTIVATE; + if ((pending = !!data->wm_state_serial)) data->pending_state.activate = TRUE; release_win_data( data ); return pending; @@ -2114,7 +2111,12 @@ BOOL window_should_take_focus( HWND hwnd, Time time ) */ void make_window_embedded( struct x11drv_win_data *data ) { - window_set_managed( data, TRUE, TRUE ); + /* the window cannot be mapped before being embedded */ + window_set_wm_state( data, WithdrawnState, FALSE ); + if (data->managed) WARN( "Window is already managed, should wait for WithdrawnState\n" ); + else window_set_managed( data, TRUE ); + data->embedded = TRUE; + window_set_wm_state( data, NormalState, FALSE ); } @@ -2153,8 +2155,8 @@ static void sync_window_position( struct x11drv_win_data *data, UINT swp_flags, if (data->is_offscreen) OffsetRect( &new_rect, window_rect.left - old_rects->window.left, window_rect.top - old_rects->window.top ); - window_set_config( data, &new_rect, above, swp_flags ); - set_mwm_hints( data, style, ex_style, swp_flags ); + window_set_config( data, &new_rect, above ); + set_mwm_hints( data, style, ex_style ); } @@ -2507,10 +2509,10 @@ static void create_whole_window( struct x11drv_win_data *data ) data->desired_state.rect = data->current_state.rect; /* Set override-redirect attribute only after window creation, Mutter gets confused otherwise */ - window_set_managed( data, is_window_managed( data->hwnd, SWP_NOACTIVATE, FALSE ), FALSE ); + window_set_managed( data, is_window_managed( data->hwnd, SWP_NOACTIVATE, FALSE ) ); x11drv_xinput2_enable( data->display, data->whole_window ); set_initial_wm_hints( data->display, data->whole_window ); - set_wm_hints( data, 0 ); + set_wm_hints( data ); XSaveContext( data->display, data->whole_window, winContext, (char *)data->hwnd ); NtUserSetProp( data->hwnd, whole_window_prop, (HANDLE)data->whole_window ); @@ -2564,7 +2566,6 @@ static void destroy_whole_window( struct x11drv_win_data *data, BOOL already_des data->whole_window = data->client_window = 0; data->whole_colormap = 0; data->managed = FALSE; - data->embedded = FALSE; memset( &data->desired_state, 0, sizeof(data->desired_state) ); memset( &data->pending_state, 0, sizeof(data->pending_state) ); @@ -2651,7 +2652,7 @@ void X11DRV_SetWindowStyle( HWND hwnd, INT offset, STYLESTRUCT *style ) if (!(data = get_win_data( hwnd ))) return; if (!data->whole_window) goto done; - if (offset == GWL_STYLE && (changed & WS_DISABLED)) set_wm_hints( data, 0 ); + if (offset == GWL_STYLE && (changed & WS_DISABLED)) set_wm_hints( data ); if (offset == GWL_EXSTYLE && (changed & WS_EX_LAYERED)) /* changing WS_EX_LAYERED resets attributes */ { @@ -2717,7 +2718,7 @@ static BOOL create_desktop_win_data( Window win, HWND hwnd ) if (!(data = alloc_win_data( display, hwnd ))) return FALSE; data->whole_window = win; - window_set_managed( data, TRUE, FALSE ); + window_set_managed( data, TRUE ); NtUserSetProp( data->hwnd, whole_window_prop, (HANDLE)win ); set_initial_wm_hints( display, win ); if (is_desktop_fullscreen()) window_set_net_wm_state( data, fullscreen_mask ); @@ -2964,7 +2965,7 @@ BOOL X11DRV_SystrayDockRemove( HWND hwnd ) if ((data = get_win_data( hwnd ))) { - if ((ret = data->embedded)) window_set_wm_state( data, WithdrawnState, 0 ); + if ((ret = data->embedded)) window_set_wm_state( data, WithdrawnState, FALSE ); release_win_data( data ); } @@ -3331,12 +3332,12 @@ void X11DRV_WindowPosChanged( HWND hwnd, HWND insert_after, HWND owner_hint, UIN struct x11drv_win_data *data; UINT new_style = NtUserGetWindowLongW( hwnd, GWL_STYLE ), old_style; struct window_rects old_rects; - BOOL was_fullscreen; + BOOL was_fullscreen, activate = !(swp_flags & SWP_NOACTIVATE); set_surface_window_rects( surface, new_rects ); if (!(data = get_win_data( hwnd ))) return; - if (is_window_managed( hwnd, swp_flags, fullscreen )) window_set_managed( data, TRUE, data->embedded ); + if (is_window_managed( hwnd, swp_flags, fullscreen )) window_set_managed( data, TRUE ); old_style = new_style & ~(WS_VISIBLE | WS_MINIMIZE | WS_MAXIMIZE); if (data->desired_state.wm_state != WithdrawnState) old_style |= WS_VISIBLE; @@ -3420,17 +3421,17 @@ void X11DRV_WindowPosChanged( HWND hwnd, HWND insert_after, HWND owner_hint, UIN needs_map = data->layered || IsRectEmpty( &new_rects->window ); release_win_data( data ); if (needs_icon) fetch_icon_data( hwnd, 0, 0 ); - if (needs_map) map_window( hwnd, new_style, swp_flags ); + if (needs_map) map_window( hwnd, new_style, activate ); return; } else if ((swp_flags & SWP_STATECHANGED) && ((old_style ^ new_style) & WS_MINIMIZE)) { - window_set_wm_state( data, (new_style & WS_MINIMIZE) ? IconicState : NormalState, swp_flags ); + window_set_wm_state( data, (new_style & WS_MINIMIZE) ? IconicState : NormalState, activate ); update_net_wm_states( data ); } else { - if (swp_flags & (SWP_FRAMECHANGED|SWP_STATECHANGED)) set_wm_hints( data, swp_flags ); + if (swp_flags & (SWP_FRAMECHANGED|SWP_STATECHANGED)) set_wm_hints( data ); update_net_wm_states( data ); } } @@ -3521,7 +3522,7 @@ void X11DRV_SetWindowIcon( HWND hwnd, UINT type, HICON icon ) else fetch_icon_data( hwnd, 0, icon ); if (!(data = get_win_data( hwnd ))) return; - set_wm_hints( data, 0 ); + set_wm_hints( data ); done: release_win_data( data ); } @@ -3573,7 +3574,7 @@ void X11DRV_SetLayeredWindowAttributes( HWND hwnd, COLORREF key, BYTE alpha, DWO ((style & WS_MINIMIZE) || is_window_rect_mapped( &data->rects.window ))) { release_win_data( data ); - map_window( hwnd, style, 0 ); + map_window( hwnd, style, TRUE ); return; } } @@ -3610,7 +3611,7 @@ void X11DRV_UpdateLayeredWindow( HWND hwnd, UINT flags ) DWORD style = NtUserGetWindowLongW( hwnd, GWL_STYLE ); if ((style & WS_VISIBLE) && ((style & WS_MINIMIZE) || is_window_rect_mapped( &data->rects.window ))) - map_window( hwnd, style, 0 ); + map_window( hwnd, style, TRUE ); } } diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index d2b2ca6a442d..0503760126be 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -640,8 +640,8 @@ enum x11drv_net_wm_state struct window_state { - UINT swp_flags; UINT wm_state; + BOOL activate; UINT net_wm_state; MwmHints mwm_hints; RECT rect; @@ -717,7 +717,7 @@ extern void net_supporting_wm_check_init( struct x11drv_thread_data *data ); extern BOOL is_net_supported( Atom atom ); extern Window init_clip_window(void); -extern void update_user_time( struct x11drv_win_data *data, Time time, BOOL force ); +extern void window_set_user_time( struct x11drv_win_data *data, Time time, BOOL init ); extern UINT get_window_net_wm_state( Display *display, Window window ); extern void make_window_embedded( struct x11drv_win_data *data ); extern Window create_client_window( HWND hwnd, RECT client_rect, const XVisualInfo *visual, Colormap colormap ); From a2232104535a3bd4f98f48de5a031907c27325fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Sat, 7 Dec 2024 12:17:23 +0100 Subject: [PATCH 144/454] winex11: Track _MOTIF_WM_HINTS property in the window state. (cherry picked from commit 821da36ab8c206a0db68e2289e45891a91c462ca) --- dlls/winex11.drv/event.c | 1 - dlls/winex11.drv/window.c | 7 ++++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c index e9e7efd6965d..82ee4debb20f 100644 --- a/dlls/winex11.drv/event.c +++ b/dlls/winex11.drv/event.c @@ -1307,7 +1307,6 @@ static void get_window_mwm_hints( Display *display, Window window, MwmHints *hin } } - /*********************************************************************** * handle_wm_state_notify * diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index 88ddafdbce24..c21a720b7529 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -938,7 +938,7 @@ static void window_set_mwm_hints( struct x11drv_win_data *data, const MwmHints * const MwmHints *old_hints = &data->pending_state.mwm_hints; data->desired_state.mwm_hints = *new_hints; - if (!data->whole_window) return; /* no window, nothing to update */ + if (!data->whole_window || !data->managed) return; /* no window or not managed, nothing to update */ if (!memcmp( old_hints, new_hints, sizeof(*new_hints) )) return; /* hints are the same, nothing to update */ data->pending_state.mwm_hints = *new_hints; @@ -990,8 +990,6 @@ static void set_mwm_hints( struct x11drv_win_data *data, UINT style, UINT ex_sty mwm_hints.flags = MWM_HINTS_FUNCTIONS | MWM_HINTS_DECORATIONS; mwm_hints.input_mode = 0; mwm_hints.status = 0; - TRACE( "%p setting mwm hints to %s (style %x exstyle %x)\n", - data->hwnd, debugstr_mwm_hints(&mwm_hints), style, ex_style ); window_set_mwm_hints( data, &mwm_hints ); } @@ -1742,6 +1740,7 @@ static UINT window_update_client_state( struct x11drv_win_data *data ) if (data->wm_state_serial) return 0; /* another WM_STATE update is pending, wait for it to complete */ if (data->net_wm_state_serial) return 0; /* another _NET_WM_STATE update is pending, wait for it to complete */ + if (data->mwm_hints_serial) return 0; /* another MWM_HINT update is pending, wait for it to complete */ if (data->configure_serial) return 0; /* another config update is pending, wait for it to complete */ new_style = old_style & ~(WS_VISIBLE | WS_MINIMIZE | WS_MAXIMIZE); @@ -1795,6 +1794,7 @@ static UINT window_update_client_config( struct x11drv_win_data *data ) if (data->wm_state_serial) return 0; /* another WM_STATE update is pending, wait for it to complete */ if (data->net_wm_state_serial) return 0; /* another _NET_WM_STATE update is pending, wait for it to complete */ + if (data->mwm_hints_serial) return 0; /* another MWM_HINT update is pending, wait for it to complete */ if (data->configure_serial) return 0; /* another config update is pending, wait for it to complete */ new_style = old_style & ~(WS_VISIBLE | WS_MINIMIZE | WS_MAXIMIZE); @@ -2572,6 +2572,7 @@ static void destroy_whole_window( struct x11drv_win_data *data, BOOL already_des memset( &data->current_state, 0, sizeof(data->current_state) ); data->wm_state_serial = 0; data->net_wm_state_serial = 0; + data->mwm_hints_serial = 0; data->configure_serial = 0; if (data->xic) From 66c69274bb14165111f18a0d778063499ae0eb73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 19 May 2025 10:22:58 +0200 Subject: [PATCH 145/454] winex11: Trace more window change request serials. (cherry picked from commit e4c2444d20a5765e84c49aeda95912c55cadd25a) --- dlls/winex11.drv/window.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index c21a720b7529..05ea86226d4b 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -539,6 +539,8 @@ static void sync_window_style( struct x11drv_win_data *data ) XSetWindowAttributes attr; int mask = get_window_attributes( data, &attr ); + TRACE( "window %p/%lx changing attributes mask %#x, serial %lu\n", data->hwnd, + data->whole_window, mask, NextRequest( data->display ) ); XChangeWindowAttributes( data->display, data->whole_window, mask, &attr ); x11drv_xinput2_enable( data->display, data->whole_window ); } @@ -926,6 +928,9 @@ static void set_size_hints( struct x11drv_win_data *data, DWORD style ) size_hints->flags |= PMinSize | PMaxSize; } } + + TRACE( "window %p/%lx requesting WM_NORMAL_HINTS flags %#lx, serial %lu\n", data->hwnd, + data->whole_window, size_hints->flags, NextRequest( data->display ) ); XSetWMNormalHints( data->display, data->whole_window, size_hints ); XFree( size_hints ); } @@ -1026,6 +1031,8 @@ static void set_style_hints( struct x11drv_win_data *data, DWORD style, DWORD ex else window_type = x11drv_atom(_NET_WM_WINDOW_TYPE_NORMAL); + TRACE( "window %p/%lx requesting _NET_WM_WINDOW_TYPE %#lx, serial %lu\n", data->hwnd, + data->whole_window, window_type, NextRequest( data->display ) ); XChangeProperty(data->display, data->whole_window, x11drv_atom(_NET_WM_WINDOW_TYPE), XA_ATOM, 32, PropModeReplace, (unsigned char*)&window_type, 1); @@ -1041,16 +1048,27 @@ static void set_style_hints( struct x11drv_win_data *data, DWORD style, DWORD ex wm_hints->icon_mask = data->icon_mask; wm_hints->flags |= IconPixmapHint | IconMaskHint; } + + TRACE( "window %p/%lx requesting WM_HINTS flags %#lx, serial %lu\n", data->hwnd, + data->whole_window, wm_hints->flags, NextRequest( data->display ) ); XSetWMHints( data->display, data->whole_window, wm_hints ); XFree( wm_hints ); } if (data->icon_bits) + { + TRACE( "window %p/%lx requesting _NET_WM_ICON, serial %lu\n", data->hwnd, + data->whole_window, NextRequest( data->display ) ); XChangeProperty( data->display, data->whole_window, x11drv_atom(_NET_WM_ICON), XA_CARDINAL, 32, PropModeReplace, (unsigned char *)data->icon_bits, data->icon_size ); + } else + { + TRACE( "window %p/%lx deleting _NET_WM_ICON, serial %lu\n", data->hwnd, + data->whole_window, NextRequest( data->display ) ); XDeleteProperty( data->display, data->whole_window, x11drv_atom(_NET_WM_ICON) ); + } XChangeProperty( data->display, data->whole_window, x11drv_atom(_WINE_HWND_STYLE), XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&style, sizeof(style) / 4 ); From f2dcdbd64b55770991df5af18493d543132febf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 19 May 2025 18:59:50 +0200 Subject: [PATCH 146/454] winex11: Move managed window check to window_set_net_wm_state. (cherry picked from commit b9318e3805f9d000474411cb889719003805fa32) --- dlls/winex11.drv/window.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index 05ea86226d4b..fc1750297244 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -1284,7 +1284,7 @@ static void window_set_net_wm_state( struct x11drv_win_data *data, UINT new_stat new_state &= x11drv_thread_data()->net_wm_state_mask; data->desired_state.net_wm_state = new_state; - if (!data->whole_window) return; /* no window, nothing to update */ + if (!data->whole_window || !data->managed) return; /* no window or not managed, nothing to update */ if (data->wm_state_serial) return; /* another WM_STATE update is pending, wait for it to complete */ /* we ignore and override previous _NET_WM_STATE update requests */ if (old_state == new_state) return; /* states are the same, nothing to update */ @@ -1448,7 +1448,7 @@ static void update_net_wm_states( struct x11drv_win_data *data ) UINT style, ex_style, new_state = 0; BOOL disable_maximize; - if (!data->managed || data->embedded) return; + if (data->embedded) return; if (data->whole_window == root_window) { if (!is_virtual_desktop()) return; From 1397c12cb9697fb563222a459bf030885cb1decf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Thu, 22 May 2025 19:37:59 +0200 Subject: [PATCH 147/454] winex11: Update the current window state when ignoring events. Although we ignore them, the information still describes the current X window state and should be recorded. (cherry picked from commit 29840436f7e465e802e131f2b64954a80ce1ef79) --- dlls/winex11.drv/window.c | 1 + 1 file changed, 1 insertion(+) diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index fc1750297244..1f6cf1a54a01 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -1915,6 +1915,7 @@ static BOOL handle_state_change( unsigned long serial, unsigned long *expect_ser if (reason) { WARN( "Ignoring %s%s%s%s\n", prefix, reason, received, expected ); + memcpy( current, value, size ); return FALSE; } From 554a0ef3bc1d360c9a562c5ddde6beb99b658ded Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 23 May 2025 15:40:24 +0200 Subject: [PATCH 148/454] winex11: Continue requesting desired window state on no-op event. (cherry picked from commit e072358059abe8446e4ac9e89d2e0618629edf5f) --- dlls/winex11.drv/window.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index 1f6cf1a54a01..cc48a148d702 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -1910,8 +1910,6 @@ static BOOL handle_state_change( unsigned long serial, unsigned long *expect_ser const char *prefix, const char *received, const char *reason ) { if (serial < *expect_serial) reason = "old "; - else if (!*expect_serial && !memcmp( current, value, size )) reason = "no-op "; - if (reason) { WARN( "Ignoring %s%s%s%s\n", prefix, reason, received, expected ); @@ -1919,9 +1917,8 @@ static BOOL handle_state_change( unsigned long serial, unsigned long *expect_ser return FALSE; } - if (!*expect_serial) reason = "unexpected "; - else if (memcmp( pending, value, size )) reason = "mismatch "; - + if (!*expect_serial && memcmp( current, value, size )) reason = "unexpected "; + if (*expect_serial && memcmp( pending, value, size )) reason = "mismatch "; if (!reason) TRACE( "%s%s%s\n", prefix, received, expected ); else { From 5b7058dad13241ab5133fbe2822e8292394aeb90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 19 May 2025 11:47:53 +0200 Subject: [PATCH 149/454] winex11: Serialize individual _NET_WM_STATE bit changes. _NET_WM_STATE change requests are going through the window manager and the PropertyNotify events might be received with a serial that doesn't match its request. (cherry picked from commit 79b4fddfc67812f32c37cf2ae370ee0f05b5eff0) --- dlls/winex11.drv/window.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index cc48a148d702..b242f8c8c627 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -1330,6 +1330,7 @@ static void window_set_net_wm_state( struct x11drv_win_data *data, UINT new_stat for (i = 0; i < NB_NET_WM_STATES; i++) { + if (data->net_wm_state_serial) break; /* another _NET_WM_STATE update is pending, wait for it to complete */ if (!((old_state ^ new_state) & (1 << i))) continue; xev.xclient.data.l[0] = (new_state & (1 << i)) ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE; @@ -1337,7 +1338,7 @@ static void window_set_net_wm_state( struct x11drv_win_data *data, UINT new_stat xev.xclient.data.l[2] = ((net_wm_state_atoms[i] == XATOM__NET_WM_STATE_MAXIMIZED_VERT) ? x11drv_atom(_NET_WM_STATE_MAXIMIZED_HORZ) : 0); - data->pending_state.net_wm_state = new_state; + data->pending_state.net_wm_state ^= (1 << i); data->net_wm_state_serial = NextRequest( data->display ); TRACE( "window %p/%lx, requesting _NET_WM_STATE %#x serial %lu\n", data->hwnd, data->whole_window, data->pending_state.net_wm_state, data->net_wm_state_serial ); From c35df070cfc98b75c9d124e04957e229e601850a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 26 Nov 2024 19:47:59 +0100 Subject: [PATCH 150/454] winex11: Track window pending config position / size independently. (cherry picked from commit ff21cbc3babff33d4822d65ea0993ed56bc9c907) --- dlls/winex11.drv/window.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index b242f8c8c627..e4c940b8a62c 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -1359,12 +1359,13 @@ static void window_set_net_wm_state( struct x11drv_win_data *data, UINT new_stat XFlush( data->display ); } -static void window_set_config( struct x11drv_win_data *data, const RECT *new_rect, BOOL above ) +static void window_set_config( struct x11drv_win_data *data, RECT rect, BOOL above ) { static const UINT fullscreen_mask = (1 << NET_WM_STATE_MAXIMIZED) | (1 << NET_WM_STATE_FULLSCREEN); UINT style = NtUserGetWindowLongW( data->hwnd, GWL_STYLE ), mask = 0, net_wm_state = -1; const RECT *old_rect = &data->pending_state.rect; XWindowChanges changes; + RECT *new_rect = ▭ BOOL is_maximized; data->desired_state.rect = *new_rect; @@ -1408,6 +1409,11 @@ static void window_set_config( struct x11drv_win_data *data, const RECT *new_rec if (changes.height > 65535) changes.height = 65535; mask |= CWWidth | CWHeight; } + else + { + new_rect->right = new_rect->left + old_rect->right - old_rect->left; + new_rect->bottom = new_rect->top + old_rect->bottom - old_rect->top; + } /* only the size is allowed to change for the desktop window or systray docked windows */ if ((old_rect->left != new_rect->left || old_rect->top != new_rect->top) && @@ -1418,6 +1424,10 @@ static void window_set_config( struct x11drv_win_data *data, const RECT *new_rec changes.y = pt.y; mask |= CWX | CWY; } + else + { + OffsetRect( new_rect, old_rect->left - new_rect->left, old_rect->top - new_rect->top ); + } if (data->force_below_hack) { @@ -1952,7 +1962,7 @@ void window_wm_state_notify( struct x11drv_win_data *data, unsigned long serial, /* send any pending changes from the desired state */ window_set_wm_state( data, data->desired_state.wm_state, data->desired_state.activate ); window_set_net_wm_state( data, data->desired_state.net_wm_state ); - window_set_config( data, &data->desired_state.rect, FALSE ); + window_set_config( data, data->desired_state.rect, FALSE ); window_set_mwm_hints( data, &data->desired_state.mwm_hints ); if (data->current_state.wm_state == NormalState) NtUserSetProp( data->hwnd, focus_time_prop, (HANDLE)time ); @@ -1979,7 +1989,7 @@ void window_net_wm_state_notify( struct x11drv_win_data *data, unsigned long ser /* send any pending changes from the desired state */ window_set_wm_state( data, data->desired_state.wm_state, data->desired_state.activate ); window_set_net_wm_state( data, data->desired_state.net_wm_state ); - window_set_config( data, &data->desired_state.rect, FALSE ); + window_set_config( data, data->desired_state.rect, FALSE ); window_set_mwm_hints( data, &data->desired_state.mwm_hints ); } @@ -2172,7 +2182,7 @@ static void sync_window_position( struct x11drv_win_data *data, UINT swp_flags, if (data->is_offscreen) OffsetRect( &new_rect, window_rect.left - old_rects->window.left, window_rect.top - old_rects->window.top ); - window_set_config( data, &new_rect, above ); + window_set_config( data, new_rect, above ); set_mwm_hints( data, style, ex_style ); } From a1977d1c1a75cc49ecd84bd0d9064da2b161aa43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 23 May 2025 11:00:25 +0200 Subject: [PATCH 151/454] winex11: Keep track of the last config above flag used. Some window manager don't send any ConfigureNotify if nothing changed, we need to be careful and avoid sending unnecessary requests or we will wait for them forever as we're going to serialize the requests. (cherry picked from commit 1ae04bc748d00b1b044f970f9a3aec991f44adfe) --- dlls/winex11.drv/window.c | 11 ++++++++--- dlls/winex11.drv/x11drv.h | 1 + 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index e4c940b8a62c..7ce453cdc940 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -1364,13 +1364,15 @@ static void window_set_config( struct x11drv_win_data *data, RECT rect, BOOL abo static const UINT fullscreen_mask = (1 << NET_WM_STATE_MAXIMIZED) | (1 << NET_WM_STATE_FULLSCREEN); UINT style = NtUserGetWindowLongW( data->hwnd, GWL_STYLE ), mask = 0, net_wm_state = -1; const RECT *old_rect = &data->pending_state.rect; + BOOL old_above = data->pending_state.above; XWindowChanges changes; RECT *new_rect = ▭ BOOL is_maximized; data->desired_state.rect = *new_rect; + data->desired_state.above = above; if (!data->whole_window) return; /* no window, nothing to update */ - if (EqualRect( old_rect, new_rect ) && !above) return; /* rects are the same, no need to be raised, nothing to update */ + if (EqualRect( old_rect, new_rect ) && (old_above || !above)) return; /* rects are the same, no need to be raised, nothing to update */ /* Kwin internal maximized state tracking gets bogus if a window configure request is sent to a maximized * window, and it loses track of whether the window was maximized state. @@ -1441,6 +1443,7 @@ static void window_set_config( struct x11drv_win_data *data, RECT rect, BOOL abo } data->pending_state.rect = *new_rect; + data->pending_state.above = above; data->configure_serial = NextRequest( data->display ); TRACE( "window %p/%lx, requesting config %s above %u mask %#x, serial %lu\n", data->hwnd, data->whole_window, wine_dbgstr_rect(new_rect), above, mask, data->configure_serial ); @@ -2017,8 +2020,10 @@ void window_configure_notify( struct x11drv_win_data *data, unsigned long serial received = wine_dbg_sprintf( "config %s/%lu", wine_dbgstr_rect(value), serial ); expected = *expect_serial ? wine_dbg_sprintf( ", expected %s/%lu", wine_dbgstr_rect(pending), *expect_serial ) : ""; - handle_state_change( serial, expect_serial, sizeof(*value), value, desired, pending, - current, expected, prefix, received, NULL ); + if (!handle_state_change( serial, expect_serial, sizeof(*value), value, desired, pending, + current, expected, prefix, received, NULL )) + return; + data->pending_state.above = FALSE; /* allow requesting it again */ } void net_active_window_notify( unsigned long serial, Window value, Time time ) diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index 0503760126be..2e79e893e002 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -645,6 +645,7 @@ struct window_state UINT net_wm_state; MwmHints mwm_hints; RECT rect; + BOOL above; }; /* x11drv private window data */ From 991b84cdfe5abbe2b9cd7f463d2a1582ff34f483 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 23 May 2025 11:46:28 +0200 Subject: [PATCH 152/454] winex11: Serialize managed window config change requests. When using a window manager, config requests are going through the WM and their ConfigureNotify might be received with a different serial from the request that triggered them. We still want to override past and transient configs with our desired state as much as possible, and we ignore the received values if some changes were delayed, and request them instead. (cherry picked from commit bf0813e39470c5682c784b1db9812fb4ff5e13cc) --- dlls/winex11.drv/window.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index 7ce453cdc940..4c707c3e400c 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -935,6 +935,13 @@ static void set_size_hints( struct x11drv_win_data *data, DWORD style ) XFree( size_hints ); } +static BOOL window_needs_config_change_delay( struct x11drv_win_data *data ) +{ + static const UINT fullscreen_mask = (1 << NET_WM_STATE_MAXIMIZED) | (1 << NET_WM_STATE_FULLSCREEN); + if (data->pending_state.wm_state != NormalState) return FALSE; + if (data->configure_serial) return TRUE; /* another config update is pending, wait for it to complete */ + return data->net_wm_state_serial && !(data->pending_state.net_wm_state & fullscreen_mask) && (data->current_state.net_wm_state & fullscreen_mask); +} static void window_set_wm_state( struct x11drv_win_data *data, UINT new_state, BOOL activate ); @@ -1373,6 +1380,15 @@ static void window_set_config( struct x11drv_win_data *data, RECT rect, BOOL abo data->desired_state.above = above; if (!data->whole_window) return; /* no window, nothing to update */ if (EqualRect( old_rect, new_rect ) && (old_above || !above)) return; /* rects are the same, no need to be raised, nothing to update */ + if (data->managed && window_needs_config_change_delay( data )) + { + /* Some window managers are sending a ConfigureNotify event with the fullscreen size when + * exiting a fullscreen window, with a serial that we cannot predict. Handling that event + * will override the Win32 window size and make the window fullscreen again. + */ + WARN( "window %p/%lx is exiting maximize/fullscreen, delaying request\n", data->hwnd, data->whole_window ); + return; + } /* Kwin internal maximized state tracking gets bogus if a window configure request is sent to a maximized * window, and it loses track of whether the window was maximized state. @@ -2020,10 +2036,23 @@ void window_configure_notify( struct x11drv_win_data *data, unsigned long serial received = wine_dbg_sprintf( "config %s/%lu", wine_dbgstr_rect(value), serial ); expected = *expect_serial ? wine_dbg_sprintf( ", expected %s/%lu", wine_dbgstr_rect(pending), *expect_serial ) : ""; + /* if we've delayed some config we want to continue with it, make sure handle_state_change doesn't overwrite it */ + if ((*expect_serial || window_needs_config_change_delay( data )) && + serial >= *expect_serial && !EqualRect( desired, pending )) + { + WARN( "%spreserving delayed config %s\n", prefix, wine_dbgstr_rect(desired) ); + desired = pending; + } + if (!handle_state_change( serial, expect_serial, sizeof(*value), value, desired, pending, current, expected, prefix, received, NULL )) return; data->pending_state.above = FALSE; /* allow requesting it again */ + + /* send any pending changes from the desired state */ + window_set_wm_state( data, data->desired_state.wm_state, data->desired_state.activate ); + window_set_net_wm_state( data, data->desired_state.net_wm_state ); + window_set_config( data, data->desired_state.rect, FALSE ); } void net_active_window_notify( unsigned long serial, Window value, Time time ) From 5e1f59748117d54fff17ef4641896626647ec984 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 23 May 2025 11:47:08 +0200 Subject: [PATCH 153/454] winex11: Serialize window config requests with some other requests. When they will likely induce some ConfigureNotify events, we need to wait for these events to arrive before requesting our changes, as we are otherwise unable to tell which ConfigureNotify event is an old one and which one corresponds to our requests. (cherry picked from commit 290fd532ee7376442d272e3833528256bfe5e9dc) --- dlls/winex11.drv/window.c | 65 +++++++++++++++++++++++++++++++++------ 1 file changed, 55 insertions(+), 10 deletions(-) diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index 4c707c3e400c..cba3b097073f 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -935,12 +935,37 @@ static void set_size_hints( struct x11drv_win_data *data, DWORD style ) XFree( size_hints ); } +/* bits that can trigger spurious ConfigureNotify events */ +static const UINT config_notify_mask = (1 << NET_WM_STATE_MAXIMIZED) | (1 << NET_WM_STATE_FULLSCREEN) | + (1 << NET_WM_STATE_ABOVE); + +static BOOL window_needs_mwm_hints_change_delay( struct x11drv_win_data *data ) +{ + if (data->pending_state.wm_state == WithdrawnState) return FALSE; /* window is unmapped, should be safe to make any change */ + if (!data->configure_serial && !data->net_wm_state_serial) return FALSE; /* no other requests are pending, should be safe */ + /* check whether we have a pending configure, either directly or because of a _NET_WM_STATE change which might trigger one */ + if (!data->configure_serial && !((data->pending_state.net_wm_state ^ data->current_state.net_wm_state) & config_notify_mask)) return FALSE; + /* delay any new _MOTIF_WM_HINTS change which might trigger a ConfigureNotify when a config/_NET_WM_STATE change is pending */ + return (!data->desired_state.mwm_hints.decorations != !data->pending_state.mwm_hints.decorations); +} + +static BOOL window_needs_net_wm_state_change_delay( struct x11drv_win_data *data ) +{ + if (data->pending_state.wm_state == WithdrawnState) return FALSE; /* window is unmapped, should be safe to make any change */ + if (!data->configure_serial && !data->mwm_hints_serial) return FALSE; /* no other requests are pending, should be safe */ + /* check whether we have a pending configure, either directly or because _MOTIF_WM_HINTS decoration changed */ + if (!data->configure_serial && !(!data->pending_state.mwm_hints.decorations != !data->current_state.mwm_hints.decorations)) return FALSE; + /* delay any new _NET_WM_STATE change which might trigger a ConfigureNotify when a config/_MOTIF_WM_HINTS change is pending */ + return (data->desired_state.net_wm_state ^ data->pending_state.net_wm_state) & config_notify_mask; +} + static BOOL window_needs_config_change_delay( struct x11drv_win_data *data ) { - static const UINT fullscreen_mask = (1 << NET_WM_STATE_MAXIMIZED) | (1 << NET_WM_STATE_FULLSCREEN); - if (data->pending_state.wm_state != NormalState) return FALSE; + if (data->pending_state.wm_state == WithdrawnState) return FALSE; /* window is unmapped, should be safe to make any change */ if (data->configure_serial) return TRUE; /* another config update is pending, wait for it to complete */ - return data->net_wm_state_serial && !(data->pending_state.net_wm_state & fullscreen_mask) && (data->current_state.net_wm_state & fullscreen_mask); + /* delay any config request when a _NET_WM_STATE or _MOTIF_WM_HINTS change which might trigger a ConfigureNotify is in flight */ + return (data->net_wm_state_serial && (data->pending_state.net_wm_state ^ data->current_state.net_wm_state) & config_notify_mask) || + (data->mwm_hints_serial && (!data->pending_state.mwm_hints.decorations != !data->current_state.mwm_hints.decorations)); } static void window_set_wm_state( struct x11drv_win_data *data, UINT new_state, BOOL activate ); @@ -953,6 +978,14 @@ static void window_set_mwm_hints( struct x11drv_win_data *data, const MwmHints * if (!data->whole_window || !data->managed) return; /* no window or not managed, nothing to update */ if (!memcmp( old_hints, new_hints, sizeof(*new_hints) )) return; /* hints are the same, nothing to update */ + if (window_needs_mwm_hints_change_delay( data )) + { + TRACE( "window %p/%lx is updating _NET_WM_STATE/config, delaying request\n", data->hwnd, data->whole_window ); + return; + } + + if (data->pending_state.wm_state == IconicState) return; /* window is iconic and may be mapped or not, don't update its state now */ + data->pending_state.mwm_hints = *new_hints; data->mwm_hints_serial = NextRequest( data->display ); TRACE( "window %p/%lx, requesting _MOTIF_WM_HINTS %s serial %lu\n", data->hwnd, data->whole_window, @@ -1296,6 +1329,12 @@ static void window_set_net_wm_state( struct x11drv_win_data *data, UINT new_stat /* we ignore and override previous _NET_WM_STATE update requests */ if (old_state == new_state) return; /* states are the same, nothing to update */ + if (window_needs_net_wm_state_change_delay( data )) + { + TRACE( "window %p/%lx is updating config/_MOTIF_WM_HINTS, delaying request\n", data->hwnd, data->whole_window ); + return; + } + /* On KWin wait for _NET_WM_STATE changes to complete when they touch maximized / fullscreen states */ if (X11DRV_HasWindowManager( "KWin" ) && data->pending_state.wm_state == NormalState && data->net_wm_state_serial && (old_state ^ new_state) & fullscreen_mask) @@ -1382,11 +1421,7 @@ static void window_set_config( struct x11drv_win_data *data, RECT rect, BOOL abo if (EqualRect( old_rect, new_rect ) && (old_above || !above)) return; /* rects are the same, no need to be raised, nothing to update */ if (data->managed && window_needs_config_change_delay( data )) { - /* Some window managers are sending a ConfigureNotify event with the fullscreen size when - * exiting a fullscreen window, with a serial that we cannot predict. Handling that event - * will override the Win32 window size and make the window fullscreen again. - */ - WARN( "window %p/%lx is exiting maximize/fullscreen, delaying request\n", data->hwnd, data->whole_window ); + TRACE( "window %p/%lx is updating _NET_WM_STATE/_MOTIF_WM_HINTS, delaying request\n", data->hwnd, data->whole_window ); return; } @@ -1981,6 +2016,7 @@ void window_wm_state_notify( struct x11drv_win_data *data, unsigned long serial, /* send any pending changes from the desired state */ window_set_wm_state( data, data->desired_state.wm_state, data->desired_state.activate ); window_set_net_wm_state( data, data->desired_state.net_wm_state ); + window_set_mwm_hints( data, &data->desired_state.mwm_hints ); window_set_config( data, data->desired_state.rect, FALSE ); window_set_mwm_hints( data, &data->desired_state.mwm_hints ); @@ -2008,6 +2044,7 @@ void window_net_wm_state_notify( struct x11drv_win_data *data, unsigned long ser /* send any pending changes from the desired state */ window_set_wm_state( data, data->desired_state.wm_state, data->desired_state.activate ); window_set_net_wm_state( data, data->desired_state.net_wm_state ); + window_set_mwm_hints( data, &data->desired_state.mwm_hints ); window_set_config( data, data->desired_state.rect, FALSE ); window_set_mwm_hints( data, &data->desired_state.mwm_hints ); } @@ -2022,8 +2059,15 @@ void window_mwm_hints_notify( struct x11drv_win_data *data, unsigned long serial received = wine_dbg_sprintf( "_MOTIF_WM_HINTS %s/%lu", debugstr_mwm_hints(value), serial ); expected = *expect_serial ? wine_dbg_sprintf( ", expected %s/%lu", debugstr_mwm_hints(pending), *expect_serial ) : ""; - handle_state_change( serial, expect_serial, sizeof(*value), value, desired, pending, - current, expected, prefix, received, NULL ); + if (!handle_state_change( serial, expect_serial, sizeof(*value), value, desired, pending, + current, expected, prefix, received, NULL )) + return; + + /* send any pending changes from the desired state */ + window_set_wm_state( data, data->desired_state.wm_state, data->desired_state.activate ); + window_set_net_wm_state( data, data->desired_state.net_wm_state ); + window_set_mwm_hints( data, &data->desired_state.mwm_hints ); + window_set_config( data, data->desired_state.rect, FALSE ); } void window_configure_notify( struct x11drv_win_data *data, unsigned long serial, const RECT *value ) @@ -2052,6 +2096,7 @@ void window_configure_notify( struct x11drv_win_data *data, unsigned long serial /* send any pending changes from the desired state */ window_set_wm_state( data, data->desired_state.wm_state, data->desired_state.activate ); window_set_net_wm_state( data, data->desired_state.net_wm_state ); + window_set_mwm_hints( data, &data->desired_state.mwm_hints ); window_set_config( data, data->desired_state.rect, FALSE ); } From 8486a21fae4fe8d5522dc039a118761aaee6ffa2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 27 May 2025 11:27:07 +0200 Subject: [PATCH 154/454] winex11: Don't set _MOTIF_WM_HINTS / _NET_WM_STATE for embedded windows. (cherry picked from commit 062c3f65d6f8cf77146d9370fb8c9e44c9c08987) --- dlls/winex11.drv/window.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index cba3b097073f..66f26e1bcb5f 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -975,7 +975,7 @@ static void window_set_mwm_hints( struct x11drv_win_data *data, const MwmHints * const MwmHints *old_hints = &data->pending_state.mwm_hints; data->desired_state.mwm_hints = *new_hints; - if (!data->whole_window || !data->managed) return; /* no window or not managed, nothing to update */ + if (!data->whole_window || !data->managed || data->embedded) return; /* no window or not managed, nothing to update */ if (!memcmp( old_hints, new_hints, sizeof(*new_hints) )) return; /* hints are the same, nothing to update */ if (window_needs_mwm_hints_change_delay( data )) @@ -1324,7 +1324,7 @@ static void window_set_net_wm_state( struct x11drv_win_data *data, UINT new_stat new_state &= x11drv_thread_data()->net_wm_state_mask; data->desired_state.net_wm_state = new_state; - if (!data->whole_window || !data->managed) return; /* no window or not managed, nothing to update */ + if (!data->whole_window || !data->managed || data->embedded) return; /* no window or not managed, nothing to update */ if (data->wm_state_serial) return; /* another WM_STATE update is pending, wait for it to complete */ /* we ignore and override previous _NET_WM_STATE update requests */ if (old_state == new_state) return; /* states are the same, nothing to update */ From ded0979dfbe50851d9659282988b13df29caca2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 27 May 2025 13:51:33 +0200 Subject: [PATCH 155/454] winex11: Avoid requesting impossible state changes over and over. Storing impossible desired rect always trigger the delayed config preserve check in window_configure_notify with embedded windows. (cherry picked from commit 1c574b676614423b17da0227fad68c1d7967b246) --- dlls/winex11.drv/window.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index 66f26e1bcb5f..09518aed786d 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -1415,6 +1415,18 @@ static void window_set_config( struct x11drv_win_data *data, RECT rect, BOOL abo RECT *new_rect = ▭ BOOL is_maximized; + /* resizing a managed maximized window is not allowed */ + if ((style & WS_MAXIMIZE) && data->managed) + { + new_rect->right = new_rect->left + old_rect->right - old_rect->left; + new_rect->bottom = new_rect->top + old_rect->bottom - old_rect->top; + } + /* only the size is allowed to change for the desktop window or systray docked windows */ + if (data->whole_window == root_window || data->embedded) + { + OffsetRect( new_rect, old_rect->left - new_rect->left, old_rect->top - new_rect->top ); + } + data->desired_state.rect = *new_rect; data->desired_state.above = above; if (!data->whole_window) return; /* no window, nothing to update */ @@ -1449,10 +1461,8 @@ static void window_set_config( struct x11drv_win_data *data, RECT rect, BOOL abo /* Gamescope has broken _NET_WM_STATE_FULLSCREEN / _NET_WM_STATE_MAXIMIZED support, always allow resizing instead */ if (X11DRV_HasWindowManager( "steamcompmgr" )) style &= ~WS_MAXIMIZE; - /* resizing a managed maximized window is not allowed */ - if ((old_rect->right - old_rect->left != new_rect->right - new_rect->left || - old_rect->bottom - old_rect->top != new_rect->bottom - new_rect->top) && - (!(style & WS_MAXIMIZE) || !data->managed)) + if (old_rect->right - old_rect->left != new_rect->right - new_rect->left || + old_rect->bottom - old_rect->top != new_rect->bottom - new_rect->top) { changes.width = new_rect->right - new_rect->left; changes.height = new_rect->bottom - new_rect->top; @@ -1468,9 +1478,7 @@ static void window_set_config( struct x11drv_win_data *data, RECT rect, BOOL abo new_rect->bottom = new_rect->top + old_rect->bottom - old_rect->top; } - /* only the size is allowed to change for the desktop window or systray docked windows */ - if ((old_rect->left != new_rect->left || old_rect->top != new_rect->top) && - (data->whole_window != root_window && !data->embedded)) + if (old_rect->left != new_rect->left || old_rect->top != new_rect->top) { POINT pt = virtual_screen_to_root( new_rect->left, new_rect->top ); changes.x = pt.x; From 62295f249991e477602b498c8e324e3f3d6a660f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 27 May 2025 11:27:07 +0200 Subject: [PATCH 156/454] winex11: Don't delay config requests for embedded windows. Embedders aren't ICCCM compliant window managers and they sometimes don't even reply to config requests. Delaying is also now unnecessary as we don't request the problematic properties with embedded windows and as we keep requesting the desired configs if necessary. (cherry picked from commit dbbaba69cad00d6e593a3694db52dfe4fd5b7252) --- dlls/winex11.drv/window.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index 09518aed786d..a98f869ba8b5 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -961,6 +961,7 @@ static BOOL window_needs_net_wm_state_change_delay( struct x11drv_win_data *data static BOOL window_needs_config_change_delay( struct x11drv_win_data *data ) { + if (!data->managed || data->embedded) return FALSE; /* window is not managed or is embedded, safe to make changes */ if (data->pending_state.wm_state == WithdrawnState) return FALSE; /* window is unmapped, should be safe to make any change */ if (data->configure_serial) return TRUE; /* another config update is pending, wait for it to complete */ /* delay any config request when a _NET_WM_STATE or _MOTIF_WM_HINTS change which might trigger a ConfigureNotify is in flight */ @@ -1431,7 +1432,7 @@ static void window_set_config( struct x11drv_win_data *data, RECT rect, BOOL abo data->desired_state.above = above; if (!data->whole_window) return; /* no window, nothing to update */ if (EqualRect( old_rect, new_rect ) && (old_above || !above)) return; /* rects are the same, no need to be raised, nothing to update */ - if (data->managed && window_needs_config_change_delay( data )) + if (window_needs_config_change_delay( data )) { TRACE( "window %p/%lx is updating _NET_WM_STATE/_MOTIF_WM_HINTS, delaying request\n", data->hwnd, data->whole_window ); return; From dd25f643d6364d54a95b6c8eb9716ee704937f40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 27 May 2025 11:39:16 +0200 Subject: [PATCH 157/454] winex11: Don't expect an event from config requests while window is unmapped. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=58277 (cherry picked from commit 33ba6cbda7e8a1b4e93ac1c0b1c64e1fdcfee996) --- dlls/winex11.drv/window.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index a98f869ba8b5..97c8fea197e5 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -1511,6 +1511,9 @@ static void window_set_config( struct x11drv_win_data *data, RECT rect, BOOL abo if (mask & (CWWidth | CWHeight)) clear_emulated_fullscreen_padding( data ); if (net_wm_state != -1) window_set_net_wm_state( data, net_wm_state ); + + /* don't expect a ConfigureNotify while window is unmapped */ + if (data->pending_state.wm_state == WithdrawnState) data->configure_serial = 0; } /*********************************************************************** From 0fe6a95c6a7c3a78f62182dc222ab2e61dd1ea45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 28 May 2025 09:06:06 +0200 Subject: [PATCH 158/454] winex11: Listen for ConfigureNotify events on the virtual desktop. This effectively reverts 33ba6cbda7e8a1b4e93ac1c0b1c64e1fdcfee996 which was incorrectly assuming the missing ConfigureNotify was because the window was unmapped: we receive such events after a window is being mapped the first time, if we select them. Wine-Bug: http://bugs.winehq.org/show_bug.cgi?id=58277 (cherry picked from commit 06b1a49f5b3e1a5bb109f276a276e6df6ad9a60d) --- dlls/winex11.drv/desktop.c | 2 +- dlls/winex11.drv/window.c | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/dlls/winex11.drv/desktop.c b/dlls/winex11.drv/desktop.c index d9d306ef645a..8afaef61c287 100644 --- a/dlls/winex11.drv/desktop.c +++ b/dlls/winex11.drv/desktop.c @@ -77,7 +77,7 @@ BOOL X11DRV_CreateDesktop( const WCHAR *name, UINT width, UINT height ) /* Create window */ win_attr.event_mask = ExposureMask | KeyPressMask | KeyReleaseMask | EnterWindowMask | PointerMotionMask | ButtonPressMask | ButtonReleaseMask | FocusChangeMask | - PropertyChangeMask; + StructureNotifyMask | PropertyChangeMask; win_attr.cursor = XCreateFontCursor( display, XC_top_left_arrow ); if (default_visual.visual != DefaultVisual( display, DefaultScreen(display) )) diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index 97c8fea197e5..a84386782529 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -1511,9 +1511,6 @@ static void window_set_config( struct x11drv_win_data *data, RECT rect, BOOL abo if (mask & (CWWidth | CWHeight)) clear_emulated_fullscreen_padding( data ); if (net_wm_state != -1) window_set_net_wm_state( data, net_wm_state ); - - /* don't expect a ConfigureNotify while window is unmapped */ - if (data->pending_state.wm_state == WithdrawnState) data->configure_serial = 0; } /*********************************************************************** @@ -1885,6 +1882,7 @@ static UINT window_update_client_config( struct x11drv_win_data *data ) BOOL disable_maximize; if (!data->managed) return 0; /* unmanaged windows are managed by the Win32 side */ + if (is_virtual_desktop()) return 0; /* ignore window manager config changes in virtual desktop mode */ if (data->desired_state.wm_state != NormalState) return 0; /* ignore config changes on invisible/minimized windows */ if (data->wm_state_serial) return 0; /* another WM_STATE update is pending, wait for it to complete */ From a1579dce7bff511e11543c9913f711e3e76cca38 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 9 Jun 2025 18:47:24 -0600 Subject: [PATCH 159/454] ntdll: HACK: Disable old EAC bridge for Crossout. CW-Bug-Id: #16533 --- dlls/ntdll/unix/loadorder.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/dlls/ntdll/unix/loadorder.c b/dlls/ntdll/unix/loadorder.c index bbe509288806..2157f85556dd 100644 --- a/dlls/ntdll/unix/loadorder.c +++ b/dlls/ntdll/unix/loadorder.c @@ -364,12 +364,20 @@ static enum loadorder get_load_order_value( HANDLE std_key, HANDLE app_key, WCHA void set_load_order_app_name( const WCHAR *app_name ) { static const WCHAR eac_launcherW[] = {'P','R','O','T','O','N','_','E','A','C','_','L','A','U','N','C','H','E','R','_','P','R','O','C','E','S','S',0}; + static const WCHAR crossoutW[] = {'C','r','o','s','s','o','u','t','.','e','x','e',0}; const WCHAR *p; if ((p = wcsrchr( app_name, '\\' ))) app_name = p + 1; app_key = open_app_key( app_name ); main_exe_loaded = TRUE; + if (!wcscmp( app_name, crossoutW )) + { + ERR( "Disabling EAC bridge.\n" ); + eac_launcher_process = TRUE; + return; + } + p = NtCurrentTeb()->Peb->ProcessParameters->Environment; while(*p) { @@ -411,6 +419,7 @@ enum loadorder get_load_order( const UNICODE_STRING *nt_name ) TRACE("looking for %s\n", debugstr_w(path)); /* HACK: special logic for easyanticheat bridge: only load the bridge (builtin) if there exists a native version of the library next to the windows version */ + basename = get_basename((WCHAR *)path); if (!wcsicmp(basename, easyanticheat_x86W) || !wcsicmp(basename, easyanticheat_x64W) || !wcsicmp(basename, easyanticheatW)) { From 67f8a19c47ebc6b89ecccd08057547b0dbf6e833 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 6 Jun 2025 17:39:44 -0600 Subject: [PATCH 160/454] kernel32/tests: Test loading dll as resource or datafile with wow64 FS redirection disabled. CW-Bug-Id: #25520 --- dlls/kernel32/tests/loader.c | 45 ++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/dlls/kernel32/tests/loader.c b/dlls/kernel32/tests/loader.c index 0871ae3b57f0..6714975d2590 100644 --- a/dlls/kernel32/tests/loader.c +++ b/dlls/kernel32/tests/loader.c @@ -4542,10 +4542,27 @@ static void test_wow64_redirection(void) char buffer[MAX_PATH]; static const char *dlls[] = {"wlanapi.dll", "dxgi.dll", "dwrite.dll"}; unsigned i; + HMODULE mod, mod_fixed, kernelbase; + IMAGE_NT_HEADERS *nt; + WORD machine; if (!is_wow64) return; + kernelbase = GetModuleHandleW(L"kernelbase.dll"); + nt = RtlImageNtHeader(kernelbase); + machine = nt->FileHeader.Machine; + + ok(!GetModuleHandleA("rasapi32.dll"), "rasapi32.dll is already loaded.\n"); + + mod = LoadLibraryExW(L"c:\\windows\\system32\\rasapi32.dll", 0, LOAD_LIBRARY_AS_IMAGE_RESOURCE); + mod_fixed = (HMODULE)((ULONG_PTR)mod & ~(ULONG_PTR)3); + ok(!!mod_fixed, "got NULL.\n" ); + nt = RtlImageNtHeader(mod_fixed); + ok(!!nt, "got NULL.\n"); + ok(nt->FileHeader.Machine == machine, "got wrong machine.\n"); + FreeLibrary(mod); + /* Disable FS redirection, then test loading system libraries (pick ones that shouldn't * already be loaded in this process). */ @@ -4558,6 +4575,34 @@ static void test_wow64_redirection(void) test_wow64_redirection_for_dll(buffer, TRUE); } + mod = LoadLibraryExW(L"c:\\windows\\system32\\kernelbase.dll", 0, LOAD_LIBRARY_AS_IMAGE_RESOURCE); + ok(!!mod, "got NULL.\n" ); + ok(mod == kernelbase, "got different modules.\n"); + FreeLibrary(mod); + + mod = LoadLibraryExW(L"c:\\windows\\system32\\kernelbase.dll", 0, LOAD_LIBRARY_AS_DATAFILE); + ok(!!mod, "got NULL.\n" ); + ok(mod == kernelbase, "got different modules.\n"); + FreeLibrary(mod); + + ok(!GetModuleHandleA("rasapi32.dll"), "rasapi32.dll is already loaded.\n"); + mod = LoadLibraryExW(L"c:\\windows\\system32\\rasapi32.dll", 0, LOAD_LIBRARY_AS_IMAGE_RESOURCE); + mod_fixed = (HMODULE)((ULONG_PTR)mod & ~(ULONG_PTR)3); + ok(!!mod_fixed, "got NULL.\n" ); + nt = RtlImageNtHeader(mod_fixed); + ok(!!nt, "got NULL.\n"); + ok(nt->FileHeader.Machine != machine, "got 32 bit dll.\n"); + FreeLibrary(mod); + + ok(!GetModuleHandleA("rasapi32.dll"), "rasapi32.dll is already loaded.\n"); + mod = LoadLibraryExW(L"c:\\windows\\system32\\rasapi32.dll", 0, LOAD_LIBRARY_AS_DATAFILE); + mod_fixed = (HMODULE)((ULONG_PTR)mod & ~(ULONG_PTR)3); + ok(!!mod_fixed, "got NULL.\n" ); + nt = RtlImageNtHeader(mod_fixed); + ok(!!nt, "got NULL.\n"); + ok(nt->FileHeader.Machine != machine, "got 32 bit dll.\n"); + FreeLibrary(mod); + ok(pWow64RevertWow64FsRedirection(OldValue), "Re-enabling FS redirection failed\n"); /* and results don't depend whether redirection is enabled or not */ for (i = 0; i < ARRAY_SIZE(dlls); i++) From 404e4831b26a8ec88eb2b750ead22510eb3bf545 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 6 Jun 2025 18:42:35 -0600 Subject: [PATCH 161/454] version/tests: Test GetFileVersionInfoW() with wow64 FS redirection. CW-Bug-Id: #25520 --- dlls/version/tests/info.c | 109 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) diff --git a/dlls/version/tests/info.c b/dlls/version/tests/info.c index 61ff05b25598..fa7ef2191936 100644 --- a/dlls/version/tests/info.c +++ b/dlls/version/tests/info.c @@ -30,6 +30,14 @@ #include "verrsrc.h" #include "wine/test.h" +static BOOL is_wow64; + +static char system_dir[MAX_PATH]; +static char syswow_dir[MAX_PATH]; + +static BOOL (WINAPI *pWow64DisableWow64FsRedirection)(void **); +static BOOL (WINAPI *pWow64RevertWow64FsRedirection)(void *); + #define MY_LAST_ERROR ((DWORD)-1) #define EXPECT_BAD_PATH__NOT_FOUND \ ok( (ERROR_PATH_NOT_FOUND == GetLastError()) || \ @@ -725,12 +733,113 @@ static void test_GetFileVersionInfoEx(void) return; } +static void test_wow64_redirection(void) +{ + char buf[MAX_PATH], buf2[MAX_PATH]; + UINT size, translation; + char *ver, *p; + void *cookie; + HMODULE module; + BOOL ret; + + if (!is_wow64) + return; + + ret = pWow64DisableWow64FsRedirection(&cookie); + ok(ret, "got error %lu.\n", GetLastError()); + + sprintf(buf, "%s\\psapi.dll", syswow_dir); + sprintf(buf2, "%s\\test.dll", syswow_dir); + ret = CopyFileA(buf, buf2, FALSE); + if (!ret && GetLastError() == ERROR_ACCESS_DENIED) + { + ret = pWow64RevertWow64FsRedirection(cookie); + ok(ret, "got error %lu.\n", GetLastError()); + skip("Can't copy file to system directory.\n"); + return; + } + ok(ret, "got error %lu.\n", GetLastError()); + + sprintf(buf, "%s\\iphlpapi.dll", system_dir); + sprintf(buf2, "%s\\test.dll", system_dir); + ret = CopyFileA(buf, buf2, FALSE); + ok(ret, "got error %lu.\n", GetLastError()); + + module = LoadLibraryA("test.dll"); + ok(!!module, "got error %lu.\n", GetLastError()); + + size = GetFileVersionInfoSizeW(L"C:\\windows\\system32\\test.dll", NULL); + ok(size, "got error %lu.\n", GetLastError()); + ver = malloc(size); + ret = GetFileVersionInfoW(L"C:\\windows\\system32\\test.dll", 0, size, ver); + ok(ret, "got error %lu.\n", GetLastError()); + ret = VerQueryValueA(ver, "\\VarFileInfo\\Translation", (void **)&p, &size); + ok(ret, "got error %lu.\n", GetLastError()); + translation = *(UINT *)p; + translation = MAKELONG(HIWORD(translation), LOWORD(translation)); + sprintf(buf, "\\StringFileInfo\\%08x\\OriginalFileName", translation); + ret = VerQueryValueA(ver, buf, (LPVOID*)&p, &size); + ok(ret, "got error %lu.\n", GetLastError()); + /* When the module is already loaded GetFileVersionInfoW finds redirected loaded one while the file which + * should've been open with disabled redirection is different. */ + ok(!strnicmp(p, "psapi", 5), "got %s.\n", debugstr_a(p)); + free(ver); + + FreeLibrary(module); + + size = GetFileVersionInfoSizeW(L"C:\\windows\\system32\\test.dll", NULL); + ok(size, "got error %lu.\n", GetLastError()); + ver = malloc(size); + ret = GetFileVersionInfoW(L"C:\\windows\\system32\\test.dll", 0, size, ver); + ok(ret, "got error %lu.\n", GetLastError()); + ret = VerQueryValueA(ver, "\\VarFileInfo\\Translation", (void **)&p, &size); + ok(ret, "got error %lu.\n", GetLastError()); + translation = *(UINT *)p; + translation = MAKELONG(HIWORD(translation), LOWORD(translation)); + sprintf(buf, "\\StringFileInfo\\%08x\\OriginalFileName", translation); + ret = VerQueryValueA(ver, buf, (LPVOID*)&p, &size); + ok(ret, "got error %lu.\n", GetLastError()); + /* When the module is not loaded GetFileVersionInfoW finds the module in system32 as one would expect. */ + ok(!strnicmp(p, "iphlpapi", 8), "got %s.\n", debugstr_a(p)); + free(ver); + + sprintf(buf2, "%s\\test.dll", syswow_dir); + ret = DeleteFileA(buf2); + ok(ret, "got error %lu.\n", GetLastError()); + + sprintf(buf2, "%s\\test.dll", system_dir); + ret = DeleteFileA(buf2); + ok(ret, "got error %lu.\n", GetLastError()); + + ret = pWow64RevertWow64FsRedirection(cookie); + ok(ret, "got error %lu.\n", GetLastError()); +} + START_TEST(info) { + HMODULE kernel32 =kernel32 = GetModuleHandleA("kernel32.dll"); + BOOL (WINAPI *pIsWow64Process)(HANDLE, BOOL *); + DWORD size; + + pWow64DisableWow64FsRedirection = (void *)GetProcAddress(kernel32, "Wow64DisableWow64FsRedirection"); + pWow64RevertWow64FsRedirection = (void *)GetProcAddress(kernel32, "Wow64RevertWow64FsRedirection"); + + if ((pIsWow64Process = (void *)GetProcAddress(kernel32, "IsWow64Process"))) + pIsWow64Process( GetCurrentProcess(), &is_wow64 ); + + size = GetSystemDirectoryA(system_dir, ARRAY_SIZE(system_dir)); + ok(size && size < ARRAY_SIZE(system_dir), "Couldn't get system directory: %lu\n", GetLastError()); + if (is_wow64) + { + size = GetSystemWow64DirectoryA(syswow_dir, ARRAY_SIZE(syswow_dir)); + ok(size && size < ARRAY_SIZE(syswow_dir), "Couldn't get wow directory: %lu\n", GetLastError()); + } + test_info_size(); test_info(); test_32bit_win(); test_VerQueryValueA(); test_extra_block(); test_GetFileVersionInfoEx(); + test_wow64_redirection(); } From b05f133c7577e8cff4c6dd97f79f06df692dc86e Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 9 Jun 2025 11:59:53 -0600 Subject: [PATCH 162/454] msi/tests: Test insalling 64 bit library loaded into wow64 installer process. CW-Bug-Id: #25520 --- dlls/msi/tests/install.c | 150 ++++++++++++++++++++++++++++----------- 1 file changed, 107 insertions(+), 43 deletions(-) diff --git a/dlls/msi/tests/install.c b/dlls/msi/tests/install.c index 3bf28cc1e541..57ee48c2d7ae 100644 --- a/dlls/msi/tests/install.c +++ b/dlls/msi/tests/install.c @@ -52,6 +52,8 @@ char PROG_FILES_DIR_NATIVE[MAX_PATH]; char COMMON_FILES_DIR[MAX_PATH]; char APP_DATA_DIR[MAX_PATH]; char WINDOWS_DIR[MAX_PATH]; +static char system_dir[MAX_PATH]; +static char syswow_dir[MAX_PATH]; static const char *customdll; @@ -102,7 +104,7 @@ static const CHAR feature_comp_dat[] = "Feature_\tComponent_\n" static const CHAR file_dat[] = "File\tComponent_\tFileName\tFileSize\tVersion\tLanguage\tAttributes\tSequence\n" "s72\ts72\tl255\ti4\tS72\tS20\tI2\ti2\n" "File\tFile\n" - "five.txt\tFive\tfive.txt\t1000\t\t\t16384\t5\n" + "five.txt\tFive\tfive.txt\t1000\t0.0.0.0\t\t16384\t5\n" "four.txt\tFour\tfour.txt\t1000\t\t\t16384\t4\n" "one.txt\tOne\tone.txt\t1000\t\t\t0\t1\n" "three.txt\tThree\tthree.txt\t1000\t\t\t0\t3\n" @@ -1348,9 +1350,9 @@ static const CHAR x64_directory_dat[] = "CABOUTDIR\tMSITESTDIR\tcabout\n" "CHANGEDDIR\tMSITESTDIR\tchanged:second\n" "FIRSTDIR\tMSITESTDIR\tfirst\n" - "MSITESTDIR\tProgramFiles64Folder\tmsitest\n" + "MSITESTDIR\tSystem64Folder\tmsitest\n" "NEWDIR\tCABOUTDIR\tnew\n" - "ProgramFiles64Folder\tTARGETDIR\t.\n" + "System64Folder\tTARGETDIR\t.\n" "TARGETDIR\t\tSourceDir"; static const CHAR sr_install_exec_seq_dat[] = @@ -2388,6 +2390,16 @@ BOOL get_system_dirs(void) if(!GetWindowsDirectoryA(WINDOWS_DIR, MAX_PATH)) return FALSE; + size = GetSystemDirectoryA(system_dir, ARRAY_SIZE(system_dir)); + if (!size || size >= ARRAY_SIZE(system_dir)) + return FALSE; + if (is_wow64) + { + size = GetSystemWow64DirectoryA(syswow_dir, ARRAY_SIZE(syswow_dir)); + if (!size || size >= ARRAY_SIZE(syswow_dir)) + return FALSE; + } + return TRUE; } @@ -2430,13 +2442,11 @@ static void create_test_files(void) DeleteFileA("five.txt"); } -BOOL delete_pf(const CHAR *rel_path, BOOL is_file) +static BOOL delete_pf_dir(const char *rel_path, BOOL is_file, const char *basedir) { - CHAR path[MAX_PATH]; + char path[MAX_PATH]; - lstrcpyA(path, PROG_FILES_DIR); - lstrcatA(path, "\\"); - lstrcatA(path, rel_path); + sprintf(path, "%s\\%s", basedir, rel_path); if (is_file) return DeleteFileA(path); @@ -2444,18 +2454,9 @@ BOOL delete_pf(const CHAR *rel_path, BOOL is_file) return RemoveDirectoryA(path); } -static BOOL delete_pf_native(const CHAR *rel_path, BOOL is_file) +BOOL delete_pf(const CHAR *rel_path, BOOL is_file) { - CHAR path[MAX_PATH]; - - lstrcpyA(path, PROG_FILES_DIR_NATIVE); - lstrcatA(path, "\\"); - lstrcatA(path, rel_path); - - if (is_file) - return DeleteFileA(path); - else - return RemoveDirectoryA(path); + return delete_pf_dir(rel_path, is_file, PROG_FILES_DIR); } static BOOL delete_cf(const CHAR *rel_path, BOOL is_file) @@ -6215,7 +6216,12 @@ static void test_deferred_action(void) static void test_wow64(void) { + WIN32_FILE_ATTRIBUTE_DATA attr; + char path[MAX_PATH]; + HMODULE module; + DWORD dll_size; void *cookie; + BOOL ret; UINT r; if (!is_wow64) @@ -6230,7 +6236,59 @@ static void test_wow64(void) return; } - create_test_files(); + sprintf(path, "%s\\msitest", "C:\\windows\\syswow64"); + ret = CreateDirectoryA(path, NULL); + ok(ret, "got error %lu.\n", GetLastError()); + sprintf(path, "%s\\msitest\\cabout", "C:\\windows\\syswow64"); + ret = CreateDirectoryA(path, NULL); + ok(ret, "got error %lu.\n", GetLastError()); + sprintf(path, "%s\\msitest\\cabout\\new", "C:\\windows\\syswow64"); + ret = CreateDirectoryA(path, NULL); + ok(ret, "got error %lu.\n", GetLastError()); + + sprintf(path, "%s\\msitest\\cabout\\new\\five.txt", "C:\\windows\\syswow64"); + ret = CopyFileA("C:\\windows\\system32\\psapi.dll", path, FALSE); + ok(ret, "got error %lu.\n", GetLastError()); + + module = LoadLibraryA(path); + ok(!!module, "failed to load module.\n"); + + Wow64DisableWow64FsRedirection(&cookie); + + sprintf(path, "%s\\msitest", "C:\\windows\\system32"); + ret = CreateDirectoryA(path, NULL); + ok(ret, "%s, got error %lu.\n", path, GetLastError()); + sprintf(path, "%s\\msitest\\cabout", "C:\\windows\\system32"); + ret = CreateDirectoryA(path, NULL); + ok(ret, "got error %lu.\n", GetLastError()); + sprintf(path, "%s\\msitest\\cabout\\new", "C:\\windows\\system32"); + ret = CreateDirectoryA(path, NULL); + ok(ret, "got error %lu.\n", GetLastError()); + + sprintf(path, "%s\\msitest\\cabout\\new\\five.txt", "C:\\windows\\system32"); + create_file(path, 100); + + CreateDirectoryA("msitest", NULL); + + create_file("msitest\\one.txt", 100); + CreateDirectoryA("msitest\\first", NULL); + create_file("msitest\\first\\two.txt", 100); + CreateDirectoryA("msitest\\second", NULL); + create_file("msitest\\second\\three.txt", 100); + + create_file("four.txt", 100); + ret = GetFileAttributesExA("C:\\windows\\system32\\psapi.dll", GetFileExInfoStandard, &attr); + ok(ret, "got error %lu.\n", GetLastError()); + dll_size = attr.nFileSizeLow; + ret = CopyFileA("C:\\windows\\system32\\psapi.dll", "five.txt", FALSE); + ok(ret, "got error %lu.\n", GetLastError()); + create_cab_file("msitest.cab", MEDIA_SIZE, "four.txt\0five.txt\0"); + create_file("msitest\\filename", 100); + + DeleteFileA("four.txt"); + DeleteFileA("five.txt"); + Wow64RevertWow64FsRedirection(cookie); + create_database_template(msifile, x64_tables, ARRAY_SIZE(x64_tables), 200, "x64;0"); r = MsiInstallProductA(msifile, NULL); if (r == ERROR_INSTALL_PACKAGE_REJECTED) @@ -6238,32 +6296,38 @@ static void test_wow64(void) skip("Not enough rights to perform tests\n"); goto error; } + FreeLibrary(module); Wow64DisableWow64FsRedirection(&cookie); - ok(!delete_pf("msitest\\cabout\\new\\five.txt", TRUE), "File installed\n"); - ok(!delete_pf("msitest\\cabout\\new", FALSE), "Directory created\n"); - ok(!delete_pf("msitest\\cabout\\four.txt", TRUE), "File installed\n"); - ok(!delete_pf("msitest\\cabout", FALSE), "Directory created\n"); - ok(!delete_pf("msitest\\changed\\three.txt", TRUE), "File installed\n"); - ok(!delete_pf("msitest\\changed", FALSE), "Directory created\n"); - ok(!delete_pf("msitest\\first\\two.txt", TRUE), "File installed\n"); - ok(!delete_pf("msitest\\first", FALSE), "Directory created\n"); - ok(!delete_pf("msitest\\one.txt", TRUE), "File installed\n"); - ok(!delete_pf("msitest\\filename", TRUE), "File installed\n"); - ok(!delete_pf("msitest", FALSE), "Directory created\n"); - - ok(delete_pf_native("msitest\\cabout\\new\\five.txt", TRUE), "File not installed\n"); - ok(delete_pf_native("msitest\\cabout\\new", FALSE), "Directory not created\n"); - ok(delete_pf_native("msitest\\cabout\\four.txt", TRUE), "File not installed\n"); - ok(delete_pf_native("msitest\\cabout", FALSE), "Directory not created\n"); - ok(delete_pf_native("msitest\\changed\\three.txt", TRUE), "File not installed\n"); - ok(delete_pf_native("msitest\\changed", FALSE), "Directory not created\n"); - ok(delete_pf_native("msitest\\first\\two.txt", TRUE), "File not installed\n"); - ok(delete_pf_native("msitest\\first", FALSE), "Directory not created\n"); - ok(delete_pf_native("msitest\\one.txt", TRUE), "File not installed\n"); - ok(delete_pf_native("msitest\\filename", TRUE), "File not installed\n"); - ok(delete_pf_native("msitest", FALSE), "Directory not created\n"); + ok(delete_pf_dir("msitest\\cabout\\new\\five.txt", TRUE, syswow_dir), "File installed\n"); + ok(delete_pf_dir("msitest\\cabout\\new", FALSE, syswow_dir), "Directory created\n"); + ok(!delete_pf_dir("msitest\\cabout\\four.txt", TRUE, syswow_dir), "File installed\n"); + ok(delete_pf_dir("msitest\\cabout", FALSE, syswow_dir), "Directory created\n"); + ok(!delete_pf_dir("msitest\\changed\\three.txt", TRUE, syswow_dir), "File installed\n"); + ok(!delete_pf_dir("msitest\\changed", FALSE, syswow_dir), "Directory created\n"); + ok(!delete_pf_dir("msitest\\first\\two.txt", TRUE, syswow_dir), "File installed\n"); + ok(!delete_pf_dir("msitest\\first", FALSE, syswow_dir), "Directory created\n"); + ok(!delete_pf_dir("msitest\\one.txt", TRUE, syswow_dir), "File installed\n"); + ok(!delete_pf_dir("msitest\\filename", TRUE, syswow_dir), "File installed\n"); + ok(delete_pf_dir("msitest", FALSE, syswow_dir), "Directory created\n"); + + sprintf(path, "%s\\msitest\\cabout\\new\\five.txt", system_dir); + ret = GetFileAttributesExA(path, GetFileExInfoStandard, &attr); + ok(ret, "got error %lu.\n", GetLastError()); + todo_wine ok(attr.nFileSizeLow == dll_size, "got %lu, expected %lu.\n", attr.nFileSizeLow, dll_size); + + ok(delete_pf_dir("msitest\\cabout\\new\\five.txt", TRUE, system_dir), "File not installed\n"); + ok(delete_pf_dir("msitest\\cabout\\new", FALSE, system_dir), "Directory not created\n"); + ok(delete_pf_dir("msitest\\cabout\\four.txt", TRUE, system_dir), "File not installed\n"); + ok(delete_pf_dir("msitest\\cabout", FALSE, system_dir), "Directory not created\n"); + ok(delete_pf_dir("msitest\\changed\\three.txt", TRUE, system_dir), "File not installed\n"); + ok(delete_pf_dir("msitest\\changed", FALSE, system_dir), "Directory not created\n"); + ok(delete_pf_dir("msitest\\first\\two.txt", TRUE, system_dir), "File not installed\n"); + ok(delete_pf_dir("msitest\\first", FALSE, system_dir), "Directory not created\n"); + ok(delete_pf_dir("msitest\\one.txt", TRUE, system_dir), "File not installed\n"); + ok(delete_pf_dir("msitest\\filename", TRUE, system_dir), "File not installed\n"); + ok(delete_pf_dir("msitest", FALSE, system_dir), "Directory not created\n"); Wow64RevertWow64FsRedirection(cookie); From 95badbc79b0fd70199a879b612c89197b6b525e9 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 9 Jun 2025 12:17:27 -0600 Subject: [PATCH 163/454] msi: Allocate buffer in msi_get_file_version_info(). CW-Bug-Id: #25520 --- dlls/msi/appsearch.c | 20 ++++---------------- dlls/msi/files.c | 24 ++++++++++++++---------- dlls/msi/msipriv.h | 2 +- 3 files changed, 19 insertions(+), 27 deletions(-) diff --git a/dlls/msi/appsearch.c b/dlls/msi/appsearch.c index 88450087196d..092a61560a4a 100644 --- a/dlls/msi/appsearch.c +++ b/dlls/msi/appsearch.c @@ -178,18 +178,9 @@ static WCHAR *search_file( MSIPACKAGE *package, WCHAR *path, struct signature *s if (attr == INVALID_FILE_ATTRIBUTES || (attr & FILE_ATTRIBUTE_DIRECTORY)) return NULL; - size = msi_get_file_version_info( package, path, 0, NULL ); - if (!size) + if (!(buffer = msi_get_file_version_info( package, path ))) return wcsdup(path); - buffer = malloc(size); - if (!buffer) - return NULL; - - size = msi_get_file_version_info( package, path, size, buffer ); - if (!size) - goto done; - if (!VerQueryValueW(buffer, L"\\", (LPVOID)&info, &size) || !info) goto done; @@ -656,17 +647,14 @@ static UINT file_version_matches( MSIPACKAGE *package, const struct signature *s BOOL *matches ) { UINT len; - void *version; VS_FIXEDFILEINFO *info = NULL; - DWORD size = msi_get_file_version_info( package, filePath, 0, NULL ); + void *version; *matches = FALSE; - if (!size) return ERROR_SUCCESS; - if (!(version = malloc( size ))) return ERROR_OUTOFMEMORY; + if (!(version = msi_get_file_version_info( package, filePath ))) return ERROR_SUCCESS; - if (msi_get_file_version_info( package, filePath, size, version )) - VerQueryValueW( version, L"\\", (void **)&info, &len ); + VerQueryValueW( version, L"\\", (void **)&info, &len ); if (info) { diff --git a/dlls/msi/files.c b/dlls/msi/files.c index 35166788dae5..8dd2b67a792f 100644 --- a/dlls/msi/files.c +++ b/dlls/msi/files.c @@ -157,27 +157,31 @@ static BOOL apply_filepatch( MSIPACKAGE *package, const WCHAR *patch, const WCHA return ret; } -DWORD msi_get_file_version_info( MSIPACKAGE *package, const WCHAR *path, DWORD buflen, BYTE *buffer ) +BYTE *msi_get_file_version_info( MSIPACKAGE *package, const WCHAR *path ) { - DWORD size, handle; + DWORD size; + BYTE *buffer = NULL; + msi_disable_fs_redirection( package ); - if (buffer) size = GetFileVersionInfoW( path, 0, buflen, buffer ); - else size = GetFileVersionInfoSizeW( path, &handle ); + if (!(size = GetFileVersionInfoSizeW( path, NULL ))) goto done; + if (!(buffer = malloc( size ))) goto done; + if (!GetFileVersionInfoW( path, 0, size, buffer )) + { + free( buffer ); + buffer = NULL; + } +done: msi_revert_fs_redirection( package ); - return size; + return buffer; } VS_FIXEDFILEINFO *msi_get_disk_file_version( MSIPACKAGE *package, const WCHAR *filename ) { VS_FIXEDFILEINFO *ptr, *ret; - DWORD version_size; UINT size; void *version; - if (!(version_size = msi_get_file_version_info( package, filename, 0, NULL ))) return NULL; - if (!(version = malloc( version_size ))) return NULL; - - msi_get_file_version_info( package, filename, version_size, version ); + if (!(version = msi_get_file_version_info( package, filename ))) return NULL; if (!VerQueryValueW( version, L"\\", (void **)&ptr, &size )) { diff --git a/dlls/msi/msipriv.h b/dlls/msi/msipriv.h index e20ead557ce2..aebfc85ab765 100644 --- a/dlls/msi/msipriv.h +++ b/dlls/msi/msipriv.h @@ -1114,7 +1114,7 @@ extern BOOL msi_set_file_attributes( MSIPACKAGE *, const WCHAR *, DWORD ); extern HANDLE msi_find_first_file( MSIPACKAGE *, const WCHAR *, WIN32_FIND_DATAW * ); extern BOOL msi_find_next_file( MSIPACKAGE *, HANDLE, WIN32_FIND_DATAW * ); extern BOOL msi_move_file( MSIPACKAGE *, const WCHAR *, const WCHAR *, DWORD ); -extern DWORD msi_get_file_version_info( MSIPACKAGE *, const WCHAR *, DWORD, BYTE * ); +extern BYTE *msi_get_file_version_info( MSIPACKAGE *, const WCHAR * ); extern BOOL msi_create_full_path( MSIPACKAGE *, const WCHAR * ); extern DWORD msi_get_disk_file_size( MSIPACKAGE *, const WCHAR * ); extern VS_FIXEDFILEINFO *msi_get_disk_file_version( MSIPACKAGE *, const WCHAR * ); From 45b4b346ed2102a6bc2148d545d46e859ac83655 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 11 Jun 2025 10:49:25 -0600 Subject: [PATCH 164/454] msi: Get system directory just once. CW-Bug-Id: #25520 --- dlls/msi/action.c | 3 +-- dlls/msi/assembly.c | 3 +-- dlls/msi/custom.c | 2 +- dlls/msi/msi_main.c | 3 +++ dlls/msi/msipriv.h | 2 ++ dlls/msi/package.c | 4 ++-- 6 files changed, 10 insertions(+), 7 deletions(-) diff --git a/dlls/msi/action.c b/dlls/msi/action.c index d5e3af9cdba1..c5a0927feb45 100644 --- a/dlls/msi/action.c +++ b/dlls/msi/action.c @@ -5210,12 +5210,11 @@ static UINT ACTION_InstallFinalize(MSIPACKAGE *package) UINT ACTION_ForceReboot(MSIPACKAGE *package) { - WCHAR buffer[256], sysdir[MAX_PATH], squashed_pc[SQUASHED_GUID_SIZE]; + WCHAR buffer[256], squashed_pc[SQUASHED_GUID_SIZE]; HKEY hkey; squash_guid( package->ProductCode, squashed_pc ); - GetSystemDirectoryW(sysdir, ARRAY_SIZE(sysdir)); RegCreateKeyW(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows\\CurrentVersion\\RunOnce", &hkey); swprintf(buffer, ARRAY_SIZE(buffer), L"%s\\MsiExec.exe /@ \"%s\"", sysdir, squashed_pc); diff --git a/dlls/msi/assembly.c b/dlls/msi/assembly.c index 47e8071502c8..6613bf3bf960 100644 --- a/dlls/msi/assembly.c +++ b/dlls/msi/assembly.c @@ -34,9 +34,8 @@ static void load_fusion_dlls( MSIPACKAGE *package ) { HRESULT (WINAPI *pLoadLibraryShim)( const WCHAR *, const WCHAR *, void *, HMODULE * ); WCHAR path[MAX_PATH]; - DWORD len = GetSystemDirectoryW( path, MAX_PATH ); - lstrcpyW( path + len, L"\\mscoree.dll" ); + lstrcpyW( path + sysdir_len, L"\\mscoree.dll" ); if (!package->hmscoree && !(package->hmscoree = LoadLibraryW( path ))) return; if (!(pLoadLibraryShim = (void *)GetProcAddress( package->hmscoree, "LoadLibraryShim" ))) { diff --git a/dlls/msi/custom.c b/dlls/msi/custom.c index 12a7c3c36766..54e1897a2c5b 100644 --- a/dlls/msi/custom.c +++ b/dlls/msi/custom.c @@ -612,7 +612,7 @@ static DWORD custom_start_server(MSIPACKAGE *package, DWORD arch) if ((sizeof(void *) == 8 || is_wow64) && arch == SCS_32BIT_BINARY) GetSystemWow64DirectoryW(path, MAX_PATH - ARRAY_SIZE(L"\\msiexec.exe")); else - GetSystemDirectoryW(path, MAX_PATH - ARRAY_SIZE(L"\\msiexec.exe")); + wcscpy(path, sysdir); lstrcatW(path, L"\\msiexec.exe"); swprintf(cmdline, ARRAY_SIZE(cmdline), L"%s -Embedding %d", path, GetCurrentProcessId()); diff --git a/dlls/msi/msi_main.c b/dlls/msi/msi_main.c index a2b6b6a05035..32b4f9ad45d8 100644 --- a/dlls/msi/msi_main.c +++ b/dlls/msi/msi_main.c @@ -50,6 +50,8 @@ LPVOID gUIContextRecord = NULL; WCHAR *gszLogFile = NULL; HINSTANCE msi_hInstance; +WCHAR sysdir[MAX_PATH]; +SIZE_T sysdir_len; /* * Dll lifetime tracking declaration @@ -75,6 +77,7 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) msi_hInstance = hinstDLL; DisableThreadLibraryCalls(hinstDLL); IsWow64Process( GetCurrentProcess(), &is_wow64 ); + sysdir_len = GetSystemDirectoryW( sysdir, ARRAY_SIZE(sysdir) ); break; case DLL_PROCESS_DETACH: if (lpvReserved) break; diff --git a/dlls/msi/msipriv.h b/dlls/msi/msipriv.h index aebfc85ab765..bd8f6ec7a48d 100644 --- a/dlls/msi/msipriv.h +++ b/dlls/msi/msipriv.h @@ -43,6 +43,8 @@ static const BOOL is_64bit = sizeof(void *) > sizeof(int); extern BOOL is_wow64; +extern WCHAR sysdir[MAX_PATH]; +extern SIZE_T sysdir_len; #define MSI_DATASIZEMASK 0x00ff #define MSITYPE_VALID 0x0100 diff --git a/dlls/msi/package.c b/dlls/msi/package.c index b8966a0df903..969441f8827e 100644 --- a/dlls/msi/package.c +++ b/dlls/msi/package.c @@ -777,7 +777,7 @@ static VOID set_installer_properties(MSIPACKAGE *package) msi_set_property( package->db, L"Intel", bufstr, len ); if (sys_info.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL) { - GetSystemDirectoryW( pth, MAX_PATH ); + wcscpy( pth, sysdir ); PathAddBackslashW( pth ); msi_set_property( package->db, L"SystemFolder", pth, -1 ); @@ -798,7 +798,7 @@ static VOID set_installer_properties(MSIPACKAGE *package) msi_set_property( package->db, L"Msix64", bufstr, -1 ); msi_set_property( package->db, L"VersionNT64", verstr, -1 ); - GetSystemDirectoryW( pth, MAX_PATH ); + wcscpy( pth, sysdir ); PathAddBackslashW( pth ); msi_set_property( package->db, L"System64Folder", pth, -1 ); From c0e623abbdd563aed23acc06c9a646a6fa76c994 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 9 Jun 2025 12:27:04 -0600 Subject: [PATCH 165/454] msi: Fix getting version info for library loaded into wow64 process. CW-Bug-Id: #25520 --- dlls/msi/files.c | 10 ++++++++++ dlls/msi/tests/install.c | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/dlls/msi/files.c b/dlls/msi/files.c index 8dd2b67a792f..1ffa918b4a93 100644 --- a/dlls/msi/files.c +++ b/dlls/msi/files.c @@ -159,10 +159,20 @@ static BOOL apply_filepatch( MSIPACKAGE *package, const WCHAR *patch, const WCHA BYTE *msi_get_file_version_info( MSIPACKAGE *package, const WCHAR *path ) { + WCHAR temppath[MAX_PATH]; DWORD size; BYTE *buffer = NULL; msi_disable_fs_redirection( package ); + if (is_wow64 && is_platform_64bit( package->platform ) && !wcsnicmp( path, sysdir, sysdir_len ) + && path[sysdir_len] == '\\') + { + SIZE_T len = sysdir_len; + + while (len && sysdir[len] != '\\') --len; + swprintf( temppath, ARRAY_SIZE(temppath), L"%.*s\\sysnative%s", len, sysdir, path + sysdir_len ); + path = temppath; + } if (!(size = GetFileVersionInfoSizeW( path, NULL ))) goto done; if (!(buffer = malloc( size ))) goto done; if (!GetFileVersionInfoW( path, 0, size, buffer )) diff --git a/dlls/msi/tests/install.c b/dlls/msi/tests/install.c index 57ee48c2d7ae..4b967d1aa4b9 100644 --- a/dlls/msi/tests/install.c +++ b/dlls/msi/tests/install.c @@ -6315,7 +6315,7 @@ static void test_wow64(void) sprintf(path, "%s\\msitest\\cabout\\new\\five.txt", system_dir); ret = GetFileAttributesExA(path, GetFileExInfoStandard, &attr); ok(ret, "got error %lu.\n", GetLastError()); - todo_wine ok(attr.nFileSizeLow == dll_size, "got %lu, expected %lu.\n", attr.nFileSizeLow, dll_size); + ok(attr.nFileSizeLow == dll_size, "got %lu, expected %lu.\n", attr.nFileSizeLow, dll_size); ok(delete_pf_dir("msitest\\cabout\\new\\five.txt", TRUE, system_dir), "File not installed\n"); ok(delete_pf_dir("msitest\\cabout\\new", FALSE, system_dir), "Directory not created\n"); From 31ef8b8f5ca0f26f5ba5e67e310c2cb9c585ac01 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 9 Jun 2025 17:16:43 -0600 Subject: [PATCH 166/454] fixup! kernelbase: HACK: Do not expose version info for builtin DLLs for VC redists. CW-Bug-Id: #25520 --- dlls/kernelbase/version.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c index 6ae5e529e028..17b414128b21 100644 --- a/dlls/kernelbase/version.c +++ b/dlls/kernelbase/version.c @@ -793,7 +793,9 @@ DWORD WINAPI GetFileVersionInfoSizeExW( DWORD flags, LPCWSTR filename, LPDWORD r && (!memcmp( exe_name + exe_name_len - 16, L"vcredist_x64.exe", 16 * sizeof(*exe_name) ) || !memcmp( exe_name + exe_name_len - 16, L"vcredist_x86.exe", 16 * sizeof(*exe_name) ))) || (exe_name_len >= 17 - && (!memcmp( exe_name + exe_name_len - 17, L"VC_redist.x64.exe", 17 * sizeof(*exe_name) ) + && (!memcmp( exe_name + exe_name_len - 17, L"vc_redist.x64.exe", 17 * sizeof(*exe_name) ) + || !memcmp( exe_name + exe_name_len - 17, L"vc_redist.x86.exe", 17 * sizeof(*exe_name) ) + || !memcmp( exe_name + exe_name_len - 17, L"VC_redist.x64.exe", 17 * sizeof(*exe_name) ) || !memcmp( exe_name + exe_name_len - 17, L"VC_redist.x86.exe", 17 * sizeof(*exe_name) )))) && (nt = RtlImageNtHeader( mod )) && (char *)nt - signature >= sizeof(builtin_signature) && !memcmp( signature, builtin_signature, sizeof(builtin_signature) )) From a9e08acce0030f0138656b9c2e5a54f442861f61 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 9 Jun 2025 17:18:19 -0600 Subject: [PATCH 167/454] msi: HACK: Report older Windows version for Halo MCC redists. CW-Bug-Id: #25520 --- dlls/msi/package.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/dlls/msi/package.c b/dlls/msi/package.c index 969441f8827e..f9d9e54447aa 100644 --- a/dlls/msi/package.c +++ b/dlls/msi/package.c @@ -620,6 +620,24 @@ static void set_msi_assembly_prop(MSIPACKAGE *package) free(version); } +static void fixup_winver(DWORD *verval) +{ + static int cached = -1; + + if (cached == -1) + { + const char *s; + + cached = (s = getenv("STEAM_COMPAT_APP_ID")) + && !strcmp(s, "976730") + ; + if (cached) + ERR("HACK: setting winver 502.\n"); + } + if (!cached) return; + if (*verval > 502) *verval = 502; +} + static VOID set_installer_properties(MSIPACKAGE *package) { WCHAR *ptr; @@ -747,6 +765,7 @@ static VOID set_installer_properties(MSIPACKAGE *package) verval = 603; OSVersion.dwBuildNumber = 9600; } + fixup_winver(&verval); len = swprintf( verstr, ARRAY_SIZE(verstr), L"%u", verval ); switch (OSVersion.dwPlatformId) { From c7bbe0f92066a88f11ff2396af39695a3437c7ae Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 13 Jun 2025 10:13:45 -0600 Subject: [PATCH 168/454] Revert "winex11.drv: HACK: Only offscreen client window in X11DRV_vulkan_surface_presented() on Gamescope." This reverts commit 6e7881149f9fc8701eb304b4f929decc380f8ea3. Obviated by a fix in Gamescope. CW-Bug-Id: #25154 --- dlls/winex11.drv/vulkan.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c index 1eeaf4a7a18e..fbc9f933f436 100644 --- a/dlls/winex11.drv/vulkan.c +++ b/dlls/winex11.drv/vulkan.c @@ -288,8 +288,7 @@ static void X11DRV_vulkan_surface_update( HWND hwnd, void *private ) TRACE( "%p %p\n", hwnd, private ); vulkan_surface_update_size( hwnd, surface ); - if (surface->offscreen || !X11DRV_HasWindowManager( "steamcompmgr" )) - vulkan_surface_update_offscreen( hwnd, surface ); + vulkan_surface_update_offscreen( hwnd, surface ); } static int force_present_to_surface(void) @@ -311,7 +310,6 @@ static int force_present_to_surface(void) static void X11DRV_vulkan_surface_presented( HWND hwnd, void *private, VkResult result ) { struct x11drv_vulkan_surface *surface = private; - BOOL was_offscreen = surface->offscreen; struct window_surface *win_surface; struct x11drv_win_data *data; RECT rect_dst, rect; @@ -337,12 +335,6 @@ static void X11DRV_vulkan_surface_presented( HWND hwnd, void *private, VkResult return; } - if (!was_offscreen && X11DRV_HasWindowManager( "steamcompmgr" )) - { - NtUserPostMessage( hwnd, WM_WINE_UPDATEWINDOWSTATE, 0, 0 ); - return; - } - toplevel = NtUserGetAncestor( hwnd, GA_ROOT ); dpi = NtUserGetDpiForWindow( hwnd ); NtUserGetClientRect( hwnd, &rect_dst, dpi ); From 50f02c1c552365d0b17584f859ef7e9ec43c3d81 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 18 Jun 2025 18:01:08 -0600 Subject: [PATCH 169/454] mmdevapi/tests: Remove a test which crashes on up to date Windows 11. CW-Bug-Id: #25561 --- dlls/mmdevapi/tests/render.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/dlls/mmdevapi/tests/render.c b/dlls/mmdevapi/tests/render.c index 236a4ff71c80..2cbbe496ba60 100644 --- a/dlls/mmdevapi/tests/render.c +++ b/dlls/mmdevapi/tests/render.c @@ -182,9 +182,6 @@ static void test_audioclient(void) handle = CreateEventW(NULL, FALSE, FALSE, NULL); - hr = IAudioClient_QueryInterface(ac, &IID_IUnknown, NULL); - ok(hr == E_POINTER, "QueryInterface(NULL) returned %08lx\n", hr); - unk = (void*)(LONG_PTR)0x12345678; hr = IAudioClient_QueryInterface(ac, &IID_NULL, (void**)&unk); ok(hr == E_NOINTERFACE, "QueryInterface(IID_NULL) returned %08lx\n", hr); @@ -197,7 +194,6 @@ static void test_audioclient(void) ref = IUnknown_Release(unk); ok(ref == 1, "Released count is %lu\n", ref); } - hr = IAudioClient_QueryInterface(ac, &IID_IAudioClient, (void**)&unk); ok(hr == S_OK, "QueryInterface(IID_IAudioClient) returned %08lx\n", hr); if (unk) From bd28ac73b7655739bc4d7aa0214c2dbac9146b19 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 18 Jun 2025 18:06:02 -0600 Subject: [PATCH 170/454] mmdevapi: Round min_period_frames up in client_GetSharedModeEnginePeriod(). CW-Bug-Id: #25561 --- dlls/mmdevapi/client.c | 3 ++- dlls/mmdevapi/tests/render.c | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/dlls/mmdevapi/client.c b/dlls/mmdevapi/client.c index d29969b80c54..07390b165274 100644 --- a/dlls/mmdevapi/client.c +++ b/dlls/mmdevapi/client.c @@ -1064,7 +1064,8 @@ static HRESULT WINAPI client_GetSharedModeEnginePeriod(IAudioClient3 *iface, return hr; *default_period_frames = def_period * format->nSamplesPerSec / (REFERENCE_TIME)10000000; - *min_period_frames = min_period * format->nSamplesPerSec / (REFERENCE_TIME)10000000; + *min_period_frames = (min_period * format->nSamplesPerSec + 10000000 - 1) / (REFERENCE_TIME)10000000; + *default_period_frames = max( *default_period_frames, *min_period_frames ); *max_period_frames = *default_period_frames; *unit_period_frames = 1; diff --git a/dlls/mmdevapi/tests/render.c b/dlls/mmdevapi/tests/render.c index 2cbbe496ba60..38e28e1ee7d4 100644 --- a/dlls/mmdevapi/tests/render.c +++ b/dlls/mmdevapi/tests/render.c @@ -368,6 +368,20 @@ static void test_audioclient(void) hr = IMMDevice_Activate(dev, &IID_IAudioClient, CLSCTX_INPROC_SERVER, NULL, (void**)&ac); ok(hr == S_OK, "Activation failed with %08lx\n", hr); + + hr = IAudioClient_QueryInterface(ac, &IID_IAudioClient3, (void**)&ac3); + ok(hr == S_OK, "Failed to query IAudioClient3 interface: %08lx\n", hr); + + hr = IAudioClient3_InitializeSharedAudioStream( + ac3, AUDCLNT_SHAREMODE_SHARED, min_period, pwfx, NULL); + ok(hr == S_OK, "InitializeSharedAudioStream returns %08lx\n", hr); + + IAudioClient3_Release(ac3); + IAudioClient_Release(ac); + + hr = IMMDevice_Activate(dev, &IID_IAudioClient, CLSCTX_INPROC_SERVER, + NULL, (void**)&ac); + ok(hr == S_OK, "Activation failed with %08lx\n", hr); } else win_skip("IAudioClient3 is not present\n"); From be7fbfb897faf9a367ed8d0f2c4cdf88313ffb6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 24 Jun 2025 11:27:51 +0200 Subject: [PATCH 171/454] winex11: Avoid requesting CWStackMode alone with managed windows. Some window manager won't send a ConfigureNotify event if the stack mode isn't actually changed, and we cannot predict whether it will. We would be waiting forever for an event and delay other requests when we can just request the above state the next time the window needs to be reconfigured. CW-Bug-Id: #25059 --- dlls/winex11.drv/window.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index a84386782529..c75cf372924e 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -1431,7 +1431,7 @@ static void window_set_config( struct x11drv_win_data *data, RECT rect, BOOL abo data->desired_state.rect = *new_rect; data->desired_state.above = above; if (!data->whole_window) return; /* no window, nothing to update */ - if (EqualRect( old_rect, new_rect ) && (old_above || !above)) return; /* rects are the same, no need to be raised, nothing to update */ + if (EqualRect( old_rect, new_rect ) && (old_above || !above || data->managed)) return; /* rects are the same, no need to be raised, nothing to update */ if (window_needs_config_change_delay( data )) { TRACE( "window %p/%lx is updating _NET_WM_STATE/_MOTIF_WM_HINTS, delaying request\n", data->hwnd, data->whole_window ); From ba4558221c62f262bd6a0e2a8e53a457991e03d4 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 25 Jun 2025 13:42:04 -0600 Subject: [PATCH 172/454] user32/tests: Add tests for STARTF_USESHOWWINDOW. CW-Bug-Id: #25608 --- dlls/user32/tests/win.c | 179 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 179 insertions(+) diff --git a/dlls/user32/tests/win.c b/dlls/user32/tests/win.c index caaf01f6089b..53161a294793 100644 --- a/dlls/user32/tests/win.c +++ b/dlls/user32/tests/win.c @@ -13747,6 +13747,178 @@ static void test_ReleaseCapture(void) UnregisterClassA(cls.lpszClassName, GetModuleHandleA(0)); } +struct test_startupinfo_showwindow_test +{ + DWORD style; + enum + { + TEST_SHOW_WS_VISIBLE, + TEST_SHOW_SHOWWINDOW, + TEST_SHOW_SETWINDOWPOS, + } + show_type; + HWND parent; + int cmd_show; + BOOL counted_as_first; + BOOL show_affected; +}; + +static const struct test_startupinfo_showwindow_test test_startupinfo_showwindow_tests[] = +{ + /* Affected window types. */ + { WS_OVERLAPPED, TEST_SHOW_SHOWWINDOW, NULL, SW_SHOW, TRUE, TRUE }, + { WS_POPUP | WS_CAPTION, TEST_SHOW_SHOWWINDOW, NULL, SW_SHOW, TRUE, TRUE }, + { 0, TEST_SHOW_SHOWWINDOW, HWND_MESSAGE, SW_SHOW, TRUE, TRUE }, + + /* Types of showing window. */ + { WS_OVERLAPPED, TEST_SHOW_WS_VISIBLE, NULL, SW_SHOW, TRUE, TRUE }, + { WS_OVERLAPPED, TEST_SHOW_SETWINDOWPOS, NULL, SW_SHOW, FALSE, FALSE }, + + /* Various cmd_show values */ + { WS_OVERLAPPED, TEST_SHOW_SHOWWINDOW, NULL, SW_NORMAL, TRUE, TRUE }, + { WS_OVERLAPPED, TEST_SHOW_SHOWWINDOW, NULL, SW_SHOWDEFAULT, TRUE, TRUE }, + { WS_OVERLAPPED, TEST_SHOW_SHOWWINDOW, NULL, SW_SHOWMAXIMIZED, TRUE, FALSE }, + { WS_OVERLAPPED, TEST_SHOW_SHOWWINDOW, NULL, SW_FORCEMINIMIZE, TRUE, FALSE }, +#if 0 + /* Excluding these tests to reduce test run time. */ + { WS_OVERLAPPED, TEST_SHOW_SHOWWINDOW, NULL, SW_HIDE, TRUE, FALSE }, + { WS_OVERLAPPED, TEST_SHOW_SHOWWINDOW, NULL, SW_SHOWMINIMIZED, TRUE, FALSE }, + { WS_OVERLAPPED, TEST_SHOW_SHOWWINDOW, NULL, SW_SHOWNOACTIVATE, TRUE, FALSE }, + { WS_OVERLAPPED, TEST_SHOW_SHOWWINDOW, NULL, SW_MINIMIZE, TRUE, FALSE }, + { WS_OVERLAPPED, TEST_SHOW_SHOWWINDOW, NULL, SW_SHOWMINNOACTIVE, TRUE, FALSE }, + { WS_OVERLAPPED, TEST_SHOW_SHOWWINDOW, NULL, SW_SHOWNA, TRUE, FALSE }, + { WS_OVERLAPPED, TEST_SHOW_SHOWWINDOW, NULL, SW_RESTORE, TRUE, FALSE }, +#endif +}; + +static void test_startupinfo_showwindow_proc( int test_id ) +{ + const struct test_startupinfo_showwindow_test *test = &test_startupinfo_showwindow_tests[test_id]; + static const DWORD ignored_window_styles[] = + { + WS_CHILD, + WS_POPUP, /* WS_POPUP windows are not ignored when used with WS_CAPTION (which is WS_BORDER | WS_DLGFRAME) */ + WS_CHILD | WS_POPUP, + WS_POPUP | WS_BORDER, + WS_POPUP | WS_DLGFRAME, + WS_POPUP | WS_SYSMENU | WS_THICKFRAME| WS_MINIMIZEBOX | WS_MAXIMIZEBOX, + }; + BOOL bval, expected; + STARTUPINFOW sa; + unsigned int i; + DWORD style; + HWND hwnd; + + GetStartupInfoW( &sa ); + + winetest_push_context( "show %u, test %d", sa.wShowWindow, test_id ); + + ok( sa.dwFlags & STARTF_USESHOWWINDOW, "got %#lx.\n", sa.dwFlags ); + + /* First test windows which are not affected by startup info. ShowWindow() called for those doesn't count as + * consuming startup info, it is still used with the next applicable window. + * + * SW_ variants for ShowWindow() which are not altered by startup info still consume startup info usage so can + * only be tested once per process. */ + + hwnd = CreateWindowA( "static", "parent", WS_OVERLAPPED, 0, 0, 0, 0, NULL, NULL, + GetModuleHandleW( NULL ), NULL ); + pump_messages(); + for (i = 0; i < ARRAY_SIZE(ignored_window_styles); ++i) + { + winetest_push_context( "%u", i ); + hwnd = CreateWindowA( "static", "overlapped", ignored_window_styles[i], 0, 0, 0, 0, + ignored_window_styles[i] & WS_CHILD ? hwnd : NULL, NULL, + GetModuleHandleW( NULL ), NULL ); + ok( !!hwnd, "got NULL.\n" ); + ShowWindow( hwnd, SW_SHOW ); + bval = IsWindowVisible( hwnd ); + if ((ignored_window_styles[i] & (WS_CHILD | WS_POPUP)) == WS_CHILD) + ok( !bval, "unexpectedly visible.\n" ); + else + ok( bval, "unexpectedly invisible.\n" ); + pump_messages(); + winetest_pop_context(); + } + DestroyWindow( hwnd ); + pump_messages(); + + style = test->style; + if (test->show_type == TEST_SHOW_WS_VISIBLE) style |= WS_VISIBLE; + hwnd = CreateWindowA( "static", "overlapped", style, 0, 0, 0, 0, NULL, NULL, + GetModuleHandleW( NULL ), NULL ); + ok( !!hwnd, "got NULL.\n" ); + pump_messages(); + if (test->show_type == TEST_SHOW_SHOWWINDOW) + ShowWindow( hwnd, test->cmd_show ); + else if (test->show_type == TEST_SHOW_SETWINDOWPOS) + SetWindowPos( hwnd, NULL, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_SHOWWINDOW ); + pump_messages(); + expected = test->cmd_show != SW_HIDE && test->cmd_show != SW_FORCEMINIMIZE + && (!test->show_affected || sa.wShowWindow != SW_HIDE); + bval = !!IsWindowVisible( hwnd ); + + todo_wine_if((test->show_affected && sa.wShowWindow == SW_HIDE) || test->cmd_show == SW_FORCEMINIMIZE) + ok( bval == expected, "got %d, expected %d.\n", bval, expected ); + + /* After default args were used once SW_SHOWDEFAULT doesn't use startupinfo. */ + ShowWindow( hwnd, SW_SHOWDEFAULT ); + bval = !!IsWindowVisible( hwnd ); + expected = test->counted_as_first || sa.wShowWindow != SW_HIDE; + todo_wine_if(!test->counted_as_first && sa.wShowWindow == SW_HIDE) ok( bval == expected, "got %d, expected %d.\n", bval, expected ); + DestroyWindow( hwnd ); + pump_messages(); + + hwnd = CreateWindowA( "static", "overlapped2", WS_OVERLAPPED, 0, 0, 0, 0, NULL, NULL, GetModuleHandleW(NULL), NULL ); + ok( !!hwnd, "got NULL.\n" ); + pump_messages(); + /* After default args were used once SW_SHOWDEFAULT doesn't use startupinfo, even with another window. */ + ShowWindow( hwnd, SW_SHOWDEFAULT ); + bval = IsWindowVisible( hwnd ); + ok( bval, "got %d, expected %d.\n", bval, expected ); + DestroyWindow( hwnd ); + pump_messages(); + + winetest_pop_context(); +} + +static void test_startupinfo_showwindow( char **argv ) +{ + STARTUPINFOA sa = {.cb = sizeof(STARTUPINFOA)}; + PROCESS_INFORMATION info; + char cmdline[MAX_PATH]; + unsigned int i; + BOOL ret; + + sa.dwFlags = STARTF_USESHOWWINDOW; + + sa.wShowWindow = SW_HIDE; + for (i = 0; i < ARRAY_SIZE(test_startupinfo_showwindow_tests); ++i) + { + sprintf( cmdline, "%s %s showwindow_proc %d", argv[0], argv[1], i ); + ret = CreateProcessA( NULL, cmdline, NULL, NULL, FALSE, 0, NULL, NULL, &sa, &info ); + ok( ret, "got error %lu\n", GetLastError() ); + wait_child_process( info.hProcess ); + CloseHandle( info.hProcess ); + CloseHandle( info.hThread ); + } + + if (0) + { + /* Excluding these tests to reduce test run time. */ + sa.wShowWindow = SW_SHOWMAXIMIZED; + for (i = 0; i < ARRAY_SIZE(test_startupinfo_showwindow_tests); ++i) + { + sprintf( cmdline, "%s %s showwindow_proc %d", argv[0], argv[1], i ); + ret = CreateProcessA( NULL, cmdline, NULL, NULL, FALSE, 0, NULL, NULL, &sa, &info ); + ok( ret, "got error %lu\n", GetLastError() ); + wait_child_process( info.hProcess ); + CloseHandle( info.hProcess ); + CloseHandle( info.hThread ); + } + } +} + START_TEST(win) { char **argv; @@ -13801,6 +13973,12 @@ START_TEST(win) return; } + if (argc == 4 && !strcmp(argv[2], "showwindow_proc")) + { + test_startupinfo_showwindow_proc( atoi( argv[3] )); + return; + } + if (!RegisterWindowClasses()) assert(0); hwndMain = CreateWindowExA(/*WS_EX_TOOLWINDOW*/ 0, "MainWindowClass", "Main window", @@ -13931,6 +14109,7 @@ START_TEST(win) test_DragDetect(); test_WM_NCCALCSIZE(); test_ReleaseCapture(); + test_startupinfo_showwindow(argv); /* add the tests above this line */ if (hhook) UnhookWindowsHookEx(hhook); From 738dc90499603960a264237318148e4ace5e37db Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 24 Jun 2025 20:29:28 -0600 Subject: [PATCH 173/454] win32u: Mind STARTF_USESHOWWINDOW in show_window(). CW-Bug-Id: #25608 --- dlls/user32/tests/win.c | 4 ++-- dlls/win32u/window.c | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/dlls/user32/tests/win.c b/dlls/user32/tests/win.c index 53161a294793..91632f79f798 100644 --- a/dlls/user32/tests/win.c +++ b/dlls/user32/tests/win.c @@ -13858,14 +13858,14 @@ static void test_startupinfo_showwindow_proc( int test_id ) && (!test->show_affected || sa.wShowWindow != SW_HIDE); bval = !!IsWindowVisible( hwnd ); - todo_wine_if((test->show_affected && sa.wShowWindow == SW_HIDE) || test->cmd_show == SW_FORCEMINIMIZE) + todo_wine_if(test->cmd_show == SW_FORCEMINIMIZE) ok( bval == expected, "got %d, expected %d.\n", bval, expected ); /* After default args were used once SW_SHOWDEFAULT doesn't use startupinfo. */ ShowWindow( hwnd, SW_SHOWDEFAULT ); bval = !!IsWindowVisible( hwnd ); expected = test->counted_as_first || sa.wShowWindow != SW_HIDE; - todo_wine_if(!test->counted_as_first && sa.wShowWindow == SW_HIDE) ok( bval == expected, "got %d, expected %d.\n", bval, expected ); + ok( bval == expected, "got %d, expected %d.\n", bval, expected ); DestroyWindow( hwnd ); pump_messages(); diff --git a/dlls/win32u/window.c b/dlls/win32u/window.c index 631606afc312..0d5666edd7af 100644 --- a/dlls/win32u/window.c +++ b/dlls/win32u/window.c @@ -4695,6 +4695,7 @@ void update_window_state( HWND hwnd ) */ static BOOL show_window( HWND hwnd, INT cmd ) { + static volatile LONG first_window = 1; WND *win; HWND parent; LONG style = get_window_long( hwnd, GWL_STYLE ), new_style; @@ -4707,6 +4708,19 @@ static BOOL show_window( HWND hwnd, INT cmd ) context = set_thread_dpi_awareness_context( get_window_dpi_awareness_context( hwnd )); + if ((!(style & (WS_POPUP | WS_CHILD)) + || ((style & (WS_POPUP | WS_CHILD | WS_CAPTION)) == (WS_POPUP | WS_CAPTION))) + && InterlockedExchange( &first_window, 0 )) + { + RTL_USER_PROCESS_PARAMETERS *params = NtCurrentTeb()->Peb->ProcessParameters; + + if (params->dwFlags & STARTF_USESHOWWINDOW && (cmd == SW_SHOW || cmd == SW_SHOWNORMAL || cmd == SW_SHOWDEFAULT)) + { + cmd = params->wShowWindow; + TRACE( "hwnd=%p, using cmd %d from startup info.\n", hwnd, cmd ); + } + } + switch(cmd) { case SW_HIDE: From f9bc9a9e4cb34754827796881a24f9e475363f41 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 27 Jun 2025 13:16:52 -0600 Subject: [PATCH 174/454] win32u: HACK: Add a frequency to 800x600 mode and disable low res modes for Dishonored 2. CW-Bug-Id: #25610 --- dlls/win32u/sysparams.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/dlls/win32u/sysparams.c b/dlls/win32u/sysparams.c index 78a7d3c4a798..7bc30b32b473 100644 --- a/dlls/win32u/sysparams.c +++ b/dlls/win32u/sysparams.c @@ -1726,7 +1726,7 @@ static SIZE *get_screen_sizes( const DEVMODEW *maximum, const DEVMODEW *modes, U } /* Titan Souls renders incorrectly if we report modes smaller than 800x600 */ - if ((enable_lowres = !(env = getenv( "SteamAppId" )) || strcmp( env, "297130" ))) + if ((enable_lowres = (!(env = getenv( "SteamAppId" )) || (strcmp( env, "297130" ) && strcmp( env, "403640" ))))) { memcpy( sizes + count, lowres_sizes, ARRAY_SIZE(lowres_sizes) * sizeof(*sizes) ); count += ARRAY_SIZE(lowres_sizes); @@ -1783,7 +1783,22 @@ static DEVMODEW *get_virtual_modes( const DEVMODEW *initial, const DEVMODEW *max if (freqs[1] <= 60) freqs[1] = 0; if (!(screen_sizes = get_screen_sizes( maximum, host_modes, host_modes_count, &sizes_count ))) return NULL; - modes = malloc( 2 * ARRAY_SIZE(freqs) * ARRAY_SIZE(depths) * (sizes_count + 2) * sizeof(*modes) ); + modes = malloc( (2 * ARRAY_SIZE(freqs) * ARRAY_SIZE(depths) * (sizes_count + 2) + 1) * sizeof(*modes) ); + + if ((env = getenv( "SteamAppId" )) && !strcmp( env, "403640" )) + { + DEVMODEW mode = + { + .dmSize = sizeof(mode), + .dmFields = DM_DISPLAYORIENTATION | DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFLAGS | DM_DISPLAYFREQUENCY, + .dmDisplayFrequency = 30, + .dmBitsPerPel = 32, + .dmDisplayOrientation = initial->dmDisplayOrientation, + .dmPelsWidth = 800, + .dmPelsHeight = 600, + }; + count += add_virtual_mode( modes, count, &mode, center_modes ); + } for (i = 0; modes && i < ARRAY_SIZE(depths); ++i) for (f = 0; f < ARRAY_SIZE(freqs); ++f) From 4dfb11b389f5275657d10738a6de6e6b5572c941 Mon Sep 17 00:00:00 2001 From: Dylan Donnell Date: Sun, 15 Jun 2025 00:23:39 +0300 Subject: [PATCH 175/454] kernelbase: Allocate a new buffer for the module name in LoadLibraryExA. (cherry picked from commit b4df52fd617c5807969a26706e65d2e13d411cbe) CW-Bug-Id: #25606 --- dlls/kernelbase/loader.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/dlls/kernelbase/loader.c b/dlls/kernelbase/loader.c index f684d32e8527..c01e60712313 100644 --- a/dlls/kernelbase/loader.c +++ b/dlls/kernelbase/loader.c @@ -533,9 +533,16 @@ HMODULE WINAPI DECLSPEC_HOTPATCH LoadLibraryW( LPCWSTR name ) HMODULE WINAPI DECLSPEC_HOTPATCH LoadLibraryExA( LPCSTR name, HANDLE file, DWORD flags ) { WCHAR *nameW; + HMODULE module; + + /* A new allocation is necessary due to TP Shell Service + * calling LoadLibraryExA from an LdrLoadDll hook */ + if (!(nameW = file_name_AtoW( name, TRUE ))) return 0; - if (!(nameW = file_name_AtoW( name, FALSE ))) return 0; - return LoadLibraryExW( nameW, file, flags ); + module = LoadLibraryExW( nameW, file, flags ); + + HeapFree( GetProcessHeap(), 0, nameW ); + return module; } From 7419a0289a2c0b55b545e4cae4bc50b61ed28a4d Mon Sep 17 00:00:00 2001 From: Dylan Donnell Date: Fri, 14 Mar 2025 23:32:03 +0200 Subject: [PATCH 176/454] ntdll: Return STATUS_ACCESS_VIOLATION from NtQueryInformationThread ThreadHideFromDebugger if *ret_len is not writable. (cherry picked from commit 2364128c56f58561f5204b445fa8abd8f9c56105) CW-Bug-Id: #25606 --- dlls/ntdll/unix/thread.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c index 7da61c1a4386..d0bba284e77d 100644 --- a/dlls/ntdll/unix/thread.c +++ b/dlls/ntdll/unix/thread.c @@ -2328,6 +2328,12 @@ NTSTATUS WINAPI NtQueryInformationThread( HANDLE handle, THREADINFOCLASS class, return get_thread_wow64_context( handle, data, length ); case ThreadHideFromDebugger: + /* TP Shell Service depends on ThreadHideFromDebugger returning + * STATUS_ACCESS_VIOLATION if *ret_len is not writable, before + * any other checks. Despite the status, the variable does not + * actually seem to be written at that time. */ + if (ret_len) *(volatile ULONG *)ret_len |= 0; + if (length != sizeof(BOOLEAN)) return STATUS_INFO_LENGTH_MISMATCH; if (!data) return STATUS_ACCESS_VIOLATION; SERVER_START_REQ( get_thread_info ) From be82d4799b90097128e086c627434e767e7dc646 Mon Sep 17 00:00:00 2001 From: Dylan Donnell Date: Sat, 15 Mar 2025 00:34:23 +0200 Subject: [PATCH 177/454] ntdll/tests: Add tests for ret_len on NtQueryInformationThread HideFromDebugger. (cherry picked from commit 408710e37b21da4cd1bb56f4df49f0f64e66fa5d) CW-Bug-Id: #25606 --- dlls/ntdll/tests/info.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/dlls/ntdll/tests/info.c b/dlls/ntdll/tests/info.c index 7ee6d8c22c18..4379af9af351 100644 --- a/dlls/ntdll/tests/info.c +++ b/dlls/ntdll/tests/info.c @@ -3283,7 +3283,7 @@ static void test_HideFromDebugger(void) { NTSTATUS status; HANDLE thread, stop_event; - ULONG dummy; + ULONG dummy, ret_len; dummy = 0; status = pNtSetInformationThread( GetCurrentThread(), ThreadHideFromDebugger, &dummy, sizeof(ULONG) ); @@ -3330,6 +3330,27 @@ static void test_HideFromDebugger(void) ok( status == STATUS_SUCCESS, "got %#lx\n", status ); ok( dummy == 1, "Expected dummy == 1, got %08lx\n", dummy ); + status = NtQueryInformationThread( thread, ThreadHideFromDebugger, &dummy, 1, (ULONG *)1 ); + ok( status == STATUS_ACCESS_VIOLATION, "Expected STATUS_ACCESS_VIOLATION, got %08lx\n", status ); + + status = NtQueryInformationThread( thread, ThreadHideFromDebugger, &dummy, 0, (ULONG *)1 ); + ok( status == STATUS_ACCESS_VIOLATION, "Expected STATUS_ACCESS_VIOLATION, got %08lx\n", status ); + + ret_len = 0xdeadbeef; + status = NtQueryInformationThread( thread, ThreadHideFromDebugger, &dummy, 0, &ret_len ); + ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %#lx\n", status ); + ok( ret_len == 0xdeadbeef, "Expected ret_len == deadbeef, got %08lx\n", ret_len ); + + ret_len = 0xdeadbeef; + status = NtQueryInformationThread( (HANDLE)0xdeadbeef, ThreadHideFromDebugger, &dummy, 1, &ret_len ); + ok( status == STATUS_INVALID_HANDLE, "Expected STATUS_INVALID_HANDLE, got %#lx\n", status ); + ok( ret_len == 0xdeadbeef, "Expected ret_len == deadbeef, got %08lx\n", ret_len ); + + ret_len = 0xdeadbeef; + status = NtQueryInformationThread( thread, ThreadHideFromDebugger, &dummy, 1, &ret_len ); + ok( status == STATUS_SUCCESS, "got %#lx\n", status ); + ok( ret_len == 1, "Expected ret_len == 1, got %08lx\n", ret_len ); + SetEvent( stop_event ); WaitForSingleObject( thread, INFINITE ); CloseHandle( thread ); From c1f2b81a98ed4116e7ab7bbc9fe8aa26c70549c9 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 24 Jun 2025 13:28:04 -0600 Subject: [PATCH 178/454] ntdll: HACK: Limit address space for Reverse: 1999. CW-Bug-Id: #25606 --- dlls/ntdll/unix/virtual.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c index 355a88986a37..d955a6172484 100644 --- a/dlls/ntdll/unix/virtual.c +++ b/dlls/ntdll/unix/virtual.c @@ -2484,6 +2484,24 @@ static void clear_native_views(void) } } +static void fixup_effective_user_space_limit( const void **effective_user_space_limit ) +{ +#ifdef _WIN64 + static int cached = -1; + + if (cached == -1) + { + const char *sgi = getenv( "SteamGameId" ); + cached = sgi && + ( + !strcmp( sgi, "3092660" ) + ); + } + if (cached) + *effective_user_space_limit = min( *effective_user_space_limit, (void *)0x700000000000 ); +#endif +} + /*********************************************************************** * map_view * @@ -2500,6 +2518,8 @@ static NTSTATUS map_view( struct file_view **view_ret, void *base, size_t size, const void *effective_user_space_limit = !is_win64 && wine_allocs_2g_limit ? (void *)0x7fff0000 : min( user_space_limit, host_addr_space_limit); + fixup_effective_user_space_limit( &effective_user_space_limit ); + if (alloc_type & MEM_REPLACE_PLACEHOLDER) { struct file_view *view; From 1df724ec7dca80ac4aaf1987b081c267276c9091 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 24 Jun 2025 14:23:37 -0600 Subject: [PATCH 179/454] ntdll: HACK: Limit address space for Wuthering Waves. CW-Bug-Id: #25606 --- dlls/ntdll/unix/virtual.c | 1 + 1 file changed, 1 insertion(+) diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c index d955a6172484..370f012de181 100644 --- a/dlls/ntdll/unix/virtual.c +++ b/dlls/ntdll/unix/virtual.c @@ -2495,6 +2495,7 @@ static void fixup_effective_user_space_limit( const void **effective_user_space_ cached = sgi && ( !strcmp( sgi, "3092660" ) + || !strcmp( sgi, "3513350" ) ); } if (cached) From 57f27aaa06886b112439c18a6128ce8c14ee037c Mon Sep 17 00:00:00 2001 From: Esme Povirk Date: Tue, 8 Apr 2025 19:29:05 +0000 Subject: [PATCH 180/454] advapi32: Return success from TreeSetNamedSecurityInfoW. (cherry picked from commit ab818329b67a3b226d42c016a613dc9b04f91e90) CW-Bug-Id: #25625 --- dlls/advapi32/security.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/advapi32/security.c b/dlls/advapi32/security.c index cbf7f40a9e68..fed8ef5179f6 100644 --- a/dlls/advapi32/security.c +++ b/dlls/advapi32/security.c @@ -3143,7 +3143,7 @@ DWORD WINAPI TreeSetNamedSecurityInfoW(WCHAR *name, SE_OBJECT_TYPE type, SECURIT FIXME("(%s, %d, %lu, %p, %p, %p, %p, %lu, %p, %d, %p) stub\n", debugstr_w(name), type, info, owner, group, dacl, sacl, action, progress, pis, args); - return ERROR_CALL_NOT_IMPLEMENTED; + return ERROR_SUCCESS; } /****************************************************************************** From a363cf9adf8655a94013a9c346568915cfba2256 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 25 Feb 2025 14:47:27 +0100 Subject: [PATCH 181/454] winevulkan: Generate function pointers for required funcs. (cherry picked from commit c664e71aaedca868be159a889b63357c24aee17e) CW-Bug-Id: #25625 --- dlls/winevulkan/make_vulkan | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan index c9a033fa9f39..bbaf230ac9a1 100755 --- a/dlls/winevulkan/make_vulkan +++ b/dlls/winevulkan/make_vulkan @@ -3228,7 +3228,7 @@ class VkGenerator(object): f.write("#define ALL_VK_DEVICE_FUNCS \\\n") first = True for vk_func in self.registry.device_funcs: - if not vk_func.needs_exposing(): + if not vk_func.is_required(): continue if not vk_func.needs_dispatch(): @@ -3245,7 +3245,7 @@ class VkGenerator(object): f.write("#define ALL_VK_INSTANCE_FUNCS \\\n") first = True for vk_func in self.registry.instance_funcs + self.registry.phys_dev_funcs: - if not vk_func.needs_exposing(): + if not vk_func.is_required(): continue if not vk_func.needs_dispatch(): From 3656ff03746da9ed50a1fcf4a0fcd16975803a41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 25 Feb 2025 14:52:38 +0100 Subject: [PATCH 182/454] win32u: Pass a vulkan_instance pointer to vulkan_surface_create. (cherry picked from commit bc16065593065358dcb9ff426ee9647afa501ed8) CW-Bug-Id: #25625 --- dlls/win32u/vulkan.c | 7 +++---- dlls/winemac.drv/vulkan.c | 6 +++--- dlls/winewayland.drv/vulkan.c | 4 ++-- dlls/winex11.drv/vulkan.c | 4 ++-- include/wine/vulkan_driver.h | 4 ++-- 5 files changed, 12 insertions(+), 13 deletions(-) diff --git a/dlls/win32u/vulkan.c b/dlls/win32u/vulkan.c index 4cb3997f6482..e7795101c3fd 100644 --- a/dlls/win32u/vulkan.c +++ b/dlls/win32u/vulkan.c @@ -148,8 +148,7 @@ static VkResult win32u_vkCreateWin32SurfaceKHR( VkInstance client_instance, cons surface->hwnd = dummy; } - if ((res = driver_funcs->p_vulkan_surface_create( surface->hwnd, instance->host.instance, - &host_surface, &surface->driver_private ))) + if ((res = driver_funcs->p_vulkan_surface_create( surface->hwnd, instance, &host_surface, &surface->driver_private ))) { if (dummy) NtUserDestroyWindow( dummy ); free( surface ); @@ -1454,7 +1453,7 @@ static struct vulkan_funcs vulkan_funcs = .p_get_host_surface_extension = win32u_get_host_surface_extension, }; -static VkResult nulldrv_vulkan_surface_create( HWND hwnd, VkInstance instance, VkSurfaceKHR *surface, void **private ) +static VkResult nulldrv_vulkan_surface_create( HWND hwnd, const struct vulkan_instance *instance, VkSurfaceKHR *surface, void **private ) { FIXME( "stub!\n" ); return VK_ERROR_INCOMPATIBLE_DRIVER; @@ -1524,7 +1523,7 @@ static void vulkan_driver_load(void) pthread_once( &init_once, vulkan_driver_init ); } -static VkResult lazydrv_vulkan_surface_create( HWND hwnd, VkInstance instance, VkSurfaceKHR *surface, void **private ) +static VkResult lazydrv_vulkan_surface_create( HWND hwnd, const struct vulkan_instance *instance, VkSurfaceKHR *surface, void **private ) { vulkan_driver_load(); return driver_funcs->p_vulkan_surface_create( hwnd, instance, surface, private ); diff --git a/dlls/winemac.drv/vulkan.c b/dlls/winemac.drv/vulkan.c index 063666407cd6..f570eceb963e 100644 --- a/dlls/winemac.drv/vulkan.c +++ b/dlls/winemac.drv/vulkan.c @@ -88,7 +88,7 @@ static void wine_vk_surface_destroy(struct wine_vk_surface *surface) free(surface); } -static VkResult macdrv_vulkan_surface_create(HWND hwnd, VkInstance instance, VkSurfaceKHR *surface, void **private) +static VkResult macdrv_vulkan_surface_create(HWND hwnd, const struct vulkan_instance *instance, VkSurfaceKHR *surface, void **private) { VkResult res; struct wine_vk_surface *mac_surface; @@ -135,7 +135,7 @@ static VkResult macdrv_vulkan_surface_create(HWND hwnd, VkInstance instance, VkS create_info_host.flags = 0; /* reserved */ create_info_host.pLayer = macdrv_view_get_metal_layer(mac_surface->view); - res = pvkCreateMetalSurfaceEXT(instance, &create_info_host, NULL /* allocator */, surface); + res = pvkCreateMetalSurfaceEXT(instance->host.instance, &create_info_host, NULL /* allocator */, surface); } else { @@ -145,7 +145,7 @@ static VkResult macdrv_vulkan_surface_create(HWND hwnd, VkInstance instance, VkS create_info_host.flags = 0; /* reserved */ create_info_host.pView = macdrv_view_get_metal_layer(mac_surface->view); - res = pvkCreateMacOSSurfaceMVK(instance, &create_info_host, NULL /* allocator */, surface); + res = pvkCreateMacOSSurfaceMVK(instance->host.instance, &create_info_host, NULL /* allocator */, surface); } if (res != VK_SUCCESS) { diff --git a/dlls/winewayland.drv/vulkan.c b/dlls/winewayland.drv/vulkan.c index 0e1c33707d18..03d1ad09db54 100644 --- a/dlls/winewayland.drv/vulkan.c +++ b/dlls/winewayland.drv/vulkan.c @@ -66,7 +66,7 @@ static void wine_vk_surface_destroy(struct wayland_client_surface *client) if (data) wayland_win_data_release(data); } -static VkResult wayland_vulkan_surface_create(HWND hwnd, VkInstance instance, VkSurfaceKHR *surface, void **private) +static VkResult wayland_vulkan_surface_create(HWND hwnd, const struct vulkan_instance *instance, VkSurfaceKHR *surface, void **private) { VkResult res; VkWaylandSurfaceCreateInfoKHR create_info_host; @@ -86,7 +86,7 @@ static VkResult wayland_vulkan_surface_create(HWND hwnd, VkInstance instance, Vk create_info_host.display = process_wayland.wl_display; create_info_host.surface = client->wl_surface; - res = pvkCreateWaylandSurfaceKHR(instance, &create_info_host, + res = pvkCreateWaylandSurfaceKHR(instance->host.instance, &create_info_host, NULL /* allocator */, surface); if (res != VK_SUCCESS) diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c index fbc9f933f436..b382651ae0bd 100644 --- a/dlls/winex11.drv/vulkan.c +++ b/dlls/winex11.drv/vulkan.c @@ -105,7 +105,7 @@ static BOOL disable_opwr(void) return disable; } -static VkResult X11DRV_vulkan_surface_create( HWND hwnd, VkInstance instance, VkSurfaceKHR *handle, void **private ) +static VkResult X11DRV_vulkan_surface_create( HWND hwnd, const struct vulkan_instance *instance, VkSurfaceKHR *handle, void **private ) { VkXlibSurfaceCreateInfoKHR info = { @@ -172,7 +172,7 @@ static VkResult X11DRV_vulkan_surface_create( HWND hwnd, VkInstance instance, Vk } info.window = surface->window; - if (pvkCreateXlibSurfaceKHR( instance, &info, NULL /* allocator */, handle )) + if (pvkCreateXlibSurfaceKHR( instance->host.instance, &info, NULL /* allocator */, handle )) { ERR("Failed to create Xlib surface\n"); vulkan_surface_destroy( hwnd, surface ); diff --git a/include/wine/vulkan_driver.h b/include/wine/vulkan_driver.h index 15d03383e12d..b385b93017a6 100644 --- a/include/wine/vulkan_driver.h +++ b/include/wine/vulkan_driver.h @@ -51,7 +51,7 @@ struct vulkan_client_object #include "wine/list.h" /* Wine internal vulkan driver version, needs to be bumped upon vulkan_funcs changes. */ -#define WINE_VULKAN_DRIVER_VERSION 35 +#define WINE_VULKAN_DRIVER_VERSION 36 struct vulkan_object { @@ -218,7 +218,7 @@ struct vulkan_funcs /* interface between win32u and the user drivers */ struct vulkan_driver_funcs { - VkResult (*p_vulkan_surface_create)(HWND, VkInstance, VkSurfaceKHR *, void **); + VkResult (*p_vulkan_surface_create)(HWND, const struct vulkan_instance *, VkSurfaceKHR *, void **); void (*p_vulkan_surface_destroy)(HWND, void *); void (*p_vulkan_surface_detach)(HWND, void *); void (*p_vulkan_surface_update)(HWND, void *); From da728ea6a7a09bb36000c4c7d117ee47789a8a74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 25 Feb 2025 14:47:55 +0100 Subject: [PATCH 183/454] winevulkan: Enable the VK_EXT_headless_surface extension. (cherry picked from commit 40fbf9651581b2698ab40f7460fb750d28f3acb9) CW-Bug-Id: #25625 --- dlls/winevulkan/make_vulkan | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan index bbaf230ac9a1..d84d6c400534 100755 --- a/dlls/winevulkan/make_vulkan +++ b/dlls/winevulkan/make_vulkan @@ -88,7 +88,6 @@ EXT_BLOCK_SIZE = 1000 UNSUPPORTED_EXTENSIONS = [ # Instance extensions - "VK_EXT_headless_surface", # Needs WSI work. "VK_KHR_display", # Needs WSI work. "VK_KHR_surface_protected_capabilities", "VK_LUNARG_direct_driver_loading", # Implemented in the Vulkan loader @@ -126,6 +125,7 @@ UNSUPPORTED_EXTENSIONS = [ # but not expose to applications (useful for test commits) UNEXPOSED_EXTENSIONS = { "VK_EXT_map_memory_placed", + "VK_EXT_headless_surface", } # The Vulkan loader provides entry-points for core functionality and important From d903d44c57640ddfe882706b91e72aea97f1398e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 25 Feb 2025 14:53:05 +0100 Subject: [PATCH 184/454] win32u: Use VK_EXT_headless_surface for nulldrv surface. (cherry picked from commit f0c8013879880b1631c9eca77cc863df8502b78c) CW-Bug-Id: #25625 --- dlls/win32u/vulkan.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dlls/win32u/vulkan.c b/dlls/win32u/vulkan.c index e7795101c3fd..f1bd833f53b9 100644 --- a/dlls/win32u/vulkan.c +++ b/dlls/win32u/vulkan.c @@ -1455,8 +1455,8 @@ static struct vulkan_funcs vulkan_funcs = static VkResult nulldrv_vulkan_surface_create( HWND hwnd, const struct vulkan_instance *instance, VkSurfaceKHR *surface, void **private ) { - FIXME( "stub!\n" ); - return VK_ERROR_INCOMPATIBLE_DRIVER; + VkHeadlessSurfaceCreateInfoEXT create_info = {.sType = VK_STRUCTURE_TYPE_HEADLESS_SURFACE_CREATE_INFO_EXT}; + return instance->p_vkCreateHeadlessSurfaceEXT( instance->host.instance, &create_info, NULL, surface ); } static void nulldrv_vulkan_surface_destroy( HWND hwnd, void *private ) @@ -1487,7 +1487,7 @@ static VkBool32 nulldrv_vkGetPhysicalDeviceWin32PresentationSupportKHR( VkPhysic static const char *nulldrv_get_host_surface_extension(void) { - return "VK_WINE_nulldrv_surface"; + return "VK_EXT_headless_surface"; } static const struct vulkan_driver_funcs nulldrv_funcs = From af3c63909b0b68a94bb5ca62c87bd48c950dfbf6 Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Wed, 25 Jun 2025 23:33:47 +0100 Subject: [PATCH 185/454] msvcp90: Fix vector_base_v4 allocation sizes. Before this commit, this->allocator appears to be given number of bytes as allocation size in msvcp90/details.c. But in msvcp120/tests/msvcp120.c, the provided allocator concurrent_vector_int_alloc, this size is multiplied by sizeof(int) again. A small change to log the size from concurrent_vector_int_alloc shows our size is exactly 4x native. I think that's enough evidence that the size here is meant to be No. elements. (cherry picked from commit c33ee79f43395cfa74d29e1858f938fbde29801e) CW-Bug-Id: #24534 --- dlls/msvcp90/details.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dlls/msvcp90/details.c b/dlls/msvcp90/details.c index 984a277ed121..8927872e8191 100644 --- a/dlls/msvcp90/details.c +++ b/dlls/msvcp90/details.c @@ -497,12 +497,12 @@ static void concurrent_vector_alloc_segment(_Concurrent_vector_base_v4 *this, __TRY { if(seg == 0) - this->segment[seg] = this->allocator(this, element_size * (1 << this->first_block)); + this->segment[seg] = this->allocator(this, 1 << this->first_block); else if(seg < this->first_block) this->segment[seg] = (BYTE**)this->segment[0] + element_size * (1 << seg); else - this->segment[seg] = this->allocator(this, element_size * (1 << seg)); + this->segment[seg] = this->allocator(this, 1 << seg); } __EXCEPT_ALL { From a0219ac331e24e59c51133f839f254e855ae6813 Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Wed, 25 Jun 2025 23:37:38 +0100 Subject: [PATCH 186/454] msvcp90: Fix calculation of segment addresses in vector. Casting this->segment[seg] to BYTE** cause the offset to be multiplied by the size of the pointer, which would be much larger than what's allocated. (cherry picked from commit 16720b237dfd63abe6a20ee43eac491d05003e18) CW-Bug-Id: #24534 --- dlls/msvcp90/details.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/dlls/msvcp90/details.c b/dlls/msvcp90/details.c index 8927872e8191..c54ef2a232d7 100644 --- a/dlls/msvcp90/details.c +++ b/dlls/msvcp90/details.c @@ -499,8 +499,7 @@ static void concurrent_vector_alloc_segment(_Concurrent_vector_base_v4 *this, if(seg == 0) this->segment[seg] = this->allocator(this, 1 << this->first_block); else if(seg < this->first_block) - this->segment[seg] = (BYTE**)this->segment[0] - + element_size * (1 << seg); + this->segment[seg] = (BYTE *)this->segment[0] + element_size * (1 << seg); else this->segment[seg] = this->allocator(this, 1 << seg); } @@ -728,7 +727,7 @@ void __thiscall _Concurrent_vector_base_v4__Internal_assign( if(this->early_size > v_size) { if((i ? 1 << i : 2) - remain_element > 0) - clear((BYTE**)this->segment[i] + element_size * remain_element, + clear((BYTE*)this->segment[i] + element_size * remain_element, (i ? 1 << i : 2) - remain_element); if(i < seg_no) { @@ -740,8 +739,8 @@ void __thiscall _Concurrent_vector_base_v4__Internal_assign( else if(this->early_size < v_size) { if((i ? 1 << i : 2) - remain_element > 0) - copy((BYTE**)this->segment[i] + element_size * remain_element, - (BYTE**)v->segment[i] + element_size * remain_element, + copy((BYTE*)this->segment[i] + element_size * remain_element, + (BYTE*)v->segment[i] + element_size * remain_element, (i ? 1 << i : 2) - remain_element); if(i < v_seg_no) { @@ -777,7 +776,7 @@ size_t __thiscall _Concurrent_vector_base_v4__Internal_grow_by( last_seg_no = _vector_base_v4__Segment_index_of(size + count - 1); remain_size = min(size + count, 1 << (seg_no + 1)) - size; if(remain_size > 0) - copy(((BYTE**)this->segment[seg_no] + element_size * (size - ((1 << seg_no) & ~1))), v, + copy(((BYTE*)this->segment[seg_no] + element_size * (size - ((1 << seg_no) & ~1))), v, remain_size); if(seg_no != last_seg_no) { @@ -810,7 +809,7 @@ size_t __thiscall _Concurrent_vector_base_v4__Internal_grow_to_at_least_with_res last_seg_no = _vector_base_v4__Segment_index_of(count - 1); remain_size = min(count, 1 << (seg_no + 1)) - size; if(remain_size > 0) - copy(((BYTE**)this->segment[seg_no] + element_size * (size - ((1 << seg_no) & ~1))), v, + copy(((BYTE*)this->segment[seg_no] + element_size * (size - ((1 << seg_no) & ~1))), v, remain_size); if(seg_no != last_seg_no) { @@ -876,7 +875,7 @@ void __thiscall _Concurrent_vector_base_v4__Internal_resize( clear(this->segment[seg_no], 1 << seg_no); clear_element = (1 << (end_seg_no + 1)) - resize; if(clear_element > 0) - clear((BYTE**)this->segment[end_seg_no] + element_size * (resize - ((1 << end_seg_no) & ~1)), + clear((BYTE*)this->segment[end_seg_no] + element_size * (resize - ((1 << end_seg_no) & ~1)), clear_element); this->early_size = resize; } From 42292cc12f69bf02a1807b1784664c939e9c00f5 Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Thu, 26 Jun 2025 17:24:17 +0100 Subject: [PATCH 187/454] kernelbase: Fix array underflow when checking for trailing spaces. (cherry picked from commit 613ddad0d019c1e52c8c0a80e97c317a418a02be) CW-Bug-Id: #24534 --- dlls/kernelbase/loader.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/kernelbase/loader.c b/dlls/kernelbase/loader.c index c01e60712313..b67903c4e572 100644 --- a/dlls/kernelbase/loader.c +++ b/dlls/kernelbase/loader.c @@ -568,7 +568,7 @@ HMODULE WINAPI DECLSPEC_HOTPATCH LoadLibraryExW( LPCWSTR name, HANDLE file, DWOR } RtlInitUnicodeString( &str, name ); - if (str.Buffer[str.Length/sizeof(WCHAR) - 1] != ' ') return load_library( &str, flags ); + if (str.Length && str.Buffer[str.Length/sizeof(WCHAR) - 1] != ' ') return load_library( &str, flags ); /* library name has trailing spaces */ RtlCreateUnicodeString( &str, name ); From 92742f8efdbebbad3f857d604a2664ddaa548887 Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Thu, 19 Jun 2025 16:32:08 +0100 Subject: [PATCH 188/454] crypt32: Fix creating signed message with > 1 signers. (cherry picked from commit edff0fb7c772f7ecc6ee75fe98f69ddc4f95187b) CW-Bug-Id: #24534 --- dlls/crypt32/msg.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dlls/crypt32/msg.c b/dlls/crypt32/msg.c index 5e6d59a1f581..5ba91cfa5626 100644 --- a/dlls/crypt32/msg.c +++ b/dlls/crypt32/msg.c @@ -946,10 +946,10 @@ static BOOL CSignedMsgData_ConstructSignerHandles(CSignedMsgData *msg_data, } ret = CryptCreateHash(*crypt_prov, algID, 0, 0, - &msg_data->signerHandles->contentHash); + &msg_data->signerHandles[signerIndex].contentHash); if (ret && msg_data->info->rgSignerInfo[signerIndex].AuthAttrs.cAttr > 0) ret = CryptCreateHash(*crypt_prov, algID, 0, 0, - &msg_data->signerHandles->authAttrHash); + &msg_data->signerHandles[signerIndex].authAttrHash); return ret; } From 86ce66c1b2688d5d74694109d8f43b26df3ca4f8 Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Wed, 25 Jun 2025 01:40:03 +0100 Subject: [PATCH 189/454] crypt32: Don't release context in CSignedEncodeMsg_Open. The docs say it should be released in the last CryptMsgUpdate, but tests show it's actually released in CryptMsgClose. So let's do the same. This solve a use-after-free issue for signed messages, because they contains HCRYPTHASH objects which references the context internally, and will use it in CryptDestroyHash. If the context is released in CSignedEncodeMsg_Open, this will be a use-after-free. (cherry picked from commit 18f55c306e59ef21701f16463db68c6adf1ca1ad) CW-Bug-Id: #24534 --- dlls/crypt32/msg.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/dlls/crypt32/msg.c b/dlls/crypt32/msg.c index 5ba91cfa5626..0c1519166947 100644 --- a/dlls/crypt32/msg.c +++ b/dlls/crypt32/msg.c @@ -1181,6 +1181,7 @@ typedef struct _CSignedEncodeMsg LPSTR innerOID; CRYPT_DATA_BLOB data; CSignedMsgData msg_data; + HCRYPTPROV prov[]; } CSignedEncodeMsg; static void CSignedEncodeMsg_Close(HCRYPTMSG hCryptMsg) @@ -1197,6 +1198,10 @@ static void CSignedEncodeMsg_Close(HCRYPTMSG hCryptMsg) for (i = 0; i < msg->msg_data.info->cSignerInfo; i++) CSignerInfo_Free(&msg->msg_data.info->rgSignerInfo[i]); CSignedMsgData_CloseHandles(&msg->msg_data); + if (msg->base.open_flags & CMSG_CRYPT_RELEASE_CONTEXT_FLAG) + for (i = 0; i < msg->msg_data.info->cSignerInfo; i++) + if (msg->prov[i]) + CryptReleaseContext(msg->prov[i], 0); CryptMemFree(msg->msg_data.info->signerKeySpec); CryptMemFree(msg->msg_data.info->rgSignerInfo); CryptMemFree(msg->msg_data.info); @@ -1357,7 +1362,7 @@ static HCRYPTMSG CSignedEncodeMsg_Open(DWORD dwFlags, PCMSG_STREAM_INFO pStreamInfo) { const CMSG_SIGNED_ENCODE_INFO_WITH_CMS *info = pvMsgEncodeInfo; - DWORD i; + DWORD i, saved_prov_count = 0; CSignedEncodeMsg *msg; if (info->cbSize != sizeof(CMSG_SIGNED_ENCODE_INFO) && @@ -1375,7 +1380,9 @@ static HCRYPTMSG CSignedEncodeMsg_Open(DWORD dwFlags, for (i = 0; i < info->cSigners; i++) if (!CRYPT_IsValidSigner(&info->rgSigners[i])) return NULL; - msg = CryptMemAlloc(sizeof(CSignedEncodeMsg)); + if (dwFlags & CMSG_CRYPT_RELEASE_CONTEXT_FLAG) + saved_prov_count = info->cSigners; + msg = CryptMemAlloc(offsetof(CSignedEncodeMsg, prov) + sizeof(HCRYPTPROV) * saved_prov_count); if (msg) { BOOL ret = TRUE; @@ -1432,11 +1439,10 @@ static HCRYPTMSG CSignedEncodeMsg_Open(DWORD dwFlags, &info->rgSigners[i]); if (ret) { + if (saved_prov_count) + msg->prov[i] = info->rgSigners[i].hCryptProv; ret = CSignedMsgData_ConstructSignerHandles( &msg->msg_data, i, &info->rgSigners[i].hCryptProv, &dwFlags); - if (dwFlags & CMSG_CRYPT_RELEASE_CONTEXT_FLAG) - CryptReleaseContext(info->rgSigners[i].hCryptProv, - 0); } msg->msg_data.info->signerKeySpec[i] = info->rgSigners[i].dwKeySpec; From a8f0b6ef2ac37b843d5ed0e8018608e6e21276fe Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Tue, 17 Jun 2025 18:09:07 +0100 Subject: [PATCH 190/454] crypt32: Handle missing attributes in CDecodeSignedMsg_GetParam. (cherry picked from commit 057e1d73e84b06fac90cba96dbf0305d219df3d5) CW-Bug-Id: #24534 --- dlls/crypt32/msg.c | 4 ++++ dlls/crypt32/tests/msg.c | 11 +++++++++++ 2 files changed, 15 insertions(+) diff --git a/dlls/crypt32/msg.c b/dlls/crypt32/msg.c index 0c1519166947..2326e3430aa2 100644 --- a/dlls/crypt32/msg.c +++ b/dlls/crypt32/msg.c @@ -3205,6 +3205,8 @@ static BOOL CDecodeSignedMsg_GetParam(CDecodeMsg *msg, DWORD dwParamType, { if (dwIndex >= msg->u.signed_data.info->cSignerInfo) SetLastError(CRYPT_E_INVALID_INDEX); + else if (!msg->u.signed_data.info->rgSignerInfo[dwIndex].AuthAttrs.cAttr) + SetLastError(CRYPT_E_ATTRIBUTES_MISSING); else ret = CRYPT_CopyAttr(pvData, pcbData, &msg->u.signed_data.info->rgSignerInfo[dwIndex].AuthAttrs); @@ -3217,6 +3219,8 @@ static BOOL CDecodeSignedMsg_GetParam(CDecodeMsg *msg, DWORD dwParamType, { if (dwIndex >= msg->u.signed_data.info->cSignerInfo) SetLastError(CRYPT_E_INVALID_INDEX); + else if (!msg->u.signed_data.info->rgSignerInfo[dwIndex].UnauthAttrs.cAttr) + SetLastError(CRYPT_E_ATTRIBUTES_MISSING); else ret = CRYPT_CopyAttr(pvData, pcbData, &msg->u.signed_data.info->rgSignerInfo[dwIndex].UnauthAttrs); diff --git a/dlls/crypt32/tests/msg.c b/dlls/crypt32/tests/msg.c index fa48d978e178..ef9c05d46f49 100644 --- a/dlls/crypt32/tests/msg.c +++ b/dlls/crypt32/tests/msg.c @@ -2808,6 +2808,17 @@ static void test_decode_msg_get_param(void) compare_signer_info((CMSG_SIGNER_INFO *)buf, &signer); CryptMemFree(buf); } + size = 0; + SetLastError(0xdeadbeef); + ret = CryptMsgGetParam(msg, CMSG_SIGNER_UNAUTH_ATTR_PARAM, 0, NULL, &size); + ok(!ret, "CryptMsgGetParam succeeded unexpectedly\n"); + ok(GetLastError() == CRYPT_E_ATTRIBUTES_MISSING, "unexpected error %08lx\n", GetLastError()); + ok(size == 0, "unexpected size: %lu\n", size); + SetLastError(0xdeadbeef); + ret = CryptMsgGetParam(msg, CMSG_SIGNER_AUTH_ATTR_PARAM, 0, NULL, &size); + ok(!ret, "CryptMsgGetParam succeeded unexpectedly\n"); + ok(GetLastError() == CRYPT_E_ATTRIBUTES_MISSING, "unexpected error %08lx\n", GetLastError()); + ok(size == 0, "unexpected size: %lu\n", size); /* Getting the CMS signer info of a PKCS7 message is possible. */ size = 0; ret = CryptMsgGetParam(msg, CMSG_CMS_SIGNER_INFO_PARAM, 0, NULL, &size); From f8a4ccbca6f3e0f54fcd2dbfbdeefd03b78f2cd6 Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Thu, 19 Jun 2025 13:35:31 +0100 Subject: [PATCH 191/454] crypt32: Fix missing size check in CSignedEncodeMsg_Open. Don't access CMS fields before checking cbSize. (cherry picked from commit 58dc31ace94a27bd2886ca7f32b9017156f3f257) CW-Bug-Id: #24534 --- dlls/crypt32/msg.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dlls/crypt32/msg.c b/dlls/crypt32/msg.c index 2326e3430aa2..0525eda7a803 100644 --- a/dlls/crypt32/msg.c +++ b/dlls/crypt32/msg.c @@ -1431,8 +1431,8 @@ static HCRYPTMSG CSignedEncodeMsg_Open(DWORD dwFlags, ret = FALSE; for (i = 0; ret && i < msg->msg_data.info->cSignerInfo; i++) { - if (info->rgSigners[i].SignerId.dwIdChoice == - CERT_ID_KEY_IDENTIFIER) + if (info->rgSigners[i].cbSize == sizeof(CMSG_SIGNER_ENCODE_INFO_WITH_CMS) && + info->rgSigners[i].SignerId.dwIdChoice == CERT_ID_KEY_IDENTIFIER) msg->msg_data.info->version = CMSG_SIGNED_DATA_V3; ret = CSignerInfo_Construct( &msg->msg_data.info->rgSignerInfo[i], From 72925db6501608a0debbc728cd173b4d44b98ee5 Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Tue, 17 Jun 2025 17:03:02 +0100 Subject: [PATCH 192/454] wininet: Fix handling of empty strings in urlcache_hash_key. (cherry picked from commit 74c6c7c90a9c1c91ea4e421872d692dd70e96314) CW-Bug-Id: #24534 --- dlls/wininet/urlcache.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/dlls/wininet/urlcache.c b/dlls/wininet/urlcache.c index 7975071ce910..366fa0b6bea7 100644 --- a/dlls/wininet/urlcache.c +++ b/dlls/wininet/urlcache.c @@ -1461,11 +1461,12 @@ static DWORD urlcache_hash_key(LPCSTR lpszKey) for (i = 0; i < ARRAY_SIZE(key); i++) key[i] = lookupTable[(*lpszKey + i) & 0xFF]; - for (lpszKey++; *lpszKey; lpszKey++) - { - for (i = 0; i < ARRAY_SIZE(key); i++) - key[i] = lookupTable[*lpszKey ^ key[i]]; - } + if (*lpszKey) + for (lpszKey++; *lpszKey; lpszKey++) + { + for (i = 0; i < ARRAY_SIZE(key); i++) + key[i] = lookupTable[*lpszKey ^ key[i]]; + } return *(DWORD *)key; } From 9084f05aa1cf183beaf3fd591aae429bc87e5dd3 Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Tue, 17 Jun 2025 17:05:35 +0100 Subject: [PATCH 193/454] wininet: Use BYTE instead of char for hash calculation. Since LPCSTR is signed, (*lpszKey ^ key[i]) could be negative, which means accessing the lookupTable with it will underflow. (cherry picked from commit 79c3a8322825a16e968706a7af9df22a362d5f05) CW-Bug-Id: #24534 --- dlls/wininet/urlcache.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/dlls/wininet/urlcache.c b/dlls/wininet/urlcache.c index 366fa0b6bea7..49c8ce9becc9 100644 --- a/dlls/wininet/urlcache.c +++ b/dlls/wininet/urlcache.c @@ -1455,17 +1455,18 @@ static DWORD urlcache_hash_key(LPCSTR lpszKey) 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB, 0x0A, 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1 }; + const BYTE *input = (const BYTE *)lpszKey; BYTE key[4]; DWORD i; for (i = 0; i < ARRAY_SIZE(key); i++) - key[i] = lookupTable[(*lpszKey + i) & 0xFF]; + key[i] = lookupTable[(*input + i) & 0xFF]; - if (*lpszKey) - for (lpszKey++; *lpszKey; lpszKey++) + if (*input) + for (input++; *input; input++) { for (i = 0; i < ARRAY_SIZE(key); i++) - key[i] = lookupTable[*lpszKey ^ key[i]]; + key[i] = lookupTable[*input ^ key[i]]; } return *(DWORD *)key; From ab768f984edb4bac49875d885dbe44715627faf7 Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Tue, 17 Jun 2025 17:35:59 +0100 Subject: [PATCH 194/454] wintrust: Fix data length mix-up in asn decoder. (cherry picked from commit bcc33bf62a607b3f9515e11149264a9469506500) CW-Bug-Id: #24534 --- dlls/wintrust/asn.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/wintrust/asn.c b/dlls/wintrust/asn.c index f6c4086b5088..152ff5a2fbfa 100644 --- a/dlls/wintrust/asn.c +++ b/dlls/wintrust/asn.c @@ -1347,7 +1347,7 @@ static BOOL CRYPT_AsnDecodeSPCLinkInternal(DWORD dwCertEncodingType, const BYTE *ptr = pbEncoded + 2 + lenBytes + realLenBytes; link->dwLinkChoice = SPC_FILE_LINK_CHOICE; - for (i = 0; i < dataLen / sizeof(WCHAR); i++) + for (i = 0; i < realDataLen / sizeof(WCHAR); i++) link->pwszFile[i] = hton16(*(const WORD *)(ptr + i * sizeof(WCHAR))); link->pwszFile[realDataLen / sizeof(WCHAR)] = '\0'; From 5f2ef5db0663a4d4fad6198850770d73f644cc15 Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Fri, 13 Jun 2025 19:05:29 +0100 Subject: [PATCH 195/454] kernelbase: Handle short urls in UrlIsA. (cherry picked from commit f2d660bbdd89b716f394e92d5632adb51d3ec891) CW-Bug-Id: #24534 --- dlls/kernelbase/path.c | 1 + 1 file changed, 1 insertion(+) diff --git a/dlls/kernelbase/path.c b/dlls/kernelbase/path.c index a737294e6553..eea31159daaa 100644 --- a/dlls/kernelbase/path.c +++ b/dlls/kernelbase/path.c @@ -4733,6 +4733,7 @@ BOOL WINAPI UrlIsA(const char *url, URLIS Urlis) return scheme_is_opaque( base.nScheme ); case URLIS_FILEURL: + if (strlen(url) < 5) return FALSE; return (CompareStringA(LOCALE_INVARIANT, NORM_IGNORECASE, url, 5, "file:", 5) == CSTR_EQUAL); case URLIS_DIRECTORY: From d1a5ee13a5ccb38b91529d4975325d5e45ebd05b Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Fri, 13 Jun 2025 18:51:59 +0100 Subject: [PATCH 196/454] shell32: Fix use-after-free in ShellView_WndProc. RevokeDragDrop release the drop target, if that happens to be the shell view itself, doing so will cause pThis to be freed. (cherry picked from commit 82e24b8d7c3f90f138f3f76d9e4a921d55f52851) CW-Bug-Id: #24534 --- dlls/shell32/shlview.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/shell32/shlview.c b/dlls/shell32/shlview.c index 9c699ecda08e..1aca1871758e 100644 --- a/dlls/shell32/shlview.c +++ b/dlls/shell32/shlview.c @@ -1696,8 +1696,8 @@ static LRESULT CALLBACK ShellView_WndProc(HWND hWnd, UINT uMessage, WPARAM wPara case WM_GETFONT: return SendMessageW(pThis->hWndList, WM_GETFONT, wParam, lParam); case WM_DESTROY: - RevokeDragDrop(pThis->hWnd); SHChangeNotifyDeregister(pThis->hNotify); + RevokeDragDrop(pThis->hWnd); break; case WM_ERASEBKGND: From 1d19d85abaaa1e1b06431bf7eebec26b1f999de2 Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Fri, 6 Jun 2025 16:12:54 +0100 Subject: [PATCH 197/454] mshtml: Fix misuse of IWinInetHttpInfo_QueryInfo. IWinInetHttpInfo_QueryInfo returns a multibyte string, not a wide string. (cherry picked from commit 229c91e40d05e3c921dff48e060b12f237cb0b92) CW-Bug-Id: #24534 --- dlls/mshtml/navigate.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/dlls/mshtml/navigate.c b/dlls/mshtml/navigate.c index f05bf0be9c6d..19837d247be0 100644 --- a/dlls/mshtml/navigate.c +++ b/dlls/mshtml/navigate.c @@ -814,7 +814,8 @@ static void query_http_info(nsChannelBSC *This, IWinInetHttpInfo *wininet_info) { const WCHAR *ptr; DWORD len = 0; - WCHAR *buf; + WCHAR *wbuf; + char *buf; IWinInetHttpInfo_QueryInfo(wininet_info, HTTP_QUERY_RAW_HEADERS_CRLF, NULL, &len, NULL, NULL); if(!len) @@ -830,13 +831,18 @@ static void query_http_info(nsChannelBSC *This, IWinInetHttpInfo *wininet_info) return; } - ptr = wcschr(buf, '\r'); + wbuf = strdupAtoW(buf); + free(buf); + if (!wbuf) + return; + + ptr = wcschr(wbuf, '\r'); if(ptr && ptr[1] == '\n') { ptr += 2; process_response_headers(This, ptr); } - free(buf); + free(wbuf); } HRESULT start_binding(HTMLInnerWindow *inner_window, BSCallback *bscallback, IBindCtx *bctx) From c2609d010bb484fb9af2582bbfdcf1191120bcd4 Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Thu, 5 Jun 2025 13:51:07 +0100 Subject: [PATCH 198/454] d3dx9: Set shared parameters pointer to NULL when freeing it. This pointer might later be passed to _realloc in d3dx_pool_sync_shared_parameter. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=55593 Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=57230 (cherry picked from commit 1031288361679219d61cfe82c0f4736b36eb7885) CW-Bug-Id: #24534 --- dlls/d3dx9_36/effect.c | 1 + 1 file changed, 1 insertion(+) diff --git a/dlls/d3dx9_36/effect.c b/dlls/d3dx9_36/effect.c index 628d8ebc3eb9..a89dc679a77a 100644 --- a/dlls/d3dx9_36/effect.c +++ b/dlls/d3dx9_36/effect.c @@ -1943,6 +1943,7 @@ static void d3dx_pool_release_shared_parameter(struct d3dx_top_level_parameter * else { free(param->shared_data->parameters); + param->shared_data->parameters = NULL; /* Zeroing table size is required as the entry in pool parameters table can be reused. */ param->shared_data->size = 0; param->shared_data = NULL; From a55f9aa49f21b973d0a70d6bb008931457561a1a Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Tue, 10 Jun 2025 14:24:39 +0100 Subject: [PATCH 199/454] mshtml: Fix buffer underflow in range_to_string. range_to_string scans the buf from right to left for whitespaces. If the buf contains only whitespaces, we will scan past the start of the buf. (cherry picked from commit 6b7fb8cf7150e1d165a2397e39d4f7965d074933) CW-Bug-Id: #24534 --- dlls/mshtml/range.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/mshtml/range.c b/dlls/mshtml/range.c index f10a42a895db..fdd39f65615f 100644 --- a/dlls/mshtml/range.c +++ b/dlls/mshtml/range.c @@ -503,7 +503,7 @@ static void range_to_string(HTMLTxtRange *This, wstrbuf_t *buf) if(buf->len) { WCHAR *p; - for(p = buf->buf+buf->len-1; p >= buf->buf && iswspace(*p); p--); + for(p = buf->buf+buf->len-1; p > buf->buf && iswspace(*p); p--); p = wcschr(p, '\r'); if(p) From 2ba237c16ccf806d3b4e038c71d0ecc5402e87db Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Mon, 9 Jun 2025 21:38:49 +0100 Subject: [PATCH 200/454] server: Fix leak of object name in device_open_file. (cherry picked from commit db2227c43919bb1df932ac06a857bf9c42d227eb) CW-Bug-Id: #24534 --- server/device.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/server/device.c b/server/device.c index 20950f507101..4c5eadcb399b 100644 --- a/server/device.c +++ b/server/device.c @@ -441,6 +441,7 @@ static struct object *device_open_file( struct object *obj, unsigned int access, struct device *device = (struct device *)obj; struct device_file *file; struct unicode_str nt_name; + WCHAR *fullname; if (!(file = alloc_object( &device_file_ops ))) return NULL; @@ -451,11 +452,16 @@ static struct object *device_open_file( struct object *obj, unsigned int access, list_add_tail( &device->files, &file->entry ); if (device->unix_path) { - mode_t mode = 0666; - access = file->obj.ops->map_access( &file->obj, access ); - nt_name.str = device->obj.ops->get_full_name( &device->obj, &nt_name.len ); - file->fd = open_fd( NULL, device->unix_path, nt_name, O_NONBLOCK, &mode, access, sharing, options ); - if (file->fd) set_fd_user( file->fd, &device_file_fd_ops, &file->obj ); + if ((fullname = device->obj.ops->get_full_name( &device->obj, &nt_name.len ))) + { + mode_t mode = 0666; + access = file->obj.ops->map_access( &file->obj, access ); + nt_name.str = fullname; + file->fd = open_fd( NULL, device->unix_path, nt_name, O_NONBLOCK, &mode, access, sharing, options ); + if (file->fd) set_fd_user( file->fd, &device_file_fd_ops, &file->obj ); + free( fullname ); + } + else file->fd = NULL; } else file->fd = alloc_pseudo_fd( &device_file_fd_ops, &file->obj, options ); From 5d68fc22914f2bb3296ee77dea73698340794421 Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Mon, 9 Jun 2025 18:47:29 +0100 Subject: [PATCH 201/454] iphlpapi: Fix use-after-free of apc context. In IcmpSendEcho2Ex, if STATUS_PENDING is returned from NtDeviceIoControlFile, there are two cases. If no event handle or apc rountine were given, we wait for the request to completion before returning, thus freeing the apc context is fine in this case. But if an event handle _is_ given, we will return STATUS_PENDING, and the request will still be in flight at this point, and we cannot free the apc context. However, the condition for freeing the context only checks for apc_routine, and not event, resulting in use-after-free if an apc_routine is not given but an event is. (cherry picked from commit 6a0cea6bd2cf5d92160b9766aadf1e06fe9b47a1) CW-Bug-Id: #24534 --- dlls/iphlpapi/iphlpapi_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/iphlpapi/iphlpapi_main.c b/dlls/iphlpapi/iphlpapi_main.c index 172afbd62b85..78000a628b74 100644 --- a/dlls/iphlpapi/iphlpapi_main.c +++ b/dlls/iphlpapi/iphlpapi_main.c @@ -4820,7 +4820,7 @@ DWORD WINAPI IcmpSendEcho2Ex( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc_r ret = IcmpParseReplies( reply, reply_size ); if (!event && request_event) CloseHandle( request_event ); - if (!apc_routine || status != STATUS_PENDING) heap_free( ctxt ); + if ((!apc_routine && !event) || status != STATUS_PENDING) heap_free( ctxt ); heap_free( in ); if (status) SetLastError( RtlNtStatusToDosError( status ) ); From 83b9ac60f27bc02a9f3704334fe5f232dd197c3f Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Mon, 2 Jun 2025 22:48:47 +0100 Subject: [PATCH 202/454] msctf: Fix read of invalid memory in SINK_FOR_EACH. If the list being iterated over is empty, LIST_ENTRY(cursor,Sink,entry) does not actually point to a Sink, and therefore reading `->interfaces.p##type` from it is invalid. This access should only happen after the `(cursor) != (list)` check. (cherry picked from commit 2329622b3047ed7622b64f0f50a8d3c248eb7608) CW-Bug-Id: #24534 --- dlls/msctf/msctf_internal.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dlls/msctf/msctf_internal.h b/dlls/msctf/msctf_internal.h index 3696edf55000..e465b985bf2b 100644 --- a/dlls/msctf/msctf_internal.h +++ b/dlls/msctf/msctf_internal.h @@ -86,9 +86,9 @@ typedef struct { #define SINK_ENTRY(cursor,type) (LIST_ENTRY(cursor,Sink,entry)->interfaces.p##type) #define SINK_FOR_EACH(cursor,list,type,elem) \ - for ((cursor) = (list)->next, elem = SINK_ENTRY(cursor,type); \ - (cursor) != (list); \ - (cursor) = (cursor)->next, elem = SINK_ENTRY(cursor,type)) + for ((cursor) = (list)->next; \ + (cursor) != (list) && (elem = SINK_ENTRY(cursor, type), 1); \ + (cursor) = (cursor)->next) HRESULT advise_sink(struct list *sink_list, REFIID riid, DWORD cookie_magic, IUnknown *unk, DWORD *cookie); HRESULT unadvise_sink(DWORD cookie); From 94e1e7da3eb2e0b8318ceb95b34e9aa6a7bfdfb6 Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Fri, 30 May 2025 13:58:27 +0100 Subject: [PATCH 203/454] crypt32: Fix invalid access of list head. At the end of LIST_FOR_EACH_ENTRY, assuming no matches were found, `provider` will point to the list head, instead of being NULL. As a result we call IsEqualGUID on invalid memory. (cherry picked from commit bcf7fdca088fe79ccf8277325de71c4126f85fd2) CW-Bug-Id: #24534 --- dlls/crypt32/sip.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/dlls/crypt32/sip.c b/dlls/crypt32/sip.c index 132f491b2f1e..d9da19b62728 100644 --- a/dlls/crypt32/sip.c +++ b/dlls/crypt32/sip.c @@ -562,10 +562,11 @@ static WINE_SIP_PROVIDER *CRYPT_GetCachedSIP(const GUID *pgSubject) LIST_FOR_EACH_ENTRY(provider, &providers, WINE_SIP_PROVIDER, entry) { if (IsEqualGUID(pgSubject, &provider->subject)) + { + ret = provider; break; + } } - if (provider && IsEqualGUID(pgSubject, &provider->subject)) - ret = provider; LeaveCriticalSection(&providers_cs); return ret; } From dd05e9ea6ea8ca76f807e4f5af370ae8c06a2113 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 2 Jul 2025 19:37:06 -0600 Subject: [PATCH 204/454] winepulse.drv: Avoid hangs on exit when pulse main loop thread gets killed. CW-Bug-Id: #25628 --- dlls/winepulse.drv/pulse.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/dlls/winepulse.drv/pulse.c b/dlls/winepulse.drv/pulse.c index 9cb29be21b62..462fb20071e5 100644 --- a/dlls/winepulse.drv/pulse.c +++ b/dlls/winepulse.drv/pulse.c @@ -209,10 +209,10 @@ static BOOL wait_pa_operation_complete(pa_operation *o) if (!o) return FALSE; - while (pa_operation_get_state(o) == PA_OPERATION_RUNNING) + while (pulse_ml && pa_operation_get_state(o) == PA_OPERATION_RUNNING) pulse_cond_wait(); pa_operation_unref(o); - return TRUE; + return !!pulse_ml; } /* Following pulseaudio design here, mainloop has the lock taken whenever @@ -244,6 +244,7 @@ static NTSTATUS pulse_process_attach(void *args) pthread_mutexattr_init(&attr); pthread_mutexattr_setprotocol(&attr, PTHREAD_PRIO_INHERIT); + pthread_mutexattr_setrobust(&attr, PTHREAD_MUTEX_ROBUST); if (pthread_mutex_init(&pulse_mutex, &attr) != 0) pthread_mutex_init(&pulse_mutex, NULL); @@ -275,6 +276,14 @@ static NTSTATUS pulse_process_detach(void *args) return STATUS_SUCCESS; } +static void pulse_main_loop_thread_cleanup(void *context) +{ + TRACE("Main loop thread is being aborted.\n"); + + pulse_ml = NULL; + pulse_broadcast(); +} + static NTSTATUS pulse_main_loop(void *args) { struct main_loop_params *params = args; @@ -283,7 +292,9 @@ static NTSTATUS pulse_main_loop(void *args) pulse_ml = pa_mainloop_new(); pa_mainloop_set_poll_func(pulse_ml, pulse_poll_func, NULL); NtSetEvent(params->event, NULL); + pthread_cleanup_push(pulse_main_loop_thread_cleanup, NULL); pa_mainloop_run(pulse_ml, &ret); + pthread_cleanup_pop(0); pa_mainloop_free(pulse_ml); pulse_unlock(); return STATUS_SUCCESS; @@ -1248,7 +1259,7 @@ static NTSTATUS pulse_release_stream(void *args) pulse_lock(); if (PA_STREAM_IS_GOOD(pa_stream_get_state(stream->stream))) { pa_stream_disconnect(stream->stream); - while (PA_STREAM_IS_GOOD(pa_stream_get_state(stream->stream))) + while (pulse_ml && PA_STREAM_IS_GOOD(pa_stream_get_state(stream->stream))) pulse_cond_wait(); } pa_stream_unref(stream->stream); From ac986aa56e51baf623433bea52c551d50ec1abf6 Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Fri, 4 Jul 2025 12:28:16 +0100 Subject: [PATCH 205/454] mscoree: Fix missing null terminator in unix_prefix. CW-Bug-Id: #24534 --- dlls/mscoree/metahost.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/mscoree/metahost.c b/dlls/mscoree/metahost.c index 46e22490e8e8..83e527805112 100644 --- a/dlls/mscoree/metahost.c +++ b/dlls/mscoree/metahost.c @@ -842,7 +842,7 @@ static BOOL get_mono_path_datadir(LPWSTR path) { static const WCHAR winedatadirW[] = {'W','I','N','E','D','A','T','A','D','I','R',0}; static const WCHAR winebuilddirW[] = {'W','I','N','E','B','U','I','L','D','D','I','R',0}; - static const WCHAR unix_prefix[] = {'\\','?','?','\\','u','n','i','x','\\'}; + static const WCHAR unix_prefix[] = {'\\','?','?','\\','u','n','i','x','\\',0}; static const WCHAR monoW[] = {'\\','m','o','n','o',0}; static const WCHAR dotdotmonoW[] = {'\\','.','.','\\','m','o','n','o',0}; const WCHAR *data_dir, *suffix; From 8cca371b3d0f593c4de4c29d04d84bebe48d4cd5 Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Fri, 4 Jul 2025 12:44:53 +0100 Subject: [PATCH 206/454] makedep: Don't free strings added to strarray. strarray_add does not clone the string. CW-Bug-Id: #24534 --- tools/makedep.c | 1 - 1 file changed, 1 deletion(-) diff --git a/tools/makedep.c b/tools/makedep.c index 4c9333e401bd..22a7c14710fc 100644 --- a/tools/makedep.c +++ b/tools/makedep.c @@ -4483,7 +4483,6 @@ static void load_sources( struct makefile *make ) char *module; if (!(module = get_expanded_arch_var( make, "MODULE", arch ))) continue; strarray_add( &make->install[INSTALL_LIB], module ); - free( module ); } } } From 5f59675f5c7560a3e18b7c597e89d7360f60acf5 Mon Sep 17 00:00:00 2001 From: Arkadiusz Hiler Date: Mon, 7 Jul 2025 10:22:16 +0300 Subject: [PATCH 207/454] ntdll: Fully resolve 64bit native steamclient dependencies without calling DllMain(). The partial import fixup performed by fixup_imports( wm, load_path ); wm->ldr.Flags |= LDR_DONT_RESOLVE_REFS; resulted in all the imports being loded but without continuing the "resolve" later on - DllMain() of those DLLs was not called. This could result in things like secur32 being loded but none of the providers are initialized if steamclient loads that DLL first. Fixes: 40af052fe02c ("ntdll: HACK: Partially fixup imports for Win Steam libs." CW-Bug-Id: #24174 CW-Bug-Id: #25106 CW-Bug-Id: #25635 --- dlls/ntdll/loader.c | 10 +++++----- include/winternl.h | 1 + 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/dlls/ntdll/loader.c b/dlls/ntdll/loader.c index 75669e95b723..bc71fe04c94a 100644 --- a/dlls/ntdll/loader.c +++ b/dlls/ntdll/loader.c @@ -1720,7 +1720,7 @@ static NTSTATUS MODULE_InitDLL( WINE_MODREF *wm, UINT reason, LPVOID lpReserved /* Skip calls for modules loaded with special load flags */ - if (wm->ldr.Flags & LDR_DONT_RESOLVE_REFS) return STATUS_SUCCESS; + if (wm->ldr.Flags & (LDR_DONT_RESOLVE_REFS | LDR_DONT_CALL_DLLMAIN)) return STATUS_SUCCESS; if (wm->ldr.TlsIndex == -1) call_tls_callbacks( wm->ldr.DllBase, reason ); if (!entry) return STATUS_SUCCESS; @@ -2336,8 +2336,6 @@ static NTSTATUS build_module( LPCWSTR load_path, const UNICODE_STRING *nt_name, { struct steamclient_setup_trampolines_params params = {.src_mod = *module, .tgt_mod = lsteamclient}; WINE_UNIX_CALL( unix_steamclient_setup_trampolines, ¶ms ); - wm->ldr.Flags |= LDR_DONT_RESOLVE_REFS; - flags |= DONT_RESOLVE_DLL_REFERENCES; if (is_steamclient32) { OBJECT_ATTRIBUTES attr; @@ -2348,6 +2346,9 @@ static NTSTATUS build_module( LPCWSTR load_path, const UNICODE_STRING *nt_name, DWORD protect_old; HANDLE file; + wm->ldr.Flags |= LDR_DONT_RESOLVE_REFS; + flags |= DONT_RESOLVE_DLL_REFERENCES; + NtProtectVirtualMemory( NtCurrentProcess(), &addr, &size, PAGE_READWRITE, &protect_old ); memset( &attr, 0, sizeof(attr) ); attr.Length = sizeof(attr); @@ -2365,8 +2366,7 @@ static NTSTATUS build_module( LPCWSTR load_path, const UNICODE_STRING *nt_name, } else { - fixup_imports( wm, load_path ); - wm->ldr.Flags |= LDR_DONT_RESOLVE_REFS; + wm->ldr.Flags |= LDR_DONT_CALL_DLLMAIN; } } diff --git a/include/winternl.h b/include/winternl.h index be01f3b31d08..465341849c85 100644 --- a/include/winternl.h +++ b/include/winternl.h @@ -3956,6 +3956,7 @@ typedef void (CALLBACK *PLDR_DLL_NOTIFICATION_FUNCTION)(ULONG, LDR_DLL_NOTIFICAT /* these ones is Wine specific */ #define LDR_DONT_RESOLVE_REFS 0x40000000 #define LDR_WINE_INTERNAL 0x80000000 +#define LDR_DONT_CALL_DLLMAIN 0x20000000 /* flag for LdrAddRefDll */ #define LDR_ADDREF_DLL_PIN 0x00000001 From 0f2977d3f782e91aa28a593b48a91fc61930aeff Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Thu, 3 Jul 2025 15:26:44 -0700 Subject: [PATCH 208/454] wineboot: Detect TSC frequency on Arm64. Otherwise the `~MHz` registry key is initialized to the maximum clock speed of the CPUs, which is known to break games. (cherry picked from commit 97d873355e1bc5445a9b856a7d160600fd41e4c2) --- programs/wineboot/wineboot.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/programs/wineboot/wineboot.c b/programs/wineboot/wineboot.c index f058ac9f3782..a18bd197b2c8 100644 --- a/programs/wineboot/wineboot.c +++ b/programs/wineboot/wineboot.c @@ -378,6 +378,15 @@ static UINT64 read_tsc_frequency(void) return freq; } +#elif defined(__aarch64__) + +static UINT64 read_tsc_frequency(void) +{ + UINT64 tsc_frequency; + __asm__ volatile( "mrs %[Res], CNTFRQ_EL0" : [Res] "=r" (tsc_frequency) ); + return tsc_frequency; +} + #else static void initialize_xstate_features(struct _KUSER_SHARED_DATA *data) From 24b230734faee5dd92c8c59954aec301fe2231d3 Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Tue, 17 Dec 2024 11:29:36 +0100 Subject: [PATCH 209/454] include: Add a couple of definitions to mscvpdb.h. Signed-off-by: Eric Pouech (cherry picked from commit 5c6de457cbb4f3aae6bf871b17c12cbfa0d01a45) --- include/wine/mscvpdb.h | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/include/wine/mscvpdb.h b/include/wine/mscvpdb.h index d389d5d22d0e..a190d1cd4da6 100644 --- a/include/wine/mscvpdb.h +++ b/include/wine/mscvpdb.h @@ -935,10 +935,12 @@ union codeview_fieldtype #define T_SHORT 0x0011 /* short */ #define T_LONG 0x0012 /* long */ #define T_QUAD 0x0013 /* long long */ +#define T_OCT 0x0014 /* 128bit int */ #define T_UCHAR 0x0020 /* unsigned char */ #define T_USHORT 0x0021 /* unsigned short */ #define T_ULONG 0x0022 /* unsigned long */ #define T_UQUAD 0x0023 /* unsigned long long */ +#define T_UOCT 0x0024 /* 128bit unsigned int */ #define T_BOOL08 0x0030 /* 8-bit boolean */ #define T_BOOL16 0x0031 /* 16-bit boolean */ #define T_BOOL32 0x0032 /* 32-bit boolean */ @@ -948,6 +950,7 @@ union codeview_fieldtype #define T_REAL80 0x0042 /* 80-bit real */ #define T_REAL128 0x0043 /* 128-bit real */ #define T_REAL48 0x0044 /* 48-bit real */ +#define T_REAL16 0x0046 /* 16-bit real */ #define T_CPLX32 0x0050 /* 32-bit complex number */ #define T_CPLX64 0x0051 /* 64-bit complex number */ #define T_CPLX80 0x0052 /* 80-bit complex number */ @@ -968,14 +971,17 @@ union codeview_fieldtype /* near pointers to basic types */ #define T_PVOID 0x0103 /* near pointer to void */ +#define T_PCURRENCY 0x0104 /* near pointer to currency */ #define T_PCHAR 0x0110 /* Near pointer to 8-bit signed */ #define T_PSHORT 0x0111 /* Near pointer to 16-bit signed */ #define T_PLONG 0x0112 /* Near pointer to 32-bit signed */ #define T_PQUAD 0x0113 /* Near pointer to 64-bit signed */ +#define T_POCT 0x0114 /* Near pointer to 128-bit signed */ #define T_PUCHAR 0x0120 /* Near pointer to 8-bit unsigned */ #define T_PUSHORT 0x0121 /* Near pointer to 16-bit unsigned */ #define T_PULONG 0x0122 /* Near pointer to 32-bit unsigned */ #define T_PUQUAD 0x0123 /* Near pointer to 64-bit unsigned */ +#define T_PUOCT 0x0124 /* Near pointer to 128-bit unsigned */ #define T_PBOOL08 0x0130 /* Near pointer to 8-bit Boolean */ #define T_PBOOL16 0x0131 /* Near pointer to 16-bit Boolean */ #define T_PBOOL32 0x0132 /* Near pointer to 32-bit Boolean */ @@ -985,6 +991,7 @@ union codeview_fieldtype #define T_PREAL80 0x0142 /* Near pointer to 80-bit real */ #define T_PREAL128 0x0143 /* Near pointer to 128-bit real */ #define T_PREAL48 0x0144 /* Near pointer to 48-bit real */ +#define T_PREAL16 0x0146 /* Near pointer to 16-bit real */ #define T_PCPLX32 0x0150 /* Near pointer to 32-bit complex */ #define T_PCPLX64 0x0151 /* Near pointer to 64-bit complex */ #define T_PCPLX80 0x0152 /* Near pointer to 80-bit complex */ @@ -1003,14 +1010,17 @@ union codeview_fieldtype /* far pointers to basic types */ #define T_PFVOID 0x0203 /* Far pointer to void */ +#define T_PFCURRENCT 0x0204 /* Far pointer to currency */ #define T_PFCHAR 0x0210 /* Far pointer to 8-bit signed */ #define T_PFSHORT 0x0211 /* Far pointer to 16-bit signed */ #define T_PFLONG 0x0212 /* Far pointer to 32-bit signed */ #define T_PFQUAD 0x0213 /* Far pointer to 64-bit signed */ +#define T_PFOCT 0x0214 /* Far pointer to 128-bit signed */ #define T_PFUCHAR 0x0220 /* Far pointer to 8-bit unsigned */ #define T_PFUSHORT 0x0221 /* Far pointer to 16-bit unsigned */ #define T_PFULONG 0x0222 /* Far pointer to 32-bit unsigned */ #define T_PFUQUAD 0x0223 /* Far pointer to 64-bit unsigned */ +#define T_PFUOCT 0x0224 /* Far pointer to 128-bit unsigned */ #define T_PFBOOL08 0x0230 /* Far pointer to 8-bit Boolean */ #define T_PFBOOL16 0x0231 /* Far pointer to 16-bit Boolean */ #define T_PFBOOL32 0x0232 /* Far pointer to 32-bit Boolean */ @@ -1020,6 +1030,7 @@ union codeview_fieldtype #define T_PFREAL80 0x0242 /* Far pointer to 80-bit real */ #define T_PFREAL128 0x0243 /* Far pointer to 128-bit real */ #define T_PFREAL48 0x0244 /* Far pointer to 48-bit real */ +#define T_PFREAL16 0x0246 /* Far pointer to 16-bit real */ #define T_PFCPLX32 0x0250 /* Far pointer to 32-bit complex */ #define T_PFCPLX64 0x0251 /* Far pointer to 64-bit complex */ #define T_PFCPLX80 0x0252 /* Far pointer to 80-bit complex */ @@ -1038,14 +1049,17 @@ union codeview_fieldtype /* huge pointers to basic types */ #define T_PHVOID 0x0303 /* Huge pointer to void */ +#define T_PHCURRENCY 0x0304 /* Huge pointer to currency */ #define T_PHCHAR 0x0310 /* Huge pointer to 8-bit signed */ #define T_PHSHORT 0x0311 /* Huge pointer to 16-bit signed */ #define T_PHLONG 0x0312 /* Huge pointer to 32-bit signed */ #define T_PHQUAD 0x0313 /* Huge pointer to 64-bit signed */ +#define T_PHOCT 0x0314 /* Huge pointer to 128-bit signed */ #define T_PHUCHAR 0x0320 /* Huge pointer to 8-bit unsigned */ #define T_PHUSHORT 0x0321 /* Huge pointer to 16-bit unsigned */ #define T_PHULONG 0x0322 /* Huge pointer to 32-bit unsigned */ #define T_PHUQUAD 0x0323 /* Huge pointer to 64-bit unsigned */ +#define T_PHUOCT 0x0324 /* Huge pointer to 128-bit unsigned */ #define T_PHBOOL08 0x0330 /* Huge pointer to 8-bit Boolean */ #define T_PHBOOL16 0x0331 /* Huge pointer to 16-bit Boolean */ #define T_PHBOOL32 0x0332 /* Huge pointer to 32-bit Boolean */ @@ -1055,6 +1069,7 @@ union codeview_fieldtype #define T_PHREAL80 0x0342 /* Huge pointer to 80-bit real */ #define T_PHREAL128 0x0343 /* Huge pointer to 128-bit real */ #define T_PHREAL48 0x0344 /* Huge pointer to 48-bit real */ +#define T_PHREAL16 0x0346 /* Far pointer to 16-bit real */ #define T_PHCPLX32 0x0350 /* Huge pointer to 32-bit complex */ #define T_PHCPLX64 0x0351 /* Huge pointer to 64-bit complex */ #define T_PHCPLX80 0x0352 /* Huge pointer to 80-bit complex */ @@ -1073,15 +1088,18 @@ union codeview_fieldtype /* 32-bit near pointers to basic types */ #define T_32PVOID 0x0403 /* 32-bit near pointer to void */ +#define T_32PCURRENCY 0x0404 /* 32-bit near pointer to currency */ #define T_32PHRESULT 0x0408 /* 16:32 near pointer to HRESULT - or error code ??? */ #define T_32PCHAR 0x0410 /* 16:32 near pointer to 8-bit signed */ #define T_32PSHORT 0x0411 /* 16:32 near pointer to 16-bit signed */ #define T_32PLONG 0x0412 /* 16:32 near pointer to 32-bit signed */ #define T_32PQUAD 0x0413 /* 16:32 near pointer to 64-bit signed */ +#define T_32POCT 0x0414 /* 16:32 near pointer to 128-bit signed */ #define T_32PUCHAR 0x0420 /* 16:32 near pointer to 8-bit unsigned */ #define T_32PUSHORT 0x0421 /* 16:32 near pointer to 16-bit unsigned */ #define T_32PULONG 0x0422 /* 16:32 near pointer to 32-bit unsigned */ #define T_32PUQUAD 0x0423 /* 16:32 near pointer to 64-bit unsigned */ +#define T_32PUOCT 0x0424 /* 16:32 near pointer to 128-bit unsigned */ #define T_32PBOOL08 0x0430 /* 16:32 near pointer to 8-bit Boolean */ #define T_32PBOOL16 0x0431 /* 16:32 near pointer to 16-bit Boolean */ #define T_32PBOOL32 0x0432 /* 16:32 near pointer to 32-bit Boolean */ @@ -1091,6 +1109,7 @@ union codeview_fieldtype #define T_32PREAL80 0x0442 /* 16:32 near pointer to 80-bit real */ #define T_32PREAL128 0x0443 /* 16:32 near pointer to 128-bit real */ #define T_32PREAL48 0x0444 /* 16:32 near pointer to 48-bit real */ +#define T_32PREAL16 0x0446 /* 16:32 near pointer to 16-bit real */ #define T_32PCPLX32 0x0450 /* 16:32 near pointer to 32-bit complex */ #define T_32PCPLX64 0x0451 /* 16:32 near pointer to 64-bit complex */ #define T_32PCPLX80 0x0452 /* 16:32 near pointer to 80-bit complex */ @@ -1109,15 +1128,18 @@ union codeview_fieldtype /* 32-bit far pointers to basic types */ #define T_32PFVOID 0x0503 /* 32-bit far pointer to void */ +#define T_32PFCURRENCY 0x0504 /* 32-bit far pointer to void */ #define T_32PFHRESULT 0x0508 /* 16:32 far pointer to HRESULT - or error code ??? */ #define T_32PFCHAR 0x0510 /* 16:32 far pointer to 8-bit signed */ #define T_32PFSHORT 0x0511 /* 16:32 far pointer to 16-bit signed */ #define T_32PFLONG 0x0512 /* 16:32 far pointer to 32-bit signed */ #define T_32PFQUAD 0x0513 /* 16:32 far pointer to 64-bit signed */ +#define T_32PFOCT 0x0514 /* 16:32 far pointer to 128-bit signed */ #define T_32PFUCHAR 0x0520 /* 16:32 far pointer to 8-bit unsigned */ #define T_32PFUSHORT 0x0521 /* 16:32 far pointer to 16-bit unsigned */ #define T_32PFULONG 0x0522 /* 16:32 far pointer to 32-bit unsigned */ #define T_32PFUQUAD 0x0523 /* 16:32 far pointer to 64-bit unsigned */ +#define T_32PFUOCT 0x0524 /* 16:32 far pointer to 128-bit unsigned */ #define T_32PFBOOL08 0x0530 /* 16:32 far pointer to 8-bit Boolean */ #define T_32PFBOOL16 0x0531 /* 16:32 far pointer to 16-bit Boolean */ #define T_32PFBOOL32 0x0532 /* 16:32 far pointer to 32-bit Boolean */ @@ -1127,6 +1149,7 @@ union codeview_fieldtype #define T_32PFREAL80 0x0542 /* 16:32 far pointer to 80-bit real */ #define T_32PFREAL128 0x0543 /* 16:32 far pointer to 128-bit real */ #define T_32PFREAL48 0x0544 /* 16:32 far pointer to 48-bit real */ +#define T_32PFREAL16 0x0546 /* 16:32 far pointer to 16-bit real */ #define T_32PFCPLX32 0x0550 /* 16:32 far pointer to 32-bit complex */ #define T_32PFCPLX64 0x0551 /* 16:32 far pointer to 64-bit complex */ #define T_32PFCPLX80 0x0552 /* 16:32 far pointer to 80-bit complex */ @@ -1145,15 +1168,18 @@ union codeview_fieldtype /* 64-bit near pointers to basic types */ #define T_64PVOID 0x0603 /* 64-bit near pointer to void */ +#define T_64PCURRENCY 0x0604 /* 64-bit near pointer to void */ #define T_64PHRESULT 0x0608 /* 64 near pointer to HRESULT - or error code ??? */ #define T_64PCHAR 0x0610 /* 64 near pointer to 8-bit signed */ #define T_64PSHORT 0x0611 /* 64 near pointer to 16-bit signed */ #define T_64PLONG 0x0612 /* 64 near pointer to 32-bit signed */ #define T_64PQUAD 0x0613 /* 64 near pointer to 64-bit signed */ +#define T_64POCT 0x0614 /* 64 near pointer to 128-bit signed */ #define T_64PUCHAR 0x0620 /* 64 near pointer to 8-bit unsigned */ #define T_64PUSHORT 0x0621 /* 64 near pointer to 16-bit unsigned */ #define T_64PULONG 0x0622 /* 64 near pointer to 32-bit unsigned */ #define T_64PUQUAD 0x0623 /* 64 near pointer to 64-bit unsigned */ +#define T_64PUOCT 0x0624 /* 64 near pointer to 128-bit unsigned */ #define T_64PBOOL08 0x0630 /* 64 near pointer to 8-bit Boolean */ #define T_64PBOOL16 0x0631 /* 64 near pointer to 16-bit Boolean */ #define T_64PBOOL32 0x0632 /* 64 near pointer to 32-bit Boolean */ @@ -1163,6 +1189,7 @@ union codeview_fieldtype #define T_64PREAL80 0x0642 /* 64 near pointer to 80-bit real */ #define T_64PREAL128 0x0643 /* 64 near pointer to 128-bit real */ #define T_64PREAL48 0x0644 /* 64 near pointer to 48-bit real */ +#define T_64PREAL16 0x0646 /* 64 near pointer to 16-bit real */ #define T_64PCPLX32 0x0650 /* 64 near pointer to 32-bit complex */ #define T_64PCPLX64 0x0651 /* 64 near pointer to 64-bit complex */ #define T_64PCPLX80 0x0652 /* 64 near pointer to 80-bit complex */ @@ -1338,6 +1365,12 @@ union codeview_fieldtype #define LF_COMPLEX80 0x800e #define LF_COMPLEX128 0x800f #define LF_VARSTRING 0x8010 +#define LF_OCTWORD 0x8017 +#define LF_UOCTWORD 0x8018 +#define LF_DECIMAL 0x8019 +#define LF_DATE 0x801a +#define LF_UTF8STRING 0x801b +#define LF_REAL16 0x801c /* symtype e.g. for public_vx.symtype */ #define SYMTYPE_NONE 0x0000 From 52873849a0f8c5f598ffee837c1689e31c793d31 Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Mon, 27 Jan 2025 17:25:35 +0100 Subject: [PATCH 210/454] include: Use flexible array-member in some structure declarations. Signed-off-by: Eric Pouech (cherry picked from commit 21b7e01bcca9eaf9199ecccfa331cba3c663770b) --- include/wine/mscvpdb.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/include/wine/mscvpdb.h b/include/wine/mscvpdb.h index a190d1cd4da6..9bb0fd8928e8 100644 --- a/include/wine/mscvpdb.h +++ b/include/wine/mscvpdb.h @@ -1969,7 +1969,7 @@ union codeview_symbol unsigned int offParent : 12; unsigned int padding : 20; struct cv_addr_range range; - struct cv_addr_gap gaps[0]; + struct cv_addr_gap gaps[]; } defrange_subfield_register_v3; struct @@ -1982,7 +1982,7 @@ union codeview_symbol unsigned short offsetParent : 12; int offBasePointer; struct cv_addr_range range; - struct cv_addr_gap gaps[0]; + struct cv_addr_gap gaps[]; } defrange_registerrel_v3; struct @@ -1999,7 +1999,7 @@ union codeview_symbol unsigned int pParent; unsigned int pEnd; cv_itemid_t inlinee; - unsigned char binaryAnnotations[0]; + unsigned char binaryAnnotations[]; } inline_site_v3; struct @@ -2020,7 +2020,7 @@ union codeview_symbol unsigned int pEnd; cv_itemid_t inlinee; unsigned int invocations; - unsigned char binaryAnnotations[0]; + unsigned char binaryAnnotations[]; } inline_site2_v3; struct @@ -2340,7 +2340,7 @@ struct CV_Checksum_t /* this one is not defined in microsoft pdb information */ unsigned strOffset; /* offset in string table for filename */ unsigned char size; /* size of checksum */ unsigned char method; /* method used to compute check sum */ - unsigned char checksum[0]; /* (size) bytes */ + unsigned char checksum[]; /* (size) bytes */ /* result is padded on 4-byte boundary */ }; @@ -2360,7 +2360,7 @@ typedef struct CV_InlineeSourceLineEx_t unsigned fileId; /* offset in DEBUG_S_FILECHKSMS */ unsigned sourceLineNum; /* first line number */ unsigned int countOfExtraFiles; - unsigned extraFileId[0]; + unsigned extraFileId[]; } InlineeSourceLineEx; #ifdef __WINESRC__ From 51086cd23806992d256d2c7e4a4755f22721dd24 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Wed, 26 Feb 2025 22:13:39 +0100 Subject: [PATCH 211/454] include: Fix some invalid array definitions in PDB structures. (cherry picked from commit 3310434ec67ecf65c49e735046f7b47a33b4d150) --- include/wine/mscvpdb.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/include/wine/mscvpdb.h b/include/wine/mscvpdb.h index 9bb0fd8928e8..d6551cc88317 100644 --- a/include/wine/mscvpdb.h +++ b/include/wine/mscvpdb.h @@ -2804,24 +2804,24 @@ typedef struct OMFSourceLine { unsigned short Seg; unsigned short cLnOff; - unsigned int offset[1]; - unsigned short lineNbr[1]; + unsigned int offset[]; +/* unsigned short lineNbr[]; */ } OMFSourceLine; typedef struct OMFSourceFile { unsigned short cSeg; unsigned short reserved; - unsigned int baseSrcLn[1]; - unsigned short cFName; - char Name; + unsigned int baseSrcLn[]; +/* unsigned short cFName; */ +/* char Name; */ } OMFSourceFile; typedef struct OMFSourceModule { unsigned short cFile; unsigned short cSeg; - unsigned int baseSrcFile[1]; + unsigned int baseSrcFile[]; } OMFSourceModule; From 297bd189b4e71ddb7563943439b33e686d5fe816 Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Thu, 13 Mar 2025 09:41:33 +0100 Subject: [PATCH 212/454] include: Remove flexible array member from PDB JG header. Signed-off-by: Eric Pouech (cherry picked from commit aceebea6c958e912c8d6fabf1aeca70f024e5568) --- dlls/dbghelp/msc.c | 4 ++-- include/wine/mscvpdb.h | 2 +- tools/winedump/pdb.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/dlls/dbghelp/msc.c b/dlls/dbghelp/msc.c index 22ec5230f7ae..9299aa27700f 100644 --- a/dlls/dbghelp/msc.c +++ b/dlls/dbghelp/msc.c @@ -3484,7 +3484,7 @@ static BOOL pdb_init(struct pdb_file_info* pdb_file, const char* image) struct PDB_JG_ROOT* root; struct PDB_JG_TOC* jg_toc; - jg_toc = pdb_jg_read(pdb, pdb->toc_block, pdb->toc.size); + jg_toc = pdb_jg_read(pdb, (unsigned short *)(pdb + 1), pdb->toc.size); if (!jg_toc) { ERR("-Unable to get TOC from .PDB\n"); @@ -3591,7 +3591,7 @@ DWORD pdb_get_file_indexinfo(void* image, DWORD size, SYMSRV_INDEX_INFOW* info) struct PDB_JG_ROOT* root; DWORD ec = ERROR_SUCCESS; - jg_toc = pdb_jg_read(pdb, pdb->toc_block, pdb->toc.size); + jg_toc = pdb_jg_read(pdb, (unsigned short*)(pdb + 1), pdb->toc.size); root = pdb_read_jg_stream(pdb, jg_toc, 1); if (!root) { diff --git a/include/wine/mscvpdb.h b/include/wine/mscvpdb.h index d6551cc88317..a21cc897059b 100644 --- a/include/wine/mscvpdb.h +++ b/include/wine/mscvpdb.h @@ -2409,7 +2409,7 @@ struct PDB_JG_HEADER unsigned short free_list_block; unsigned short total_alloc; struct PDB_JG_STREAM toc; - unsigned short toc_block[]; + /* unsigned short toc_block[]; */ }; struct PDB_DS_HEADER diff --git a/tools/winedump/pdb.c b/tools/winedump/pdb.c index dc59695a8262..06484a3dab77 100644 --- a/tools/winedump/pdb.c +++ b/tools/winedump/pdb.c @@ -110,7 +110,7 @@ static BOOL pdb_jg_init(struct pdb_reader* reader) if (!reader->u.jg.header) return FALSE; reader->read_stream = pdb_jg_read_stream; reader->u.jg.toc = pdb_jg_read(reader->u.jg.header, - reader->u.jg.header->toc_block, + (unsigned short *)(reader->u.jg.header + 1), reader->u.jg.header->toc.size); memset(reader->stream_used, 0, sizeof(reader->stream_used)); reader->u.jg.root = reader->read_stream(reader, 1); From 0b9bc7cfb3d8a4cbc3ce2424ba59920734d679f2 Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Tue, 14 Jan 2025 18:51:00 +0100 Subject: [PATCH 213/454] dbghelp: Fix potential crash for old debug formats. Signed-off-by: Eric Pouech (cherry picked from commit df340b2e6aba4400191845316882bce8c104e19d) --- dlls/dbghelp/msc.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/dlls/dbghelp/msc.c b/dlls/dbghelp/msc.c index 9299aa27700f..9ba0f3ab9e2c 100644 --- a/dlls/dbghelp/msc.c +++ b/dlls/dbghelp/msc.c @@ -4440,8 +4440,9 @@ static BOOL codeview_process_info(const struct process *pcs, if (ret) { msc_dbg->module->module.CVSig = *signature; - memcpy(msc_dbg->module->module.CVData, msc_dbg->root, - sizeof(msc_dbg->module->module.CVData)); + if (*signature == CODEVIEW_RSDS_SIG) + memcpy(msc_dbg->module->module.CVData, msc_dbg->root, + sizeof(msc_dbg->module->module.CVData)); } return ret; } From 1d5307457b14dfe00bbb173fc7ba743a53446e74 Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Sat, 30 Nov 2024 10:06:34 +0100 Subject: [PATCH 214/454] dbghelp: Support large PDB files (> 4G). Signed-off-by: Eric Pouech (cherry picked from commit 93e623209a701d716c773199aed06e82b6f1d7a0) --- dlls/dbghelp/msc.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dlls/dbghelp/msc.c b/dlls/dbghelp/msc.c index 9ba0f3ab9e2c..3f103930c8ae 100644 --- a/dlls/dbghelp/msc.c +++ b/dlls/dbghelp/msc.c @@ -3071,12 +3071,12 @@ static void* pdb_ds_read(const struct PDB_DS_HEADER* pdb, const UINT *block_list if (!size) return NULL; num_blocks = (size + pdb->block_size - 1) / pdb->block_size; - buffer = HeapAlloc(GetProcessHeap(), 0, num_blocks * pdb->block_size); + buffer = HeapAlloc(GetProcessHeap(), 0, (SIZE_T)num_blocks * pdb->block_size); if (!buffer) return NULL; for (i = 0; i < num_blocks; i++) memcpy(buffer + i * pdb->block_size, - (const char*)pdb + block_list[i] * pdb->block_size, pdb->block_size); + (const char*)pdb + (DWORD_PTR)block_list[i] * pdb->block_size, pdb->block_size); return buffer; } @@ -3521,7 +3521,7 @@ static BOOL pdb_init(struct pdb_file_info* pdb_file, const char* image) struct PDB_DS_ROOT* root; struct PDB_DS_TOC* ds_toc; - ds_toc = pdb_ds_read(pdb, (const UINT*)((const char*)pdb + pdb->toc_block * pdb->block_size), + ds_toc = pdb_ds_read(pdb, (const UINT*)((const char*)pdb + (DWORD_PTR)pdb->toc_block * pdb->block_size), pdb->toc_size); if (!ds_toc) { @@ -3635,7 +3635,7 @@ DWORD pdb_get_file_indexinfo(void* image, DWORD size, SYMSRV_INDEX_INFOW* info) struct PDB_DS_ROOT* root; DWORD ec = ERROR_SUCCESS; - ds_toc = pdb_ds_read(pdb, (const UINT*)((const char*)pdb + pdb->toc_block * pdb->block_size), + ds_toc = pdb_ds_read(pdb, (const UINT*)((const char*)pdb + (DWORD_PTR)pdb->toc_block * pdb->block_size), pdb->toc_size); root = pdb_read_ds_stream(pdb, ds_toc, 1); if (!root) From 3d3ade5d19f1faedbb7f719d9167472e80067373 Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Thu, 28 Nov 2024 08:59:31 +0100 Subject: [PATCH 215/454] dbghelp: Store pointer to context instead of context. Signed-off-by: Eric Pouech (cherry picked from commit 3721a90381d3d123588615f83f39ca525e36a092) --- dlls/dbghelp/dbghelp_private.h | 1 + dlls/dbghelp/dwarf.c | 20 ++++++++++++-------- dlls/dbghelp/storage.c | 9 +++++++++ 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/dlls/dbghelp/dbghelp_private.h b/dlls/dbghelp/dbghelp_private.h index 8def6a7dae6f..b618eee2501f 100644 --- a/dlls/dbghelp/dbghelp_private.h +++ b/dlls/dbghelp/dbghelp_private.h @@ -45,6 +45,7 @@ void* pool_alloc(struct pool* a, size_t len) __WINE_ALLOC_SIZE(2) __WINE_MALL void* pool_realloc(struct pool* a, void* ptr, size_t len) __WINE_ALLOC_SIZE(3); char* pool_strdup(struct pool* a, const char* str) __WINE_MALLOC; WCHAR* pool_wcsdup(struct pool* a, const WCHAR* str) __WINE_MALLOC; +void pool_free(struct pool* a, void* ptr); struct vector { diff --git a/dlls/dbghelp/dwarf.c b/dlls/dbghelp/dwarf.c index 3190dfecd0a3..638049f4368f 100644 --- a/dlls/dbghelp/dwarf.c +++ b/dlls/dbghelp/dwarf.c @@ -2858,7 +2858,7 @@ static dwarf2_parse_context_t* dwarf2_locate_cu(dwarf2_parse_module_context_t* m const BYTE* where; for (i = 0; i < module_ctx->unit_contexts.num_elts; ++i) { - ctx = vector_at(&module_ctx->unit_contexts, i); + ctx = *(dwarf2_parse_context_t**)vector_at(&module_ctx->unit_contexts, i); where = module_ctx->sections[ctx->section].address + ref; if (where >= ctx->traverse_DIE.data && where < ctx->traverse_DIE.end_data) return ctx; @@ -4114,7 +4114,7 @@ static BOOL dwarf2_load_CU_module(dwarf2_parse_module_context_t* module_ctx, str module_ctx->module = module; module_ctx->thunks = thunks; module_ctx->load_offset = load_offset; - vector_init(&module_ctx->unit_contexts, sizeof(dwarf2_parse_context_t), 16); + vector_init(&module_ctx->unit_contexts, sizeof(dwarf2_parse_context_t*), 16); module_ctx->cu_versions = 0; /* phase I: parse all CU heads */ @@ -4122,10 +4122,13 @@ static BOOL dwarf2_load_CU_module(dwarf2_parse_module_context_t* module_ctx, str mod_ctx.end_data = mod_ctx.data + sections[section_debug].size; while (mod_ctx.data < mod_ctx.end_data) { - dwarf2_parse_context_t* unit_ctx = vector_add(&module_ctx->unit_contexts, &module_ctx->module->pool); + dwarf2_parse_context_t **punit_ctx = vector_add(&module_ctx->unit_contexts, &module_ctx->module->pool); - unit_ctx->module_ctx = module_ctx; - dwarf2_parse_compilation_unit_head(unit_ctx, &mod_ctx); + if (!(*punit_ctx = pool_alloc(&module_ctx->module->pool, sizeof(dwarf2_parse_context_t)))) + return FALSE; + + (*punit_ctx)->module_ctx = module_ctx; + dwarf2_parse_compilation_unit_head(*punit_ctx, &mod_ctx); } /* phase2: load content of all CU @@ -4135,7 +4138,7 @@ static BOOL dwarf2_load_CU_module(dwarf2_parse_module_context_t* module_ctx, str */ if (!is_dwz) for (i = 0; i < module_ctx->unit_contexts.num_elts; ++i) - dwarf2_parse_compilation_unit((dwarf2_parse_context_t*)vector_at(&module_ctx->unit_contexts, i)); + dwarf2_parse_compilation_unit(*(dwarf2_parse_context_t**)vector_at(&module_ctx->unit_contexts, i)); return TRUE; } @@ -4189,9 +4192,10 @@ static BOOL dwarf2_unload_CU_module(dwarf2_parse_module_context_t* module_ctx) unsigned i; for (i = 0; i < module_ctx->unit_contexts.num_elts; ++i) { - dwarf2_parse_context_t* unit = vector_at(&module_ctx->unit_contexts, i); - if (unit->status != UNIT_ERROR) + dwarf2_parse_context_t* unit = *(dwarf2_parse_context_t**)vector_at(&module_ctx->unit_contexts, i); + if (unit && unit->status != UNIT_ERROR) pool_destroy(&unit->pool); + pool_free(&module_ctx->module->pool, unit); } dwarf2_unload_dwz(module_ctx->dwz); return TRUE; diff --git a/dlls/dbghelp/storage.c b/dlls/dbghelp/storage.c index f7c08adab8a9..2344f0b848c2 100644 --- a/dlls/dbghelp/storage.c +++ b/dlls/dbghelp/storage.c @@ -48,6 +48,15 @@ void* pool_realloc(struct pool* pool, void* ptr, size_t len) return ptr ? HeapReAlloc(pool->heap, 0, ptr, len) : pool_alloc(pool, len); } +void pool_free(struct pool* pool, void* ptr) +{ +#ifdef USE_STATS + if (ptr) + mem_stats_down(&pool->stats, HeapSize(pool->heap, 0, ptr)); +#endif + HeapFree(pool->heap, 0, ptr); +} + char* pool_strdup(struct pool* pool, const char* str) { char* ret; From dafc659e3ba2d59c9832f870b76e433e780bf253 Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Thu, 28 Nov 2024 08:58:58 +0100 Subject: [PATCH 216/454] dbghelp: Optimize vector allocation. Introduce a new model for vector allocation: - No longer requiring initial size (using a pure quadratic growth), - No longer providing stability of element address across add operations. This allows some reduction in memory usage. Signed-off-by: Eric Pouech (cherry picked from commit 5c54087c4748324b1726fb8545dd90b3d80bc698) --- dlls/dbghelp/dwarf.c | 8 ++++---- dlls/dbghelp/module.c | 6 +++--- dlls/dbghelp/msc.c | 2 +- dlls/dbghelp/storage.c | 42 ++++++++++++++++++++++++++++++++++++------ dlls/dbghelp/symbol.c | 10 +++++----- dlls/dbghelp/type.c | 6 +++--- 6 files changed, 52 insertions(+), 22 deletions(-) diff --git a/dlls/dbghelp/dwarf.c b/dlls/dbghelp/dwarf.c index 638049f4368f..7d3abee5924b 100644 --- a/dlls/dbghelp/dwarf.c +++ b/dlls/dbghelp/dwarf.c @@ -1369,7 +1369,7 @@ static BOOL dwarf2_read_one_debug_info(dwarf2_parse_context_t* ctx, else di->data = NULL; if (abbrev->have_child) { - vector_init(&di->children, sizeof(dwarf2_debug_info_t*), 16); + vector_init(&di->children, sizeof(dwarf2_debug_info_t*), 0); while (traverse->data < traverse->end_data) { if (!dwarf2_read_one_debug_info(ctx, traverse, di, &child)) return FALSE; @@ -2685,7 +2685,7 @@ static BOOL dwarf2_parse_line_numbers(dwarf2_parse_context_t* ctx, opcode_len = traverse.data; traverse.data += opcode_base - 1; - vector_init(&dirs, sizeof(const char*), 4); + vector_init(&dirs, sizeof(const char*), 0); p = vector_add(&dirs, &ctx->pool); *p = compile_dir ? compile_dir : "."; while (traverse.data < traverse.end_data && *traverse.data) @@ -2712,7 +2712,7 @@ static BOOL dwarf2_parse_line_numbers(dwarf2_parse_context_t* ctx, } traverse.data++; - vector_init(&files, sizeof(unsigned), 16); + vector_init(&files, sizeof(unsigned), 0); while (traverse.data < traverse.end_data && *traverse.data) { unsigned int dir_index, mod_time; @@ -4114,7 +4114,7 @@ static BOOL dwarf2_load_CU_module(dwarf2_parse_module_context_t* module_ctx, str module_ctx->module = module; module_ctx->thunks = thunks; module_ctx->load_offset = load_offset; - vector_init(&module_ctx->unit_contexts, sizeof(dwarf2_parse_context_t*), 16); + vector_init(&module_ctx->unit_contexts, sizeof(dwarf2_parse_context_t*), 0); module_ctx->cu_versions = 0; /* phase I: parse all CU heads */ diff --git a/dlls/dbghelp/module.c b/dlls/dbghelp/module.c index b8848f4010a7..6cee537a610c 100644 --- a/dlls/dbghelp/module.c +++ b/dlls/dbghelp/module.c @@ -253,14 +253,14 @@ struct module* module_new(struct process* pcs, const WCHAR* name, module->cpu = dbghelp_current_cpu; module->debug_format_bitmask = 0; - vector_init(&module->vsymt, sizeof(struct symt*), 128); - vector_init(&module->vcustom_symt, sizeof(struct symt*), 16); + vector_init(&module->vsymt, sizeof(struct symt*), 0); + vector_init(&module->vcustom_symt, sizeof(struct symt*), 0); /* FIXME: this seems a bit too high (on a per module basis) * need some statistics about this */ hash_table_init(&module->pool, &module->ht_symbols, 4096); hash_table_init(&module->pool, &module->ht_types, 4096); - vector_init(&module->vtypes, sizeof(struct symt*), 32); + vector_init(&module->vtypes, sizeof(struct symt*), 0); module->sources_used = 0; module->sources_alloc = 0; diff --git a/dlls/dbghelp/msc.c b/dlls/dbghelp/msc.c index 3f103930c8ae..510252428791 100644 --- a/dlls/dbghelp/msc.c +++ b/dlls/dbghelp/msc.c @@ -4187,7 +4187,7 @@ static void pev_init(struct pevaluator* pev, struct cpu_stack_walk* csw, { pev->csw = csw; pool_init(&pev->pool, 512); - vector_init(&pev->stack, sizeof(char*), 8); + vector_init(&pev->stack, sizeof(char*), 0); pev->stk_index = 0; hash_table_init(&pev->pool, &pev->values, 8); pev->error[0] = '\0'; diff --git a/dlls/dbghelp/storage.c b/dlls/dbghelp/storage.c index 2344f0b848c2..0589ed699eeb 100644 --- a/dlls/dbghelp/storage.c +++ b/dlls/dbghelp/storage.c @@ -74,10 +74,11 @@ WCHAR* pool_wcsdup(struct pool* pool, const WCHAR* str) void vector_init(struct vector* v, unsigned esz, unsigned bucket_sz) { v->buckets = NULL; - /* align size on DWORD boundaries */ - v->elt_size = (esz + 3) & ~3; + /* align size */ + v->elt_size = (esz + sizeof(void*) - 1) & ~(sizeof(void*) - 1); switch (bucket_sz) { + case 0: v->shift = 0; break; /* special case see below */ case 2: v->shift = 1; break; case 4: v->shift = 2; break; case 8: v->shift = 3; break; @@ -102,15 +103,44 @@ unsigned vector_length(const struct vector* v) void* vector_at(const struct vector* v, unsigned pos) { - unsigned o; - if (pos >= v->num_elts) return NULL; - o = pos & ((1 << v->shift) - 1); - return (char*)v->buckets[pos >> v->shift] + o * v->elt_size; + if (v->shift) + { + unsigned o = pos & ((1 << v->shift) - 1); + return (char*)v->buckets[pos >> v->shift] + o * v->elt_size; + } + else + { + return (char*)v->buckets + pos * v->elt_size; + } } void* vector_add(struct vector* v, struct pool* pool) { + if (!v->shift) + { + if (v->num_elts == 1024) + { + /* we'll need a second bucket, so go directly for it */ + void **new = pool_alloc(pool, 2 * sizeof(void*)); + if (!new) return NULL; + *new = v->buckets; + v->buckets = new; + v->num_buckets = 1; + v->buckets_allocated = 2; + v->shift = 10; + } + else + { + if (!v->num_elts || !(v->num_elts & (v->num_elts - 1))) + { + void *new = pool_realloc(pool, v->buckets, (v->num_elts ? v->num_elts * 2 : 1) * v->elt_size); + if (!new) return NULL; + v->buckets = new; + } + return vector_at(v, v->num_elts++); + } + } if (v->num_elts == (v->num_buckets << v->shift)) { if (v->num_buckets == v->buckets_allocated) diff --git a/dlls/dbghelp/symbol.c b/dlls/dbghelp/symbol.c index 38382b006d73..7c8acf8e68cd 100644 --- a/dlls/dbghelp/symbol.c +++ b/dlls/dbghelp/symbol.c @@ -226,7 +226,7 @@ struct symt_module* symt_new_module(struct module* module) { sym->symt.tag = SymTagExe; sym->module = module; - vector_init(&sym->vchildren, sizeof(struct symt*), 8); + vector_init(&sym->vchildren, sizeof(struct symt*), 0); } return sym; } @@ -244,7 +244,7 @@ struct symt_compiland* symt_new_compiland(struct module* module, unsigned src_id sym->container = module->top; sym->address = 0; sym->source = src_idx; - vector_init(&sym->vchildren, sizeof(struct symt*), 32); + vector_init(&sym->vchildren, sizeof(struct symt*), 0); sym->user = NULL; p = vector_add(&module->top->vchildren, &module->pool); *p = sym; @@ -333,8 +333,8 @@ static struct symt_function* init_function_or_inlinesite(struct module* module, sym->hash_elt.name = pool_strdup(&module->pool, name); sym->container = container; sym->type = sig_type; - vector_init(&sym->vlines, sizeof(struct line_info), tag == SymTagFunction ? 8 : 4); - vector_init(&sym->vchildren, sizeof(struct symt*), 8); + vector_init(&sym->vlines, sizeof(struct line_info), 0); + vector_init(&sym->vchildren, sizeof(struct symt*), 0); sym->num_ranges = num_ranges; } return sym; @@ -548,7 +548,7 @@ struct symt_block* symt_open_func_block(struct module* module, block->symt.tag = SymTagBlock; block->num_ranges = num_ranges; block->container = parent_block ? &parent_block->symt : &func->symt; - vector_init(&block->vchildren, sizeof(struct symt*), 4); + vector_init(&block->vchildren, sizeof(struct symt*), 0); if (parent_block) p = vector_add(&parent_block->vchildren, &module->pool); else diff --git a/dlls/dbghelp/type.c b/dlls/dbghelp/type.c index b89c63c5b3d6..9a1482e73abd 100644 --- a/dlls/dbghelp/type.c +++ b/dlls/dbghelp/type.c @@ -260,7 +260,7 @@ struct symt_udt* symt_new_udt(struct module* module, const char* typename, sym->hash_elt.name = pool_strdup(&module->pool, typename); hash_table_add(&module->ht_types, &sym->hash_elt); } else sym->hash_elt.name = NULL; - vector_init(&sym->vchildren, sizeof(struct symt*), 8); + vector_init(&sym->vchildren, sizeof(struct symt*), 0); symt_add_type(module, &sym->symt); } return sym; @@ -345,7 +345,7 @@ struct symt_enum* symt_new_enum(struct module* module, const char* typename, hash_table_add(&module->ht_types, &sym->hash_elt); } else sym->hash_elt.name = NULL; sym->base_type = basetype; - vector_init(&sym->vchildren, sizeof(struct symt*), 8); + vector_init(&sym->vchildren, sizeof(struct symt*), 0); symt_add_type(module, &sym->symt); } return sym; @@ -404,7 +404,7 @@ struct symt_function_signature* symt_new_function_signature(struct module* modul { sym->symt.tag = SymTagFunctionType; sym->rettype = ret_type; - vector_init(&sym->vchildren, sizeof(struct symt*), 4); + vector_init(&sym->vchildren, sizeof(struct symt*), 0); sym->call_conv = call_conv; symt_add_type(module, &sym->symt); } From caa952b05e38afce7e573964c942fa361f14b70c Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Fri, 8 Nov 2024 16:59:58 +0100 Subject: [PATCH 217/454] dbghelp: Simplify get_line_from_addr(). We can't (no longer) get inlined functions here. Signed-off-by: Eric Pouech (cherry picked from commit c36cbd9da96f9dc1f9acef9222f3fa4f93b1db3f) --- dlls/dbghelp/symbol.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dlls/dbghelp/symbol.c b/dlls/dbghelp/symbol.c index 7c8acf8e68cd..961f77094b53 100644 --- a/dlls/dbghelp/symbol.c +++ b/dlls/dbghelp/symbol.c @@ -1910,9 +1910,9 @@ static BOOL get_line_from_addr(HANDLE hProcess, DWORD64 addr, struct symt_ht* symt; if (!module_init_pair(&pair, hProcess, addr)) return FALSE; - if ((symt = symt_find_symbol_at(pair.effective, addr)) == NULL) return FALSE; + symt = symt_find_symbol_at(pair.effective, addr); - if (symt->symt.tag != SymTagFunction && symt->symt.tag != SymTagInlineSite) return FALSE; + if (!symt_check_tag(&symt->symt, SymTagFunction)) return FALSE; return get_line_from_function(&pair, (struct symt_function*)symt, addr, pdwDisplacement, intl); } From 080d1afc3329ab4e9d62bc58d55c27b109d28690 Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Thu, 28 Nov 2024 12:07:29 +0100 Subject: [PATCH 218/454] dbghelp: Support module lookup in SymEnumSourceFiles. Signed-off-by: Eric Pouech (cherry picked from commit 72a322bbc6000e64cde70fbccdb27401112d3bd6) --- dlls/dbghelp/source.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/dlls/dbghelp/source.c b/dlls/dbghelp/source.c index f2dc84d47695..2ef80deb1dc5 100644 --- a/dlls/dbghelp/source.c +++ b/dlls/dbghelp/source.c @@ -157,10 +157,18 @@ BOOL WINAPI SymEnumSourceFilesW(HANDLE hProcess, ULONG64 ModBase, PCWSTR Mask, } else { - if (Mask[0] == '!') + WCHAR *bang = wcschr(Mask, '!'); + if (bang) { - pair.requested = module_find_by_nameW(pair.pcs, Mask + 1); + WCHAR *module_name; + + if (!(module_name = HeapAlloc(GetProcessHeap(), 0, (bang - Mask + 1) * sizeof(WCHAR)))) return FALSE; + memcpy(module_name, Mask, (bang - Mask) * sizeof(WCHAR)); + module_name[bang - Mask] = L'\0'; + pair.requested = module_find_by_nameW(pair.pcs, module_name); + HeapFree(GetProcessHeap(), 0, module_name); if (!module_get_debug(&pair)) return FALSE; + Mask = bang + 1; } else { @@ -183,10 +191,12 @@ BOOL WINAPI SymEnumSourceFilesW(HANDLE hProcess, ULONG64 ModBase, PCWSTR Mask, MultiByteToWideChar(CP_ACP, 0, ptr, -1, conversion_buffer, len); - /* FIXME: not using Mask */ - sf.ModBase = ModBase; - sf.FileName = conversion_buffer; - if (!cbSrcFiles(&sf, UserContext)) break; + if (!Mask || !*Mask || SymMatchStringW(conversion_buffer, Mask, FALSE)) + { + sf.ModBase = pair.requested->module.BaseOfImage; + sf.FileName = conversion_buffer; + if (!cbSrcFiles(&sf, UserContext)) break; + } } HeapFree(GetProcessHeap(), 0, conversion_buffer); From eed0332a7bacccbee4a7c5e2011a809092ef49db Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Sun, 24 Nov 2024 09:39:24 +0100 Subject: [PATCH 219/454] dbghelp: Factorize some code between type enumeration APIs. Signed-off-by: Eric Pouech (cherry picked from commit e0e93303bcbaea77cef486dd3b8af13f60afef51) --- dlls/dbghelp/type.c | 94 +++++++++++++++++---------------------------- 1 file changed, 35 insertions(+), 59 deletions(-) diff --git a/dlls/dbghelp/type.c b/dlls/dbghelp/type.c index 9a1482e73abd..9c85a7a613a0 100644 --- a/dlls/dbghelp/type.c +++ b/dlls/dbghelp/type.c @@ -460,36 +460,29 @@ struct symt_typedef* symt_new_typedef(struct module* module, struct symt* ref, return sym; } -/****************************************************************** - * SymEnumTypes (DBGHELP.@) - * - */ -BOOL WINAPI SymEnumTypes(HANDLE hProcess, ULONG64 BaseOfDll, - PSYM_ENUMERATESYMBOLS_CALLBACK EnumSymbolsCallback, - PVOID UserContext) +static BOOL sym_enum_types(struct module_pair *pair, const char *type_name, PSYM_ENUMERATESYMBOLS_CALLBACK cb, void *user) { - struct module_pair pair; char buffer[sizeof(SYMBOL_INFO) + 256]; - SYMBOL_INFO* sym_info = (SYMBOL_INFO*)buffer; - struct symt* type; + struct symt *type; + SYMBOL_INFO *sym_info = (SYMBOL_INFO*)buffer; DWORD64 size; unsigned int i; - - TRACE("(%p %I64x %p %p)\n", hProcess, BaseOfDll, EnumSymbolsCallback, UserContext); - - if (!module_init_pair(&pair, hProcess, BaseOfDll)) return FALSE; + const char *tname; sym_info->SizeOfStruct = sizeof(SYMBOL_INFO); sym_info->MaxNameLen = sizeof(buffer) - sizeof(SYMBOL_INFO); - for (i=0; ivtypes); i++) + for (i = 0; i < vector_length(&pair->effective->vtypes); i++) { - type = *(struct symt**)vector_at(&pair.effective->vtypes, i); - sym_info->TypeIndex = symt_ptr2index(pair.effective, type); + type = *(struct symt**)vector_at(&pair->effective->vtypes, i); + tname = symt_get_name(type); + if (!tname || !*tname) continue; + if (type_name && !SymMatchStringA(tname, type_name, TRUE)) continue; + sym_info->TypeIndex = symt_ptr2index(pair->effective, type); sym_info->Index = 0; /* FIXME */ - symt_get_info(pair.effective, type, TI_GET_LENGTH, &size); + symt_get_info(pair->effective, type, TI_GET_LENGTH, &size); sym_info->Size = size; - sym_info->ModBase = pair.requested->module.BaseOfImage; + sym_info->ModBase = pair->requested->module.BaseOfImage; sym_info->Flags = 0; /* FIXME */ sym_info->Value = 0; /* FIXME */ sym_info->Address = 0; /* FIXME */ @@ -497,11 +490,29 @@ BOOL WINAPI SymEnumTypes(HANDLE hProcess, ULONG64 BaseOfDll, sym_info->Scope = 0; /* FIXME */ sym_info->Tag = type->tag; symbol_setname(sym_info, symt_get_name(type)); - if (!EnumSymbolsCallback(sym_info, sym_info->Size, UserContext)) break; + if (!cb(sym_info, sym_info->Size, user)) return FALSE; } return TRUE; } +/****************************************************************** + * SymEnumTypes (DBGHELP.@) + * + */ +BOOL WINAPI SymEnumTypes(HANDLE hProcess, ULONG64 BaseOfDll, + PSYM_ENUMERATESYMBOLS_CALLBACK EnumSymbolsCallback, + PVOID UserContext) +{ + struct module_pair pair; + + TRACE("(%p %I64x %p %p)\n", hProcess, BaseOfDll, EnumSymbolsCallback, UserContext); + + if (!module_init_pair(&pair, hProcess, BaseOfDll)) return FALSE; + + sym_enum_types(&pair, NULL, EnumSymbolsCallback, UserContext); + return TRUE; +} + struct enum_types_AtoW { char buffer[sizeof(SYMBOL_INFOW) + 256 * sizeof(WCHAR)]; @@ -534,41 +545,6 @@ BOOL WINAPI SymEnumTypesW(HANDLE hProcess, ULONG64 BaseOfDll, return SymEnumTypes(hProcess, BaseOfDll, enum_types_AtoW, &et); } -static void enum_types_of_module(struct module_pair* pair, const char* name, PSYM_ENUMERATESYMBOLS_CALLBACK cb, PVOID user) -{ - char buffer[sizeof(SYMBOL_INFO) + 256]; - SYMBOL_INFO* sym_info = (SYMBOL_INFO*)buffer; - struct symt* type; - DWORD64 size; - unsigned i; - const char* tname; - - sym_info->SizeOfStruct = sizeof(SYMBOL_INFO); - sym_info->MaxNameLen = sizeof(buffer) - sizeof(SYMBOL_INFO); - - for (i = 0; i < vector_length(&pair->effective->vtypes); i++) - { - type = *(struct symt**)vector_at(&pair->effective->vtypes, i); - tname = symt_get_name(type); - if (tname && SymMatchStringA(tname, name, TRUE)) - { - sym_info->TypeIndex = symt_ptr2index(pair->effective, type); - sym_info->Index = 0; /* FIXME */ - symt_get_info(pair->effective, type, TI_GET_LENGTH, &size); - sym_info->Size = size; - sym_info->ModBase = pair->requested->module.BaseOfImage; - sym_info->Flags = 0; /* FIXME */ - sym_info->Value = 0; /* FIXME */ - sym_info->Address = 0; /* FIXME */ - sym_info->Register = 0; /* FIXME */ - sym_info->Scope = 0; /* FIXME */ - sym_info->Tag = type->tag; - symbol_setname(sym_info, tname); - if (!cb(sym_info, sym_info->Size, user)) break; - } - } -} - static BOOL walk_modules(struct module_pair* pair) { /* first walk PE only modules */ @@ -597,8 +573,7 @@ BOOL WINAPI SymEnumTypesByName(HANDLE proc, ULONG64 base, PCSTR name, PSYM_ENUME TRACE("(%p %I64x %s %p %p)\n", proc, base, debugstr_a(name), cb, user); - if (!name) return SymEnumTypes(proc, base, cb, user); - bang = strchr(name, '!'); + bang = name ? strchr(name, '!') : NULL; if (bang) { DWORD sz; @@ -614,14 +589,15 @@ BOOL WINAPI SymEnumTypesByName(HANDLE proc, ULONG64 base, PCSTR name, PSYM_ENUME while (walk_modules(&pair)) { if (SymMatchStringW(pair.requested->modulename, modW, FALSE)) - enum_types_of_module(&pair, bang + 1, cb, user); + if (!sym_enum_types(&pair, bang + 1, cb, user)) + break; } free(modW); } else { if (!module_init_pair(&pair, proc, base) || !module_get_debug(&pair)) return FALSE; - enum_types_of_module(&pair, name, cb, user); + sym_enum_types(&pair, name, cb, user); } return TRUE; } From fdbc2c08525578d6e7a0361a0ca4c479a6d40e70 Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Sun, 24 Nov 2024 11:23:31 +0100 Subject: [PATCH 220/454] dbghelp: Only store types with names in module. Enumeration only report types with name, so we can get rid of module's vtype vector and only the use the types' hash table. Signed-off-by: Eric Pouech (cherry picked from commit f56985ca96ec9ddf1909554bd451202e5553b660) --- dlls/dbghelp/dbghelp_private.h | 1 - dlls/dbghelp/module.c | 2 -- dlls/dbghelp/type.c | 45 ++++++++++++---------------------- 3 files changed, 16 insertions(+), 32 deletions(-) diff --git a/dlls/dbghelp/dbghelp_private.h b/dlls/dbghelp/dbghelp_private.h index b618eee2501f..334f1d5bf833 100644 --- a/dlls/dbghelp/dbghelp_private.h +++ b/dlls/dbghelp/dbghelp_private.h @@ -470,7 +470,6 @@ struct module /* types */ struct hash_table ht_types; - struct vector vtypes; /* source files */ unsigned sources_used; diff --git a/dlls/dbghelp/module.c b/dlls/dbghelp/module.c index 6cee537a610c..8960973c0660 100644 --- a/dlls/dbghelp/module.c +++ b/dlls/dbghelp/module.c @@ -260,7 +260,6 @@ struct module* module_new(struct process* pcs, const WCHAR* name, */ hash_table_init(&module->pool, &module->ht_symbols, 4096); hash_table_init(&module->pool, &module->ht_types, 4096); - vector_init(&module->vtypes, sizeof(struct symt*), 0); module->sources_used = 0; module->sources_alloc = 0; @@ -1622,7 +1621,6 @@ void module_reset_debug_info(struct module* module) hash_table_destroy(&module->ht_types); module->ht_types.num_buckets = 0; module->ht_types.buckets = NULL; - module->vtypes.num_elts = 0; hash_table_destroy(&module->ht_symbols); module->sources_used = module->sources_alloc = 0; module->sources = NULL; diff --git a/dlls/dbghelp/type.c b/dlls/dbghelp/type.c index 9c85a7a613a0..7e9bb44e6903 100644 --- a/dlls/dbghelp/type.c +++ b/dlls/dbghelp/type.c @@ -213,14 +213,6 @@ static struct symt* symt_find_type_by_name(const struct module* module, return NULL; } -static void symt_add_type(struct module* module, struct symt* symt) -{ - struct symt** p; - p = vector_add(&module->vtypes, &module->pool); - assert(p); - *p = symt; -} - struct symt_basic* symt_get_basic(enum BasicType bt, unsigned size) { static struct symt_basic cache[32] = { { {SymTagBaseType}, btNoType, 0 } }; @@ -261,7 +253,6 @@ struct symt_udt* symt_new_udt(struct module* module, const char* typename, hash_table_add(&module->ht_types, &sym->hash_elt); } else sym->hash_elt.name = NULL; vector_init(&sym->vchildren, sizeof(struct symt*), 0); - symt_add_type(module, &sym->symt); } return sym; } @@ -346,7 +337,6 @@ struct symt_enum* symt_new_enum(struct module* module, const char* typename, } else sym->hash_elt.name = NULL; sym->base_type = basetype; vector_init(&sym->vchildren, sizeof(struct symt*), 0); - symt_add_type(module, &sym->symt); } return sym; } @@ -389,7 +379,6 @@ struct symt_array* symt_new_array(struct module* module, int min, DWORD cnt, sym->count = cnt; sym->base_type = base; sym->index_type = index; - symt_add_type(module, &sym->symt); } return sym; } @@ -406,7 +395,6 @@ struct symt_function_signature* symt_new_function_signature(struct module* modul sym->rettype = ret_type; vector_init(&sym->vchildren, sizeof(struct symt*), 0); sym->call_conv = call_conv; - symt_add_type(module, &sym->symt); } return sym; } @@ -439,13 +427,12 @@ struct symt_pointer* symt_new_pointer(struct module* module, struct symt* ref_ty sym->symt.tag = SymTagPointerType; sym->pointsto = ref_type; sym->size = size; - symt_add_type(module, &sym->symt); } return sym; } -struct symt_typedef* symt_new_typedef(struct module* module, struct symt* ref, - const char* name) +struct symt_typedef* symt_new_typedef(struct module* module, struct symt* ref, + const char* typename) { struct symt_typedef* sym; @@ -453,9 +440,8 @@ struct symt_typedef* symt_new_typedef(struct module* module, struct symt* ref, { sym->symt.tag = SymTagTypedef; sym->type = ref; - sym->hash_elt.name = pool_strdup(&module->pool, name); + sym->hash_elt.name = pool_strdup(&module->pool, typename); hash_table_add(&module->ht_types, &sym->hash_elt); - symt_add_type(module, &sym->symt); } return sym; } @@ -463,24 +449,25 @@ struct symt_typedef* symt_new_typedef(struct module* module, struct symt* ref, static BOOL sym_enum_types(struct module_pair *pair, const char *type_name, PSYM_ENUMERATESYMBOLS_CALLBACK cb, void *user) { char buffer[sizeof(SYMBOL_INFO) + 256]; - struct symt *type; SYMBOL_INFO *sym_info = (SYMBOL_INFO*)buffer; + struct hash_table_iter hti; + void* ptr; + struct symt_ht *type; DWORD64 size; - unsigned int i; - const char *tname; sym_info->SizeOfStruct = sizeof(SYMBOL_INFO); sym_info->MaxNameLen = sizeof(buffer) - sizeof(SYMBOL_INFO); - for (i = 0; i < vector_length(&pair->effective->vtypes); i++) + hash_table_iter_init(&pair->effective->ht_types, &hti, type_name); + while ((ptr = hash_table_iter_up(&hti))) { - type = *(struct symt**)vector_at(&pair->effective->vtypes, i); - tname = symt_get_name(type); - if (!tname || !*tname) continue; - if (type_name && !SymMatchStringA(tname, type_name, TRUE)) continue; - sym_info->TypeIndex = symt_ptr2index(pair->effective, type); + type = CONTAINING_RECORD(ptr, struct symt_ht, hash_elt); + + if (type_name && !SymMatchStringA(type->hash_elt.name, type_name, TRUE)) continue; + + sym_info->TypeIndex = symt_ptr2index(pair->effective, &type->symt); sym_info->Index = 0; /* FIXME */ - symt_get_info(pair->effective, type, TI_GET_LENGTH, &size); + symt_get_info(pair->effective, &type->symt, TI_GET_LENGTH, &size); sym_info->Size = size; sym_info->ModBase = pair->requested->module.BaseOfImage; sym_info->Flags = 0; /* FIXME */ @@ -488,8 +475,8 @@ static BOOL sym_enum_types(struct module_pair *pair, const char *type_name, PSYM sym_info->Address = 0; /* FIXME */ sym_info->Register = 0; /* FIXME */ sym_info->Scope = 0; /* FIXME */ - sym_info->Tag = type->tag; - symbol_setname(sym_info, symt_get_name(type)); + sym_info->Tag = type->symt.tag; + symbol_setname(sym_info, type->hash_elt.name); if (!cb(sym_info, sym_info->Size, user)) return FALSE; } return TRUE; From fb3392f62ab5168dd2d8ee3579d4d2ae730b89a5 Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Tue, 10 Dec 2024 11:36:06 +0100 Subject: [PATCH 221/454] dbghelp: Factorize function signature creation {dwarf}. Signed-off-by: Eric Pouech (cherry picked from commit 1b8161fdbe8cee29deed278fc1bd09e39d5f8b12) --- dlls/dbghelp/dwarf.c | 31 ++++++++----------------------- 1 file changed, 8 insertions(+), 23 deletions(-) diff --git a/dlls/dbghelp/dwarf.c b/dlls/dbghelp/dwarf.c index 7d3abee5924b..5442e33be713 100644 --- a/dlls/dbghelp/dwarf.c +++ b/dlls/dbghelp/dwarf.c @@ -2076,11 +2076,6 @@ static void dwarf2_parse_variable(dwarf2_subprogram_t* subpgm, WARN("dropping global variable %s which has been optimized away\n", debugstr_a(name.u.string)); } } - if (is_pmt && subpgm->current_func && symt_check_tag(subpgm->current_func->type, SymTagFunctionType)) - symt_add_function_signature_parameter(subpgm->ctx->module_ctx->module, - (struct symt_function_signature*)subpgm->current_func->type, - param_type); - if (dwarf2_get_di_children(di)) FIXME("Unsupported children\n"); } @@ -2116,8 +2111,6 @@ static void dwarf2_parse_inlined_subroutine(dwarf2_subprogram_t* subpgm, dwarf2_debug_info_t* di) { struct attribute name; - struct symt* ret_type; - struct symt_function_signature* sig_type; struct symt_function* inlined; struct vector* children; dwarf2_debug_info_t*child; @@ -2136,16 +2129,12 @@ static void dwarf2_parse_inlined_subroutine(dwarf2_subprogram_t* subpgm, FIXME("No name for function... dropping function\n"); return; } - ret_type = dwarf2_lookup_type(di); - - /* FIXME: assuming C source code */ - sig_type = symt_new_function_signature(subpgm->ctx->module_ctx->module, ret_type, CV_CALL_FAR_C); inlined = symt_new_inlinesite(subpgm->ctx->module_ctx->module, subpgm->top_func, subpgm->current_block ? &subpgm->current_block->symt : &subpgm->current_func->symt, dwarf2_get_cpp_name(di, name.u.string), - &sig_type->symt, num_ranges); + dwarf2_parse_subroutine_type(di), num_ranges); subpgm->current_func = inlined; subpgm->current_block = NULL; @@ -2254,7 +2243,8 @@ static void dwarf2_parse_subprogram_block(dwarf2_subprogram_t* subpgm, dwarf2_parse_pointer_type(child); break; case DW_TAG_subroutine_type: - dwarf2_parse_subroutine_type(child); + if (!child->symt) + child->symt = dwarf2_parse_subroutine_type(child); break; case DW_TAG_const_type: dwarf2_parse_const_type(child); @@ -2304,8 +2294,6 @@ static struct symt* dwarf2_parse_subprogram(dwarf2_debug_info_t* di) unsigned num_addr_ranges; struct attribute is_decl; struct attribute inline_flags; - struct symt* ret_type; - struct symt_function_signature* sig_type; dwarf2_subprogram_t subpgm; struct vector* children; dwarf2_debug_info_t* child; @@ -2352,13 +2340,11 @@ static struct symt* dwarf2_parse_subprogram(dwarf2_debug_info_t* di) free(addr_ranges); return NULL; } - ret_type = dwarf2_lookup_type(di); - /* FIXME: assuming C source code */ - sig_type = symt_new_function_signature(di->unit_ctx->module_ctx->module, ret_type, CV_CALL_FAR_C); subpgm.top_func = symt_new_function(di->unit_ctx->module_ctx->module, di->unit_ctx->compiland, dwarf2_get_cpp_name(di, name.u.string), - addr_ranges[0].low, addr_ranges[0].high - addr_ranges[0].low, &sig_type->symt); + addr_ranges[0].low, addr_ranges[0].high - addr_ranges[0].low, + dwarf2_parse_subroutine_type(di)); if (num_addr_ranges > 1) WARN("Function %s has multiple address ranges, only using the first one\n", debugstr_a(name.u.string)); free(addr_ranges); @@ -2446,8 +2432,6 @@ static struct symt* dwarf2_parse_subroutine_type(dwarf2_debug_info_t* di) dwarf2_debug_info_t* child; unsigned int i; - if (di->symt) return di->symt; - TRACE("%s\n", dwarf2_debug_di(di)); ret_type = dwarf2_lookup_type(di); @@ -2472,7 +2456,7 @@ static struct symt* dwarf2_parse_subroutine_type(dwarf2_debug_info_t* di) } } - return di->symt = &sig_type->symt; + return &sig_type->symt; } static void dwarf2_parse_namespace(dwarf2_debug_info_t* di) @@ -2561,7 +2545,8 @@ static void dwarf2_load_one_entry(dwarf2_debug_info_t* di) dwarf2_parse_subprogram(di); break; case DW_TAG_subroutine_type: - dwarf2_parse_subroutine_type(di); + if (!di->symt) + di->symt = dwarf2_parse_subroutine_type(di); break; case DW_TAG_variable: { From 7cab397be660acfc86176e2b6e3d4cb78e89db2c Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Fri, 28 Feb 2025 12:16:22 +0100 Subject: [PATCH 222/454] dbghelp: Introduce helper to match an ANSI string against a Unicode regex. Signed-off-by: Eric Pouech (cherry picked from commit 6298b0cab2086ae61f46b284d22c420dfbb2b44e) --- dlls/dbghelp/dbghelp_private.h | 1 + dlls/dbghelp/symbol.c | 45 +++++++++++++++++----------------- 2 files changed, 24 insertions(+), 22 deletions(-) diff --git a/dlls/dbghelp/dbghelp_private.h b/dlls/dbghelp/dbghelp_private.h index 334f1d5bf833..06f3e397d622 100644 --- a/dlls/dbghelp/dbghelp_private.h +++ b/dlls/dbghelp/dbghelp_private.h @@ -827,6 +827,7 @@ extern BOOL symt_get_address(const struct symt* type, ULONG64* addr); extern int __cdecl symt_cmp_addr(const void* p1, const void* p2); extern void copy_symbolW(SYMBOL_INFOW* siw, const SYMBOL_INFO* si); extern void symbol_setname(SYMBOL_INFO* si, const char* name); +extern BOOL symt_match_stringAW(const char *string, const WCHAR *re, BOOL _case); extern struct symt_ht* symt_find_nearest(struct module* module, DWORD_PTR addr); extern struct symt_ht* diff --git a/dlls/dbghelp/symbol.c b/dlls/dbghelp/symbol.c index 961f77094b53..4fdd0a141be3 100644 --- a/dlls/dbghelp/symbol.c +++ b/dlls/dbghelp/symbol.c @@ -2359,13 +2359,28 @@ static BOOL re_match_multi(const WCHAR** pstring, const WCHAR** pre, BOOL _case) return TRUE; } +BOOL symt_match_stringAW(const char *string, const WCHAR *re, BOOL _case) +{ + WCHAR* strW; + BOOL ret = FALSE; + DWORD sz; + + sz = MultiByteToWideChar(CP_ACP, 0, string, -1, NULL, 0); + if ((strW = HeapAlloc(GetProcessHeap(), 0, sz * sizeof(WCHAR)))) + { + MultiByteToWideChar(CP_ACP, 0, string, -1, strW, sz); + ret = SymMatchStringW(strW, re, _case); + HeapFree(GetProcessHeap(), 0, strW); + } + return ret; +} + /****************************************************************** * SymMatchStringA (DBGHELP.@) * */ BOOL WINAPI SymMatchStringA(PCSTR string, PCSTR re, BOOL _case) { - WCHAR* strW; WCHAR* reW; BOOL ret = FALSE; DWORD sz; @@ -2377,17 +2392,13 @@ BOOL WINAPI SymMatchStringA(PCSTR string, PCSTR re, BOOL _case) } TRACE("%s %s %c\n", debugstr_a(string), debugstr_a(re), _case ? 'Y' : 'N'); - sz = MultiByteToWideChar(CP_ACP, 0, string, -1, NULL, 0); - if ((strW = HeapAlloc(GetProcessHeap(), 0, sz * sizeof(WCHAR)))) - MultiByteToWideChar(CP_ACP, 0, string, -1, strW, sz); sz = MultiByteToWideChar(CP_ACP, 0, re, -1, NULL, 0); if ((reW = HeapAlloc(GetProcessHeap(), 0, sz * sizeof(WCHAR)))) + { MultiByteToWideChar(CP_ACP, 0, re, -1, reW, sz); - - if (strW && reW) - ret = SymMatchStringW(strW, reW, _case); - HeapFree(GetProcessHeap(), 0, strW); - HeapFree(GetProcessHeap(), 0, reW); + ret = symt_match_stringAW(string, reW, _case); + HeapFree(GetProcessHeap(), 0, reW); + } return ret; } @@ -2555,20 +2566,10 @@ BOOL WINAPI SymEnumLines(HANDLE hProcess, ULONG64 base, PCSTR compiland, if (dli->is_source_file) { file = source_get(pair.effective, dli->u.source_file); - if (!file) sci.FileName[0] = '\0'; + if (file && symt_match_stringAW(file, srcmask, FALSE)) + strcpy(sci.FileName, file); else - { - DWORD sz = MultiByteToWideChar(CP_ACP, 0, file, -1, NULL, 0); - WCHAR* fileW; - - if ((fileW = HeapAlloc(GetProcessHeap(), 0, sz * sizeof(WCHAR)))) - MultiByteToWideChar(CP_ACP, 0, file, -1, fileW, sz); - if (SymMatchStringW(fileW, srcmask, FALSE)) - strcpy(sci.FileName, file); - else - sci.FileName[0] = '\0'; - HeapFree(GetProcessHeap(), 0, fileW); - } + sci.FileName[0] = '\0'; } else if (sci.FileName[0]) { From a852340d2dfa23a4b6d53bf4bb7898b2dcff4a9a Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Thu, 17 Oct 2024 09:43:15 +0200 Subject: [PATCH 223/454] winedbg: Support more integral types in VARIANT for enum value. Signed-off-by: Eric Pouech (cherry picked from commit 2609784e2fa317058b75f2d61da65f77655d33f8) --- programs/winedbg/memory.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/programs/winedbg/memory.c b/programs/winedbg/memory.c index 5431c4297cd5..3da70e1bfb21 100644 --- a/programs/winedbg/memory.c +++ b/programs/winedbg/memory.c @@ -601,14 +601,18 @@ static void print_typed_basic(const struct dbg_lvalue* lvalue) for (i = 0; i < min(fcp->Count, count); i++) { sub_type.id = fcp->ChildId[i]; - if (!types_get_info(&sub_type, TI_GET_VALUE, &variant)) + if (!types_get_info(&sub_type, TI_GET_VALUE, &variant)) continue; switch (V_VT(&variant)) { - case VT_I1: ok = (val_int == V_I1(&variant)); break; - case VT_I2: ok = (val_int == V_I2(&variant)); break; - case VT_I4: ok = (val_int == V_I4(&variant)); break; - case VT_I8: ok = (val_int == V_I8(&variant)); break; + case VT_I1: ok = (val_int == V_I1(&variant)); break; + case VT_I2: ok = (val_int == V_I2(&variant)); break; + case VT_I4: ok = (val_int == V_I4(&variant)); break; + case VT_I8: ok = (val_int == V_I8(&variant)); break; + case VT_UI1: ok = (val_int == (dbg_lguint_t)V_UI1(&variant)); break; + case VT_UI2: ok = (val_int == (dbg_lguint_t)V_UI2(&variant)); break; + case VT_UI4: ok = (val_int == (dbg_lguint_t)V_UI4(&variant)); break; + case VT_UI8: ok = (val_int == (dbg_lguint_t)V_UI8(&variant)); break; default: WINE_FIXME("Unsupported variant type (%u)\n", V_VT(&variant)); } if (ok && types_get_info(&sub_type, TI_GET_SYMNAME, &ptr) && ptr) From 71b6eced02e1119177d5fee9e942529c5094bfe2 Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Sat, 14 Sep 2024 11:37:17 +0200 Subject: [PATCH 224/454] dbghelp: Pass a VARIANT to add an enumeration entry. Signed-off-by: Eric Pouech (cherry picked from commit 38a6eac6ccce0e8724e3ede33126871a7b3de3aa) --- dlls/dbghelp/dbghelp_private.h | 6 +++--- dlls/dbghelp/dwarf.c | 6 +++++- dlls/dbghelp/msc.c | 10 ++++++++-- dlls/dbghelp/stabs.c | 7 +++++-- dlls/dbghelp/type.c | 5 ++--- 5 files changed, 23 insertions(+), 11 deletions(-) diff --git a/dlls/dbghelp/dbghelp_private.h b/dlls/dbghelp/dbghelp_private.h index 06f3e397d622..90b41691f331 100644 --- a/dlls/dbghelp/dbghelp_private.h +++ b/dlls/dbghelp/dbghelp_private.h @@ -930,9 +930,9 @@ extern BOOL symt_add_udt_element(struct module* module, extern struct symt_enum* symt_new_enum(struct module* module, const char* typename, struct symt* basetype); -extern BOOL symt_add_enum_element(struct module* module, - struct symt_enum* enum_type, - const char* name, int value); +extern BOOL symt_add_enum_element(struct module* module, + struct symt_enum* enum_type, + const char* name, const VARIANT *value); extern struct symt_array* symt_new_array(struct module* module, int min, DWORD count, struct symt* base, struct symt* index); diff --git a/dlls/dbghelp/dwarf.c b/dlls/dbghelp/dwarf.c index 5442e33be713..29c41627a816 100644 --- a/dlls/dbghelp/dwarf.c +++ b/dlls/dbghelp/dwarf.c @@ -1838,6 +1838,7 @@ static struct symt* dwarf2_parse_udt_type(dwarf2_debug_info_t* di, static void dwarf2_parse_enumerator(dwarf2_debug_info_t* di, struct symt_enum* parent) { + VARIANT v; struct attribute name; struct attribute value; @@ -1845,7 +1846,10 @@ static void dwarf2_parse_enumerator(dwarf2_debug_info_t* di, if (!dwarf2_find_attribute(di, DW_AT_name, &name)) return; if (!dwarf2_find_attribute(di, DW_AT_const_value, &value)) value.u.svalue = 0; - symt_add_enum_element(di->unit_ctx->module_ctx->module, parent, name.u.string, value.u.svalue); + V_VT(&v) = VT_I4; + V_I4(&v) = value.u.svalue; + + symt_add_enum_element(di->unit_ctx->module_ctx->module, parent, name.u.string, &v); if (dwarf2_get_di_children(di)) FIXME("Unsupported children\n"); } diff --git a/dlls/dbghelp/msc.c b/dlls/dbghelp/msc.c index 510252428791..18080768fadd 100644 --- a/dlls/dbghelp/msc.c +++ b/dlls/dbghelp/msc.c @@ -891,19 +891,25 @@ static BOOL codeview_add_type_enum_field_list(struct codeview_type_parse* ctp, { case LF_ENUMERATE_V1: { + VARIANT v; int value, vlen = numeric_leaf(&value, type->enumerate_v1.data); const struct p_string* p_name = (const struct p_string*)&type->enumerate_v1.data[vlen]; + V_VT(&v) = VT_I4; + V_I4(&v) = value; - symt_add_enum_element(ctp->module, symt, terminate_string(p_name), value); + symt_add_enum_element(ctp->module, symt, terminate_string(p_name), &v); ptr += 2 + 2 + vlen + (1 + p_name->namelen); break; } case LF_ENUMERATE_V3: { + VARIANT v; int value, vlen = numeric_leaf(&value, type->enumerate_v3.data); const char* name = (const char*)&type->enumerate_v3.data[vlen]; + V_VT(&v) = VT_I4; + V_I4(&v) = value; - symt_add_enum_element(ctp->module, symt, name, value); + symt_add_enum_element(ctp->module, symt, name, &v); ptr += 2 + 2 + vlen + (1 + strlen(name)); break; } diff --git a/dlls/dbghelp/stabs.c b/dlls/dbghelp/stabs.c index 2f3ee9fff4ed..d02f320e281b 100644 --- a/dlls/dbghelp/stabs.c +++ b/dlls/dbghelp/stabs.c @@ -718,9 +718,10 @@ static inline int stabs_pts_read_aggregate(struct ParseTypedefData* ptd, return 0; } -static inline int stabs_pts_read_enum(struct ParseTypedefData* ptd, +static inline int stabs_pts_read_enum(struct ParseTypedefData* ptd, struct symt_enum* edt) { + VARIANT v; LONG_PTR value; int idx; @@ -730,7 +731,9 @@ static inline int stabs_pts_read_enum(struct ParseTypedefData* ptd, PTS_ABORTIF(ptd, stabs_pts_read_id(ptd) == -1); PTS_ABORTIF(ptd, stabs_pts_read_number(ptd, &value) == -1); PTS_ABORTIF(ptd, *ptd->ptr++ != ','); - symt_add_enum_element(ptd->module, edt, ptd->buf + idx, value); + V_VT(&v) = VT_I4; + V_I4(&v) = value; + symt_add_enum_element(ptd->module, edt, ptd->buf + idx, &v); ptd->idx = idx; } ptd->ptr++; diff --git a/dlls/dbghelp/type.c b/dlls/dbghelp/type.c index 7e9bb44e6903..a3ad80aacbb0 100644 --- a/dlls/dbghelp/type.c +++ b/dlls/dbghelp/type.c @@ -342,7 +342,7 @@ struct symt_enum* symt_new_enum(struct module* module, const char* typename, } BOOL symt_add_enum_element(struct module* module, struct symt_enum* enum_type, - const char* name, int value) + const char* name, const VARIANT *variant) { struct symt_data* e; struct symt** p; @@ -357,8 +357,7 @@ BOOL symt_add_enum_element(struct module* module, struct symt_enum* enum_type, e->kind = DataIsConstant; e->container = &enum_type->symt; e->type = enum_type->base_type; - V_VT(&e->u.value) = VT_I4; - V_I4(&e->u.value) = value; + e->u.value = *variant; p = vector_add(&enum_type->vchildren, &module->pool); if (!p) return FALSE; /* FIXME we leak e */ From a54e5bc3448783bd3a762365cb4067a3948025dd Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Sat, 14 Sep 2024 11:44:26 +0200 Subject: [PATCH 225/454] dbghelp: Use VARIANT for storing enum values (pdb). Signed-off-by: Eric Pouech (cherry picked from commit e6ecd70618cdcc44493fb49c88d58add86480f93) --- dlls/dbghelp/msc.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/dlls/dbghelp/msc.c b/dlls/dbghelp/msc.c index 18080768fadd..f5872442263f 100644 --- a/dlls/dbghelp/msc.c +++ b/dlls/dbghelp/msc.c @@ -273,15 +273,15 @@ static int leaf_as_variant(VARIANT *v, const unsigned char *leaf) { unsigned short int type = *(const unsigned short *)leaf; int length = 2; - leaf += length; if (type < LF_NUMERIC) { - V_VT(v) = VT_UINT; - V_UINT(v) = type; + V_VT(v) = VT_I2; + V_I2(v) = type; } else { + leaf += length; switch (type) { case LF_CHAR: @@ -892,10 +892,8 @@ static BOOL codeview_add_type_enum_field_list(struct codeview_type_parse* ctp, case LF_ENUMERATE_V1: { VARIANT v; - int value, vlen = numeric_leaf(&value, type->enumerate_v1.data); + int vlen = leaf_as_variant(&v, type->enumerate_v1.data); const struct p_string* p_name = (const struct p_string*)&type->enumerate_v1.data[vlen]; - V_VT(&v) = VT_I4; - V_I4(&v) = value; symt_add_enum_element(ctp->module, symt, terminate_string(p_name), &v); ptr += 2 + 2 + vlen + (1 + p_name->namelen); @@ -904,10 +902,8 @@ static BOOL codeview_add_type_enum_field_list(struct codeview_type_parse* ctp, case LF_ENUMERATE_V3: { VARIANT v; - int value, vlen = numeric_leaf(&value, type->enumerate_v3.data); + int vlen = leaf_as_variant(&v, type->enumerate_v3.data); const char* name = (const char*)&type->enumerate_v3.data[vlen]; - V_VT(&v) = VT_I4; - V_I4(&v) = value; symt_add_enum_element(ctp->module, symt, name, &v); ptr += 2 + 2 + vlen + (1 + strlen(name)); From 026da484936f2ad8d7c186e297747a0c00e9db9a Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Sun, 15 Sep 2024 09:01:11 +0200 Subject: [PATCH 226/454] dbghelp: Store LEB128 encoded as 64bit entities (dwarf). As they can represent constant values, use largest possible integral type. Signed-off-by: Eric Pouech (cherry picked from commit 01f7d93aaa291763ce4b19d31ff5750f960c8f13) --- dlls/dbghelp/dwarf.c | 70 ++++++++++++++++++++++++++------------------ dlls/dbghelp/msc.c | 7 +++-- 2 files changed, 46 insertions(+), 31 deletions(-) diff --git a/dlls/dbghelp/dwarf.c b/dlls/dbghelp/dwarf.c index 29c41627a816..f7a6de5f8c2c 100644 --- a/dlls/dbghelp/dwarf.c +++ b/dlls/dbghelp/dwarf.c @@ -128,6 +128,7 @@ struct attribute ULONG_PTR uvalue; ULONGLONG lluvalue; LONG_PTR svalue; + LONGLONG llsvalue; const char* string; struct dwarf2_block block; } u; @@ -282,18 +283,19 @@ static DWORD64 dwarf2_parse_u8(dwarf2_traverse_context_t* ctx) return uvalue; } -static ULONG_PTR dwarf2_get_leb128_as_unsigned(const unsigned char* ptr, const unsigned char** end) +static ULONG64 dwarf2_get_leb128_as_unsigned(const unsigned char* ptr, const unsigned char** end) { - ULONG_PTR ret = 0; + ULONG64 ret = 0; unsigned char byte; unsigned shift = 0; do { byte = dwarf2_get_byte(ptr++); - ret |= (byte & 0x7f) << shift; + ret |= (ULONG64)(byte & 0x7f) << shift; shift += 7; } while (byte & 0x80); + if ((ret >> (shift - 7)) != (byte & 0x7F)) FIXME("Overflow in LEB128 encoding\n"); if (end) *end = ptr; return ret; @@ -301,47 +303,45 @@ static ULONG_PTR dwarf2_get_leb128_as_unsigned(const unsigned char* ptr, const u static ULONG_PTR dwarf2_leb128_as_unsigned(dwarf2_traverse_context_t* ctx) { - ULONG_PTR ret; + ULONG64 ret; assert(ctx); ret = dwarf2_get_leb128_as_unsigned(ctx->data, &ctx->data); - + if (ret != (ULONG_PTR)ret) WARN("Dropping bits from LEB128 value\n"); return ret; } -static LONG_PTR dwarf2_get_leb128_as_signed(const unsigned char* ptr, const unsigned char** end) +static LONG64 dwarf2_get_leb128_as_signed(const unsigned char* ptr, const unsigned char** end) { - LONG_PTR ret = 0; + ULONG64 ret = 0; unsigned char byte; unsigned shift = 0; - const unsigned size = sizeof(int) * 8; do { byte = dwarf2_get_byte(ptr++); - ret |= (byte & 0x7f) << shift; + ret |= (ULONG64)(byte & 0x7f) << shift; shift += 7; } while (byte & 0x80); - if (end) *end = ptr; - /* as spec: sign bit of byte is 2nd high order bit (80x40) - * -> 0x80 is used as flag. - */ - if ((shift < size) && (byte & 0x40)) - { - ret |= - (1 << shift); - } + if (end) *end = ptr; + if ((shift < sizeof(ULONG64) * 8) && (byte & 0x40)) + /* as spec: sign bit of byte is 2nd high order bit (80x40) + * -> 0x80 is used as flag. + */ + ret |= ~(ULONG64)0 << shift; return ret; } static LONG_PTR dwarf2_leb128_as_signed(dwarf2_traverse_context_t* ctx) { - LONG_PTR ret = 0; + LONG64 ret = 0; assert(ctx); ret = dwarf2_get_leb128_as_signed(ctx->data, &ctx->data); + if (ret != (LONG_PTR)ret) WARN("Dropping bits from LEB128 value\n"); return ret; } @@ -609,16 +609,16 @@ static BOOL dwarf2_fill_attr(const dwarf2_parse_context_t* ctx, break; case DW_FORM_sdata: - attr->u.svalue = dwarf2_get_leb128_as_signed(data, NULL); + attr->u.llsvalue = dwarf2_get_leb128_as_signed(data, NULL); break; case DW_FORM_ref_udata: - attr->u.uvalue = ctx->ref_offset + dwarf2_get_leb128_as_unsigned(data, NULL); + attr->u.lluvalue = ctx->ref_offset + dwarf2_get_leb128_as_unsigned(data, NULL); TRACE("ref_udata<0x%Ix>\n", attr->u.uvalue); break; case DW_FORM_udata: - attr->u.uvalue = dwarf2_get_leb128_as_unsigned(data, NULL); + attr->u.lluvalue = dwarf2_get_leb128_as_unsigned(data, NULL); TRACE("udata<0x%Ix>\n", attr->u.uvalue); break; @@ -1061,12 +1061,24 @@ static BOOL dwarf2_compute_location_attr(dwarf2_parse_context_t* ctx, return TRUE; } /* fall through */ - case DW_FORM_data1: case DW_FORM_data2: - case DW_FORM_udata: case DW_FORM_sdata: + case DW_FORM_data1: + case DW_FORM_data2: + loc->kind = loc_absolute; + loc->reg = 0; + loc->offset = xloc.u.uvalue; + return TRUE; + case DW_FORM_udata: loc->kind = loc_absolute; loc->reg = 0; + if (xloc.u.uvalue != xloc.u.lluvalue) WARN("Cropping integral value\n"); loc->offset = xloc.u.uvalue; return TRUE; + case DW_FORM_sdata: + loc->kind = loc_absolute; + loc->reg = 0; + if (xloc.u.svalue != xloc.u.llsvalue) WARN("Cropping integral value\n"); + loc->offset = xloc.u.svalue; + return TRUE; case DW_FORM_data8: if (ctx->head.version >= 4) { @@ -1285,12 +1297,14 @@ static BOOL dwarf2_fill_ranges(const dwarf2_debug_info_t* di, struct addr_range* { case DW_FORM_addr: break; + case DW_FORM_sdata: + case DW_FORM_udata: + if (high_pc.u.uvalue != high_pc.u.lluvalue) WARN("Cropping integral value\n"); + /* fall through */ case DW_FORM_data1: case DW_FORM_data2: case DW_FORM_data4: case DW_FORM_data8: - case DW_FORM_sdata: - case DW_FORM_udata: /* From dwarf4 on, when FORM's class is constant, high_pc is an offset from low_pc */ high_pc.u.uvalue += low_pc.u.uvalue; break; @@ -1999,12 +2013,12 @@ static void dwarf2_parse_variable(dwarf2_subprogram_t* subpgm, case DW_FORM_data1: case DW_FORM_data2: case DW_FORM_data4: - case DW_FORM_udata: case DW_FORM_addr: V_VT(&v) = VT_UI4; V_UI4(&v) = value.u.uvalue; break; + case DW_FORM_udata: case DW_FORM_data8: case DW_FORM_sec_offset: V_VT(&v) = VT_UI8; @@ -2012,8 +2026,8 @@ static void dwarf2_parse_variable(dwarf2_subprogram_t* subpgm, break; case DW_FORM_sdata: - V_VT(&v) = VT_I4; - V_I4(&v) = value.u.svalue; + V_VT(&v) = VT_I8; + V_I8(&v) = value.u.llsvalue; break; case DW_FORM_strp: diff --git a/dlls/dbghelp/msc.c b/dlls/dbghelp/msc.c index f5872442263f..c01a8fd9c691 100644 --- a/dlls/dbghelp/msc.c +++ b/dlls/dbghelp/msc.c @@ -395,7 +395,8 @@ static int leaf_as_variant(VARIANT *v, const unsigned char *leaf) return length; } -static int numeric_leaf(int *value, const unsigned char *leaf) +#define numeric_leaf(v,l) _numeric_leaf(__LINE__,v,l) +static int _numeric_leaf(unsigned line, int *value, const unsigned char *leaf) { unsigned short int type = *(const unsigned short int *)leaf; int length = 2; @@ -436,7 +437,7 @@ static int numeric_leaf(int *value, const unsigned char *leaf) case LF_QUADWORD: case LF_UQUADWORD: - FIXME("Unsupported numeric leaf type %04x\n", type); + FIXME("Unsupported numeric leaf type %04x from %u (%I64x)\n", type, line, *(ULONG64*)leaf); length += 8; *value = 0; /* FIXME */ break; @@ -2786,7 +2787,7 @@ static BOOL codeview_snarf(const struct msc_debug_info* msc_dbg, if (top_frame_size == -1 && curr_func && curr_func == top_func) top_frame_size = sym->frame_info_v2.sz_frame; else - FIXME("Unexpected S_FRAMEPROC %d (%p %p)\n", top_frame_size, top_func, curr_func); + FIXME("Unexpected S_FRAMEPROC %d (%p %p) %x\n", top_frame_size, top_func, curr_func, i); break; /* the symbols we can safely ignore for now */ From 897b3dcc42195db750cb3b87b817ec12782660a1 Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Sun, 15 Sep 2024 09:58:25 +0200 Subject: [PATCH 227/454] dbghelp: Introduce helper to fill in VARIANT (dwarf). Signed-off-by: Eric Pouech (cherry picked from commit 20607748735bd85aac2502b32be68213163e1e44) --- dlls/dbghelp/dwarf.c | 110 +++++++++++++++++++++++-------------------- 1 file changed, 58 insertions(+), 52 deletions(-) diff --git a/dlls/dbghelp/dwarf.c b/dlls/dbghelp/dwarf.c index f7a6de5f8c2c..8db8b2a9f145 100644 --- a/dlls/dbghelp/dwarf.c +++ b/dlls/dbghelp/dwarf.c @@ -712,6 +712,63 @@ static BOOL dwarf2_fill_attr(const dwarf2_parse_context_t* ctx, return TRUE; } +static BOOL dwarf2_fill_in_variant(struct module *module, VARIANT *v, const struct attribute *attr) +{ + switch (attr->form) + { + case DW_FORM_data1: + case DW_FORM_data2: + case DW_FORM_data4: + case DW_FORM_addr: + V_VT(v) = VT_UI4; + V_UI4(v) = attr->u.uvalue; + break; + + case DW_FORM_udata: + case DW_FORM_data8: + case DW_FORM_sec_offset: + V_VT(v) = VT_UI8; + V_UI8(v) = attr->u.lluvalue; + break; + + case DW_FORM_sdata: + V_VT(v) = VT_I8; + V_I8(v) = attr->u.llsvalue; + break; + + case DW_FORM_strp: + case DW_FORM_string: + /* FIXME: native doesn't report const strings from here !! + * however, the value of the string is in the code somewhere + */ + V_VT(v) = VT_BYREF; + V_BYREF(v) = pool_strdup(&module->pool, attr->u.string); + break; + + case DW_FORM_block: + case DW_FORM_block1: + case DW_FORM_block2: + case DW_FORM_block4: + case DW_FORM_exprloc: + V_VT(v) = VT_I4; + switch (attr->u.block.size) + { + case 1: V_I4(v) = *(BYTE*)attr->u.block.ptr; break; + case 2: V_I4(v) = *(USHORT*)attr->u.block.ptr; break; + case 4: V_I4(v) = *(DWORD*)attr->u.block.ptr; break; + default: + V_VT(v) = VT_BYREF; + V_BYREF(v) = pool_alloc(&module->pool, attr->u.block.size); + memcpy(V_BYREF(v), attr->u.block.ptr, attr->u.block.size); + } + break; + default: + V_VT(v) = VT_EMPTY; + return FALSE; + } + return TRUE; +} + static dwarf2_debug_info_t* dwarf2_jump_to_debug_info(struct attribute* attr); static BOOL dwarf2_find_attribute(const dwarf2_debug_info_t* di, @@ -2008,60 +2065,9 @@ static void dwarf2_parse_variable(dwarf2_subprogram_t* subpgm, { VARIANT v; - switch (value.form) - { - case DW_FORM_data1: - case DW_FORM_data2: - case DW_FORM_data4: - case DW_FORM_addr: - V_VT(&v) = VT_UI4; - V_UI4(&v) = value.u.uvalue; - break; - - case DW_FORM_udata: - case DW_FORM_data8: - case DW_FORM_sec_offset: - V_VT(&v) = VT_UI8; - V_UI8(&v) = value.u.lluvalue; - break; - - case DW_FORM_sdata: - V_VT(&v) = VT_I8; - V_I8(&v) = value.u.llsvalue; - break; - - case DW_FORM_strp: - case DW_FORM_string: - /* FIXME: native doesn't report const strings from here !! - * however, the value of the string is in the code somewhere - */ - V_VT(&v) = VT_BYREF; - V_BYREF(&v) = pool_strdup(&subpgm->ctx->module_ctx->module->pool, value.u.string); - break; - - case DW_FORM_block: - case DW_FORM_block1: - case DW_FORM_block2: - case DW_FORM_block4: - case DW_FORM_exprloc: - V_VT(&v) = VT_I4; - switch (value.u.block.size) - { - case 1: V_I4(&v) = *(BYTE*)value.u.block.ptr; break; - case 2: V_I4(&v) = *(USHORT*)value.u.block.ptr; break; - case 4: V_I4(&v) = *(DWORD*)value.u.block.ptr; break; - default: - V_VT(&v) = VT_BYREF; - V_BYREF(&v) = pool_alloc(&subpgm->ctx->module_ctx->module->pool, value.u.block.size); - memcpy(V_BYREF(&v), value.u.block.ptr, value.u.block.size); - } - break; - - default: + if (!dwarf2_fill_in_variant(subpgm->ctx->module_ctx->module, &v, &value)) FIXME("Unsupported form for const value %s (%Ix)\n", debugstr_a(name.u.string), value.form); - V_VT(&v) = VT_EMPTY; - } if (subpgm->current_func) { if (is_pmt) WARN("Constant parameter %s reported as local variable in function '%s'\n", From 4b3b315f1cfc0f82891911545789bcf4f9d33ef4 Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Fri, 13 Sep 2024 15:53:56 +0200 Subject: [PATCH 228/454] dbghelp: Fix debug information for C++ enumeration types (dwarf). Signed-off-by: Eric Pouech (cherry picked from commit 0a42aa7b9ebf0fd54cc4f9a99e1f2ddadd509159) --- dlls/dbghelp/dwarf.c | 121 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 102 insertions(+), 19 deletions(-) diff --git a/dlls/dbghelp/dwarf.c b/dlls/dbghelp/dwarf.c index 8db8b2a9f145..8ba953941f85 100644 --- a/dlls/dbghelp/dwarf.c +++ b/dlls/dbghelp/dwarf.c @@ -712,28 +712,48 @@ static BOOL dwarf2_fill_attr(const dwarf2_parse_context_t* ctx, return TRUE; } -static BOOL dwarf2_fill_in_variant(struct module *module, VARIANT *v, const struct attribute *attr) +static struct symt *symt_get_real_type(struct symt *symt) { + while (symt && symt->tag == SymTagTypedef) + symt = ((struct symt_typedef*)symt)->type; + return symt; +} + +static BOOL dwarf2_fill_in_variant(struct module *module, VARIANT *v, const struct attribute *attr, struct symt *type) +{ + ULONG64 uinteger; + LONG64 sinteger; + enum BasicType bt = btInt; + + type = symt_get_real_type(type); + if (symt_check_tag(type, SymTagBaseType)) + bt = ((struct symt_basic*)type)->bt; + + /* data1, data2, data4 can hold either signed or unsigned values... + * so ensure proper extension of signed types... + */ switch (attr->form) { case DW_FORM_data1: + uinteger = attr->u.uvalue; + sinteger = (char)(unsigned char)attr->u.uvalue; + break; case DW_FORM_data2: + uinteger = attr->u.uvalue; + sinteger = (short)(unsigned short)attr->u.uvalue; + break; case DW_FORM_data4: - case DW_FORM_addr: - V_VT(v) = VT_UI4; - V_UI4(v) = attr->u.uvalue; + uinteger = attr->u.uvalue; + sinteger = (int)(unsigned int)attr->u.uvalue; break; case DW_FORM_udata: case DW_FORM_data8: - case DW_FORM_sec_offset: - V_VT(v) = VT_UI8; - V_UI8(v) = attr->u.lluvalue; + sinteger = uinteger = attr->u.lluvalue; break; case DW_FORM_sdata: - V_VT(v) = VT_I8; - V_I8(v) = attr->u.llsvalue; + uinteger = sinteger = attr->u.llsvalue; break; case DW_FORM_strp: @@ -743,6 +763,7 @@ static BOOL dwarf2_fill_in_variant(struct module *module, VARIANT *v, const stru */ V_VT(v) = VT_BYREF; V_BYREF(v) = pool_strdup(&module->pool, attr->u.string); + return TRUE; break; case DW_FORM_block: @@ -761,11 +782,68 @@ static BOOL dwarf2_fill_in_variant(struct module *module, VARIANT *v, const stru V_BYREF(v) = pool_alloc(&module->pool, attr->u.block.size); memcpy(V_BYREF(v), attr->u.block.ptr, attr->u.block.size); } + return TRUE; break; + case DW_FORM_sec_offset: + case DW_FORM_addr: + FIXME("Unexpected form %Ix\n", attr->form); default: V_VT(v) = VT_EMPTY; return FALSE; } + /* native always stores in the shortest format in variant */ + if (bt == btChar || bt == btInt || bt == btLong) + { + if (sinteger == (signed char)sinteger) + { + V_VT(v) = VT_I1; + V_I1(v) = sinteger; + } + if (sinteger == (short int)sinteger) + { + V_VT(v) = VT_I2; + V_I2(v) = sinteger; + } + else if (sinteger == (int)sinteger) + { + V_VT(v) = VT_I4; + V_I4(v) = sinteger; + } + else + { + V_VT(v) = VT_I8; + V_I8(v) = sinteger; + } + } + else if (bt == btUInt || bt == btULong || bt == btWChar) + { + if (uinteger == (unsigned char)uinteger) + { + V_VT(v) = VT_UI1; + V_UI1(v) = uinteger; + } + else if (uinteger == (unsigned short int)uinteger) + { + V_VT(v) = VT_UI2; + V_UI2(v) = uinteger; + } + else if (uinteger == (unsigned int)uinteger) + { + V_VT(v) = VT_UI4; + V_UI4(v) = uinteger; + } + else + { + V_VT(v) = VT_UI8; + V_UI8(v) = uinteger; + } + } + else + { + FIXME("Unexpected base type bt=%x for form=%Ix\n", bt, attr->form); + return FALSE; + } + return TRUE; } @@ -1915,11 +1993,15 @@ static void dwarf2_parse_enumerator(dwarf2_debug_info_t* di, TRACE("%s\n", dwarf2_debug_di(di)); - if (!dwarf2_find_attribute(di, DW_AT_name, &name)) return; - if (!dwarf2_find_attribute(di, DW_AT_const_value, &value)) value.u.svalue = 0; - V_VT(&v) = VT_I4; - V_I4(&v) = value.u.svalue; + V_VT(&v) = VT_EMPTY; + if (!dwarf2_find_attribute(di, DW_AT_name, &name)) return; + if (dwarf2_find_attribute(di, DW_AT_const_value, &value) && + symt_check_tag(parent->base_type, SymTagBaseType)) + { + if (!dwarf2_fill_in_variant(di->unit_ctx->module_ctx->module, &v, &value, parent->base_type)) + TRACE("Failed to get variant\n"); + } symt_add_enum_element(di->unit_ctx->module_ctx->module, parent, name.u.string, &v); if (dwarf2_get_di_children(di)) FIXME("Unsupported children\n"); @@ -1930,7 +2012,7 @@ static struct symt* dwarf2_parse_enumeration_type(dwarf2_debug_info_t* di) struct attribute name; struct attribute attrtype; dwarf2_debug_info_t*ditype; - struct symt* type; + struct symt* type = NULL; struct vector* children; dwarf2_debug_info_t*child; unsigned int i; @@ -1941,20 +2023,20 @@ static struct symt* dwarf2_parse_enumeration_type(dwarf2_debug_info_t* di) if (!dwarf2_find_attribute(di, DW_AT_name, &name)) name.u.string = NULL; if (dwarf2_find_attribute(di, DW_AT_type, &attrtype) && (ditype = dwarf2_jump_to_debug_info(&attrtype)) != NULL) - type = ditype->symt; - else /* no type found for this enumeration, construct it from size */ + type = symt_get_real_type(ditype->symt); + if (!type || type->tag != SymTagBaseType) /* no type found for this enumeration, construct it from size */ { struct attribute size; struct symt_basic* basetype; if (!dwarf2_find_attribute(di, DW_AT_byte_size, &size)) size.u.uvalue = 4; - - switch (size.u.uvalue) /* FIXME: that's wrong */ + switch (size.u.uvalue) { case 1: basetype = symt_get_basic(btInt, 1); break; case 2: basetype = symt_get_basic(btInt, 2); break; default: case 4: basetype = symt_get_basic(btInt, 4); break; + case 8: basetype = symt_get_basic(btInt, 8); break; } type = &basetype->symt; } @@ -2065,9 +2147,10 @@ static void dwarf2_parse_variable(dwarf2_subprogram_t* subpgm, { VARIANT v; - if (!dwarf2_fill_in_variant(subpgm->ctx->module_ctx->module, &v, &value)) + if (!dwarf2_fill_in_variant(subpgm->ctx->module_ctx->module, &v, &value, param_type)) FIXME("Unsupported form for const value %s (%Ix)\n", debugstr_a(name.u.string), value.form); + if (subpgm->current_func) { if (is_pmt) WARN("Constant parameter %s reported as local variable in function '%s'\n", From ad24d0fd0ba7af808f7b5448005c953df3ee0b93 Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Mon, 6 Jan 2025 11:59:00 +0100 Subject: [PATCH 229/454] dbghelp: Add a couple of missing basic types for PDB. Signed-off-by: Eric Pouech (cherry picked from commit ea42184e1d076b908b9369a7c8c9ff2ed6458f8e) --- dlls/dbghelp/msc.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/dlls/dbghelp/msc.c b/dlls/dbghelp/msc.c index c01a8fd9c691..58c416eeed8a 100644 --- a/dlls/dbghelp/msc.c +++ b/dlls/dbghelp/msc.c @@ -180,6 +180,9 @@ static void codeview_init_basic_types(struct module* module) cv_basic_types[T_UINT4] = &symt_get_basic(btUInt, 4)->symt; /* UINT4 */ cv_basic_types[T_INT8] = &symt_get_basic(btInt, 8)->symt; /* INT8 */ cv_basic_types[T_UINT8] = &symt_get_basic(btUInt, 8)->symt; /* UINT8 */ + cv_basic_types[T_OCT] = &symt_get_basic(btInt, 8)->symt; /* INT8 */ + cv_basic_types[T_UOCT] = &symt_get_basic(btUInt, 8)->symt; /* UINT8 */ + cv_basic_types[T_HRESULT]= &symt_get_basic(btUInt, 4)->symt; /* HRESULT */ cv_basic_types[T_32PVOID] = &symt_new_pointer(module, cv_basic_types[T_VOID], 4)->symt; @@ -187,10 +190,12 @@ static void codeview_init_basic_types(struct module* module) cv_basic_types[T_32PSHORT] = &symt_new_pointer(module, cv_basic_types[T_SHORT], 4)->symt; cv_basic_types[T_32PLONG] = &symt_new_pointer(module, cv_basic_types[T_LONG], 4)->symt; cv_basic_types[T_32PQUAD] = &symt_new_pointer(module, cv_basic_types[T_QUAD], 4)->symt; + cv_basic_types[T_32POCT] = &symt_new_pointer(module, cv_basic_types[T_OCT], 4)->symt; cv_basic_types[T_32PUCHAR] = &symt_new_pointer(module, cv_basic_types[T_UCHAR], 4)->symt; cv_basic_types[T_32PUSHORT] = &symt_new_pointer(module, cv_basic_types[T_USHORT], 4)->symt; cv_basic_types[T_32PULONG] = &symt_new_pointer(module, cv_basic_types[T_ULONG], 4)->symt; cv_basic_types[T_32PUQUAD] = &symt_new_pointer(module, cv_basic_types[T_UQUAD], 4)->symt; + cv_basic_types[T_32PUOCT] = &symt_new_pointer(module, cv_basic_types[T_UOCT], 4)->symt; cv_basic_types[T_32PBOOL08] = &symt_new_pointer(module, cv_basic_types[T_BOOL08], 4)->symt; cv_basic_types[T_32PBOOL16] = &symt_new_pointer(module, cv_basic_types[T_BOOL16], 4)->symt; cv_basic_types[T_32PBOOL32] = &symt_new_pointer(module, cv_basic_types[T_BOOL32], 4)->symt; @@ -216,10 +221,12 @@ static void codeview_init_basic_types(struct module* module) cv_basic_types[T_64PSHORT] = &symt_new_pointer(module, cv_basic_types[T_SHORT], 8)->symt; cv_basic_types[T_64PLONG] = &symt_new_pointer(module, cv_basic_types[T_LONG], 8)->symt; cv_basic_types[T_64PQUAD] = &symt_new_pointer(module, cv_basic_types[T_QUAD], 8)->symt; + cv_basic_types[T_64POCT] = &symt_new_pointer(module, cv_basic_types[T_OCT], 8)->symt; cv_basic_types[T_64PUCHAR] = &symt_new_pointer(module, cv_basic_types[T_UCHAR], 8)->symt; cv_basic_types[T_64PUSHORT] = &symt_new_pointer(module, cv_basic_types[T_USHORT], 8)->symt; cv_basic_types[T_64PULONG] = &symt_new_pointer(module, cv_basic_types[T_ULONG], 8)->symt; cv_basic_types[T_64PUQUAD] = &symt_new_pointer(module, cv_basic_types[T_UQUAD], 8)->symt; + cv_basic_types[T_64PUOCT] = &symt_new_pointer(module, cv_basic_types[T_UOCT], 8)->symt; cv_basic_types[T_64PBOOL08] = &symt_new_pointer(module, cv_basic_types[T_BOOL08], 8)->symt; cv_basic_types[T_64PBOOL16] = &symt_new_pointer(module, cv_basic_types[T_BOOL16], 8)->symt; cv_basic_types[T_64PBOOL32] = &symt_new_pointer(module, cv_basic_types[T_BOOL32], 8)->symt; @@ -245,10 +252,12 @@ static void codeview_init_basic_types(struct module* module) cv_basic_types[T_PSHORT] = &symt_new_pointer(module, cv_basic_types[T_SHORT], ptrsz)->symt; cv_basic_types[T_PLONG] = &symt_new_pointer(module, cv_basic_types[T_LONG], ptrsz)->symt; cv_basic_types[T_PQUAD] = &symt_new_pointer(module, cv_basic_types[T_QUAD], ptrsz)->symt; + cv_basic_types[T_POCT] = &symt_new_pointer(module, cv_basic_types[T_OCT], ptrsz)->symt; cv_basic_types[T_PUCHAR] = &symt_new_pointer(module, cv_basic_types[T_UCHAR], ptrsz)->symt; cv_basic_types[T_PUSHORT] = &symt_new_pointer(module, cv_basic_types[T_USHORT], ptrsz)->symt; cv_basic_types[T_PULONG] = &symt_new_pointer(module, cv_basic_types[T_ULONG], ptrsz)->symt; cv_basic_types[T_PUQUAD] = &symt_new_pointer(module, cv_basic_types[T_UQUAD], ptrsz)->symt; + cv_basic_types[T_PUOCT] = &symt_new_pointer(module, cv_basic_types[T_UOCT], ptrsz)->symt; cv_basic_types[T_PBOOL08] = &symt_new_pointer(module, cv_basic_types[T_BOOL08], ptrsz)->symt; cv_basic_types[T_PBOOL16] = &symt_new_pointer(module, cv_basic_types[T_BOOL16], ptrsz)->symt; cv_basic_types[T_PBOOL32] = &symt_new_pointer(module, cv_basic_types[T_BOOL32], ptrsz)->symt; From a92ebd36d0f0f2b4ebb00d87777fe24f3a369fc8 Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Mon, 2 Dec 2024 14:48:39 +0100 Subject: [PATCH 230/454] dbghelp: Silence a couple of CodeView symbols. Signed-off-by: Eric Pouech (cherry picked from commit 064b2c8c8a0ddd26132346f35518479d8227293a) --- dlls/dbghelp/msc.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/dlls/dbghelp/msc.c b/dlls/dbghelp/msc.c index 58c416eeed8a..33b7158bceeb 100644 --- a/dlls/dbghelp/msc.c +++ b/dlls/dbghelp/msc.c @@ -1407,6 +1407,10 @@ static struct symt* codeview_parse_one_type(struct codeview_type_parse* ctp, symt = &symt_new_udt(ctp->module, buf, 0, UdtStruct)->symt; } break; + /* types we can simply silence for now */ + case LF_LABEL_V1: + case LF_VFTABLE_V3: + break; default: FIXME("Unsupported type-id leaf %x\n", type->generic.id); dump(type, 2 + type->generic.len); @@ -2800,12 +2804,14 @@ static BOOL codeview_snarf(const struct msc_debug_info* msc_dbg, break; /* the symbols we can safely ignore for now */ + case S_SKIP: case S_TRAMPOLINE: case S_FRAMECOOKIE: case S_SECTION: case S_COFFGROUP: case S_EXPORT: case S_CALLSITEINFO: + case S_ARMSWITCHTABLE: /* even if S_LOCAL groks all the S_DEFRANGE* records following itself, * those kinds of records can also be present after a S_FILESTATIC record * so silence them until (at least) S_FILESTATIC is supported From ecdef0bb99bb6364b3870e95551a2ce50e952035 Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Mon, 4 Nov 2024 11:19:32 +0100 Subject: [PATCH 231/454] dbghelp: Uniformize the two readers for PDB line information. Basically, - calling one or the other from the same point, - exposing if line number information has been read. This refactorization will be used in next patch. Signed-off-by: Eric Pouech (cherry picked from commit 994f8d2030180de841f3fc0022a3bf9d1ffed920) --- dlls/dbghelp/msc.c | 66 +++++++++++++++++++++++++++++----------------- 1 file changed, 42 insertions(+), 24 deletions(-) diff --git a/dlls/dbghelp/msc.c b/dlls/dbghelp/msc.c index 33b7158bceeb..1352fe145f4b 100644 --- a/dlls/dbghelp/msc.c +++ b/dlls/dbghelp/msc.c @@ -1585,7 +1585,7 @@ static BOOL codeview_parse_type_table(struct codeview_type_parse* ctp) static ULONG_PTR codeview_get_address(const struct msc_debug_info* msc_dbg, unsigned seg, unsigned offset); -static void codeview_snarf_linetab(const struct msc_debug_info* msc_dbg, const BYTE* linetab, +static BOOL codeview_snarf_linetab(const struct msc_debug_info* msc_dbg, const BYTE* linetab, int size, BOOL pascal_str) { const BYTE* ptr = linetab; @@ -1603,6 +1603,7 @@ static void codeview_snarf_linetab(const struct msc_debug_info* msc_dbg, const B const struct codeview_linetab_block* ltb; nfile = *(const short*)linetab; + if (!nfile) return FALSE; filetab = (const unsigned int*)(linetab + 2 * sizeof(short)); for (i = 0; i < nfile; i++) @@ -1650,11 +1651,12 @@ static void codeview_snarf_linetab(const struct msc_debug_info* msc_dbg, const B } } } + return TRUE; } static const char* pdb_get_string_table_entry(const PDB_STRING_TABLE* table, unsigned offset); -static void codeview_snarf_linetab2(const struct msc_debug_info* msc_dbg, const struct cv_module_snarf* cvmod) +static BOOL codeview_snarf_linetab2(const struct msc_debug_info* msc_dbg, const struct cv_module_snarf* cvmod) { unsigned i; const void* hdr_last = (const char*)cvmod->dbgsubsect + cvmod->dbgsubsect_size; @@ -1680,7 +1682,7 @@ static void codeview_snarf_linetab2(const struct msc_debug_info* msc_dbg, const if (!hdr_files) { TRACE("No DEBUG_S_FILECHKSMS found\n"); - return; + return FALSE; } for (hdr = cvmod->dbgsubsect; CV_IS_INSIDE(hdr, hdr_last); hdr = hdr_next) @@ -1730,6 +1732,7 @@ static void codeview_snarf_linetab2(const struct msc_debug_info* msc_dbg, const } hdr = hdr_next; } + return TRUE; } /*======================================================================== @@ -2837,7 +2840,6 @@ static BOOL codeview_snarf(const struct msc_debug_info* msc_dbg, break; } } - if (cvmod) codeview_snarf_linetab2(msc_dbg, cvmod); return TRUE; } @@ -3595,7 +3597,8 @@ static BOOL pdb_process_internal(const struct process *pcs, const struct msc_debug_info *msc_dbg, const WCHAR *filename, struct pdb_module_info *pdb_module_info, - unsigned module_index); + unsigned module_index, + BOOL *has_linenumber_info); DWORD pdb_get_file_indexinfo(void* image, DWORD size, SYMSRV_INDEX_INFOW* info) { @@ -3730,6 +3733,7 @@ static void pdb_process_symbol_imports(const struct process *pcs, while (imp < (const PDB_SYMBOL_IMPORT*)last) { SYMSRV_INDEX_INFOW info; + BOOL line_info; ptr = (const char*)imp + sizeof(*imp) + strlen(imp->filename); if (i >= CV_MAX_MODULES) FIXME("Out of bounds!!!\n"); @@ -3737,7 +3741,7 @@ static void pdb_process_symbol_imports(const struct process *pcs, debugstr_a(imp->filename), imp->Age, imp->TimeDateStamp); if (path_find_symbol_file(pcs, msc_dbg->module, imp->filename, TRUE, NULL, imp->TimeDateStamp, imp->Age, &info, &msc_dbg->module->module.PdbUnmatched)) - pdb_process_internal(pcs, msc_dbg, info.pdbfile, pdb_module_info, i); + pdb_process_internal(pcs, msc_dbg, info.pdbfile, pdb_module_info, i, &line_info); i++; imp = (const PDB_SYMBOL_IMPORT*)((const char*)first + ((ptr - (const char*)first + strlen(ptr) + 1 + 3) & ~3)); } @@ -3757,7 +3761,8 @@ static BOOL pdb_process_internal(const struct process *pcs, const struct msc_debug_info *msc_dbg, const WCHAR *filename, struct pdb_module_info *pdb_module_info, - unsigned module_index) + unsigned module_index, + BOOL *has_linenumber_info) { HANDLE hFile = NULL, hMap = NULL; char* image = NULL; @@ -3767,6 +3772,8 @@ static BOOL pdb_process_internal(const struct process *pcs, TRACE("Processing PDB file %ls\n", filename); + *has_linenumber_info = FALSE; + pdb_file = &pdb_module_info->pdb_files[module_index == -1 ? 0 : module_index]; /* Open and map() .PDB file */ if ((hFile = CreateFileW(filename, GENERIC_READ, FILE_SHARE_READ, NULL, @@ -3845,9 +3852,10 @@ static BOOL pdb_process_internal(const struct process *pcs, data = pdb_read_stream(pdb_file, symbols.global_hash_stream); if (data) { - codeview_snarf_sym_hashtable(msc_dbg, globalimage, global_size, - data, pdb_get_stream_size(pdb_file, symbols.global_hash_stream), - pdb_global_feed_types); + if (codeview_snarf_sym_hashtable(msc_dbg, globalimage, global_size, + data, pdb_get_stream_size(pdb_file, symbols.global_hash_stream), + pdb_global_feed_types)) + *has_linenumber_info = TRUE; pdb_free((void*)data); } } @@ -3874,11 +3882,18 @@ static BOOL pdb_process_internal(const struct process *pcs, if (sfile.lineno_size && sfile.lineno2_size) FIXME("Both line info present... only supporting second\n"); else if (sfile.lineno_size) - codeview_snarf_linetab(msc_dbg, - modimage + sfile.symbol_size, - sfile.lineno_size, - pdb_file->kind == PDB_JG); - + { + if (codeview_snarf_linetab(msc_dbg, + modimage + sfile.symbol_size, + sfile.lineno_size, + pdb_file->kind == PDB_JG)) + *has_linenumber_info = TRUE; + } + else if (sfile.lineno2_size) + { + if (codeview_snarf_linetab2(msc_dbg, &cvmod)) + *has_linenumber_info = TRUE; + } pdb_free(modimage); } file_name += strlen(file_name) + 1; @@ -3935,7 +3950,7 @@ static BOOL pdb_process_file(const struct process *pcs, (modfmt = HeapAlloc(GetProcessHeap(), 0, sizeof(struct module_format) + sizeof(struct pdb_module_info)))) { - BOOL ret; + BOOL ret, has_linenumber_info; pdb_module_info = (void*)(modfmt + 1); msc_dbg->module->format_info[DFI_PDB] = modfmt; @@ -3947,7 +3962,7 @@ static BOOL pdb_process_file(const struct process *pcs, memset(cv_zmodules, 0, sizeof(cv_zmodules)); codeview_init_basic_types(msc_dbg->module); ret = pdb_process_internal(pcs, msc_dbg, info.pdbfile, - msc_dbg->module->format_info[DFI_PDB]->u.pdb_info, -1); + msc_dbg->module->format_info[DFI_PDB]->u.pdb_info, -1, &has_linenumber_info); codeview_clear_type_table(); if (ret) { @@ -3959,7 +3974,7 @@ static BOOL pdb_process_file(const struct process *pcs, wcscpy(msc_dbg->module->module.LoadedPdbName, info.pdbfile); /* FIXME: we could have a finer grain here */ - msc_dbg->module->module.LineNumbers = TRUE; + msc_dbg->module->module.LineNumbers = has_linenumber_info; msc_dbg->module->module.GlobalSymbols = TRUE; msc_dbg->module->module.TypeInfo = TRUE; msc_dbg->module->module.SourceIndexed = TRUE; @@ -4358,7 +4373,8 @@ static BOOL codeview_process_info(const struct process *pcs, const OMFDirEntry* ent; const OMFDirEntry* prev; const OMFDirEntry* next; - unsigned int i; + unsigned int i; + BOOL has_linenumber_info = FALSE; codeview_init_basic_types(msc_dbg->module); @@ -4407,19 +4423,21 @@ static BOOL codeview_process_info(const struct process *pcs, * FIXME: This is not a general solution! */ if (next && next->iMod == ent->iMod && next->SubSection == sstSrcModule) - codeview_snarf_linetab(msc_dbg, msc_dbg->root + next->lfo, - next->cb, TRUE); + if (codeview_snarf_linetab(msc_dbg, msc_dbg->root + next->lfo, + next->cb, TRUE)) + has_linenumber_info = TRUE; if (prev && prev->iMod == ent->iMod && prev->SubSection == sstSrcModule) - codeview_snarf_linetab(msc_dbg, msc_dbg->root + prev->lfo, - prev->cb, TRUE); + if (codeview_snarf_linetab(msc_dbg, msc_dbg->root + prev->lfo, + prev->cb, TRUE)) + has_linenumber_info = TRUE; } } msc_dbg->module->module.SymType = SymCv; + msc_dbg->module->module.LineNumbers = has_linenumber_info; /* FIXME: we could have a finer grain here */ - msc_dbg->module->module.LineNumbers = TRUE; msc_dbg->module->module.GlobalSymbols = TRUE; msc_dbg->module->module.TypeInfo = TRUE; msc_dbg->module->module.SourceIndexed = TRUE; From e1d74473345a013547d2202be9e0c898b8754c86 Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Mon, 4 Nov 2024 11:19:47 +0100 Subject: [PATCH 232/454] dbghelp: Only load line information when SYMOPT_LOAD_LINE is set. Signed-off-by: Eric Pouech (cherry picked from commit f640ba193708f38b416288c76c5e20dacd7b2bcc) --- dlls/dbghelp/coff.c | 22 ++++++------- dlls/dbghelp/dwarf.c | 2 +- dlls/dbghelp/msc.c | 75 ++++++++++++++++++++++++-------------------- dlls/dbghelp/stabs.c | 29 +++++++++-------- 4 files changed, 68 insertions(+), 60 deletions(-) diff --git a/dlls/dbghelp/coff.c b/dlls/dbghelp/coff.c index 21b4f726dd36..29c75c375e1c 100644 --- a/dlls/dbghelp/coff.c +++ b/dlls/dbghelp/coff.c @@ -161,7 +161,6 @@ BOOL coff_process_info(const struct msc_debug_info* msc_dbg) int linetab_indx; const char* nampnt; int naux; - BOOL ret = FALSE; ULONG64 addr; TRACE("Processing COFF symbols...\n"); @@ -378,7 +377,8 @@ BOOL coff_process_info(const struct msc_debug_info* msc_dbg) i += naux; } - if (coff_files.files != NULL) + if (coff_files.files == NULL) return FALSE; + if (SymGetOptions() & SYMOPT_LOAD_LINES) { /* * OK, we now should have a list of files, and we should have a list @@ -436,15 +436,13 @@ BOOL coff_process_info(const struct msc_debug_info* msc_dbg) HeapFree(GetProcessHeap(), 0, coff_files.files[j].entries); } HeapFree(GetProcessHeap(), 0, coff_files.files); - msc_dbg->module->module.SymType = SymCoff; - /* FIXME: we could have a finer grain here */ - msc_dbg->module->module.LineNumbers = TRUE; - msc_dbg->module->module.GlobalSymbols = TRUE; - msc_dbg->module->module.TypeInfo = FALSE; - msc_dbg->module->module.SourceIndexed = TRUE; - msc_dbg->module->module.Publics = TRUE; - ret = TRUE; } - - return ret; + msc_dbg->module->module.SymType = SymCoff; + + msc_dbg->module->module.LineNumbers = !!(SymGetOptions() & SYMOPT_LOAD_LINES); + msc_dbg->module->module.GlobalSymbols = TRUE; + msc_dbg->module->module.TypeInfo = FALSE; + msc_dbg->module->module.SourceIndexed = TRUE; + msc_dbg->module->module.Publics = TRUE; + return TRUE; } diff --git a/dlls/dbghelp/dwarf.c b/dlls/dbghelp/dwarf.c index 8ba953941f85..c1789182ba38 100644 --- a/dlls/dbghelp/dwarf.c +++ b/dlls/dbghelp/dwarf.c @@ -3074,7 +3074,7 @@ static BOOL dwarf2_parse_compilation_unit(dwarf2_parse_context_t* ctx) child = *(dwarf2_debug_info_t**)vector_at(children, i); dwarf2_load_one_entry(child); } - if (dwarf2_find_attribute(di, DW_AT_stmt_list, &stmt_list)) + if ((SymGetOptions() & SYMOPT_LOAD_LINES) && dwarf2_find_attribute(di, DW_AT_stmt_list, &stmt_list)) { if (dwarf2_parse_line_numbers(ctx, comp_dir.u.string, stmt_list.u.uvalue)) ctx->module_ctx->module->module.LineNumbers = TRUE; diff --git a/dlls/dbghelp/msc.c b/dlls/dbghelp/msc.c index 1352fe145f4b..0b28abf200d9 100644 --- a/dlls/dbghelp/msc.c +++ b/dlls/dbghelp/msc.c @@ -3773,7 +3773,6 @@ static BOOL pdb_process_internal(const struct process *pcs, TRACE("Processing PDB file %ls\n", filename); *has_linenumber_info = FALSE; - pdb_file = &pdb_module_info->pdb_files[module_index == -1 ? 0 : module_index]; /* Open and map() .PDB file */ if ((hFile = CreateFileW(filename, GENERIC_READ, FILE_SHARE_READ, NULL, @@ -3879,21 +3878,25 @@ static BOOL pdb_process_internal(const struct process *pcs, files_image}; codeview_snarf(msc_dbg, modimage, sizeof(DWORD), sfile.symbol_size, &cvmod, file_name); - if (sfile.lineno_size && sfile.lineno2_size) - FIXME("Both line info present... only supporting second\n"); - else if (sfile.lineno_size) - { - if (codeview_snarf_linetab(msc_dbg, - modimage + sfile.symbol_size, - sfile.lineno_size, - pdb_file->kind == PDB_JG)) - *has_linenumber_info = TRUE; - } - else if (sfile.lineno2_size) + if (SymGetOptions() & SYMOPT_LOAD_LINES) { - if (codeview_snarf_linetab2(msc_dbg, &cvmod)) - *has_linenumber_info = TRUE; + if (sfile.lineno_size && sfile.lineno2_size) + FIXME("Both line info present... only supporting second\n"); + else if (sfile.lineno_size) + { + if (codeview_snarf_linetab(msc_dbg, + modimage + sfile.symbol_size, + sfile.lineno_size, + pdb_file->kind == PDB_JG)) + *has_linenumber_info = TRUE; + } + else if (sfile.lineno2_size) + { + if (codeview_snarf_linetab2(msc_dbg, &cvmod)) + *has_linenumber_info = TRUE; + } } + pdb_free(modimage); } file_name += strlen(file_name) + 1; @@ -3909,9 +3912,11 @@ static BOOL pdb_process_internal(const struct process *pcs, data = pdb_read_stream(pdb_file, symbols.global_hash_stream); if (data) { - codeview_snarf_sym_hashtable(msc_dbg, globalimage, global_size, - data, pdb_get_stream_size(pdb_file, symbols.global_hash_stream), - pdb_global_feed_variables); + if (codeview_snarf_sym_hashtable(msc_dbg, globalimage, global_size, + data, pdb_get_stream_size(pdb_file, + symbols.global_hash_stream), + pdb_global_feed_variables)) + *has_linenumber_info = TRUE; pdb_free((void*)data); } if (!(dbghelp_options & SYMOPT_NO_PUBLICS) && (data = pdb_read_stream(pdb_file, symbols.public_stream))) @@ -4415,23 +4420,25 @@ static BOOL codeview_process_info(const struct process *pcs, { codeview_snarf(msc_dbg, msc_dbg->root + ent->lfo, sizeof(DWORD), ent->cb, NULL, NULL); - /* - * Check the next and previous entry. If either is a - * sstSrcModule, it contains the line number info for - * this file. - * - * FIXME: This is not a general solution! - */ - if (next && next->iMod == ent->iMod && next->SubSection == sstSrcModule) - if (codeview_snarf_linetab(msc_dbg, msc_dbg->root + next->lfo, - next->cb, TRUE)) - has_linenumber_info = TRUE; - - if (prev && prev->iMod == ent->iMod && prev->SubSection == sstSrcModule) - if (codeview_snarf_linetab(msc_dbg, msc_dbg->root + prev->lfo, - prev->cb, TRUE)) - has_linenumber_info = TRUE; - + if (SymGetOptions() & SYMOPT_LOAD_LINES) + { + /* + * Check the next and previous entry. If either is a + * sstSrcModule, it contains the line number info for + * this file. + * + * FIXME: This is not a general solution! + */ + if (next && next->iMod == ent->iMod && next->SubSection == sstSrcModule) + if (codeview_snarf_linetab(msc_dbg, msc_dbg->root + next->lfo, + next->cb, TRUE)) + has_linenumber_info = TRUE; + + if (prev && prev->iMod == ent->iMod && prev->SubSection == sstSrcModule) + if (codeview_snarf_linetab(msc_dbg, msc_dbg->root + prev->lfo, + prev->cb, TRUE)) + has_linenumber_info = TRUE; + } } } diff --git a/dlls/dbghelp/stabs.c b/dlls/dbghelp/stabs.c index d02f320e281b..8d9b91eeb4fa 100644 --- a/dlls/dbghelp/stabs.c +++ b/dlls/dbghelp/stabs.c @@ -1493,21 +1493,24 @@ BOOL stabs_parse(struct module* module, ULONG_PTR load_offset, if (curr_func != NULL) pending_add_var(&pending_block, ptr, DataIsLocal, &loc); break; case N_SLINE: - /* - * This is a line number. These are always relative to the start - * of the function (N_FUN), and this makes the lookup easier. - */ - assert(source_idx >= 0); - if (curr_func != NULL) + if (SymGetOptions() & SYMOPT_LOAD_LINES) { - ULONG_PTR offset = n_value; - if (module->type == DMT_MACHO) - offset -= curr_func->ranges[0].low - load_offset; - symt_add_func_line(module, curr_func, source_idx, - stab_ptr->n_desc, curr_func->ranges[0].low + offset); + /* + * This is a line number. These are always relative to the start + * of the function (N_FUN), and this makes the lookup easier. + */ + assert(source_idx >= 0); + if (curr_func != NULL) + { + ULONG_PTR offset = n_value; + if (module->type == DMT_MACHO) + offset -= curr_func->ranges[0].low - load_offset; + symt_add_func_line(module, curr_func, source_idx, + stab_ptr->n_desc, curr_func->ranges[0].low + offset); + } + else pending_add_line(&pending_func, source_idx, stab_ptr->n_desc, + n_value, load_offset); } - else pending_add_line(&pending_func, source_idx, stab_ptr->n_desc, - n_value, load_offset); break; case N_FUN: /* From 610d38c63aca1f89212630592d794c7a2878c9f5 Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Mon, 4 Nov 2024 11:19:59 +0100 Subject: [PATCH 233/454] dbghelp: Fix order of non-commutative binary op:s in PDB/FPO unwinder. Signed-off-by: Eric Pouech (cherry picked from commit fc0e0e8ec6ad6a11563dd290eb18705766954a3b) --- dlls/dbghelp/msc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/dbghelp/msc.c b/dlls/dbghelp/msc.c index 0b28abf200d9..2e6fd07d4e79 100644 --- a/dlls/dbghelp/msc.c +++ b/dlls/dbghelp/msc.c @@ -4163,7 +4163,7 @@ static BOOL pev_binop(struct pevaluator* pev, char op) char res[PEV_MAX_LEN]; DWORD_PTR v1, v2, c; - if (!pev_pop_val(pev, &v1) || !pev_pop_val(pev, &v2)) return FALSE; + if (!pev_pop_val(pev, &v2) || !pev_pop_val(pev, &v1)) return FALSE; if ((op == '/' || op == '%') && v2 == 0) { pev_set_error(pev, "binop: division by zero"); From d74d21bb71098ecf6a629e7407d8980133ca2d16 Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Mon, 4 Nov 2024 11:21:01 +0100 Subject: [PATCH 234/454] dbghelp: Introduce new PDB reader, use it for unwinding FPO frames. Signed-off-by: Eric Pouech (cherry picked from commit cf1256f9a49446f4fdd136bc8c5959b434eec81d) --- dlls/dbghelp/Makefile.in | 1 + dlls/dbghelp/dbghelp_private.h | 8 + dlls/dbghelp/msc.c | 422 +++--------------- dlls/dbghelp/pdb.c | 788 +++++++++++++++++++++++++++++++++ dlls/dbghelp/storage.c | 4 - 5 files changed, 867 insertions(+), 356 deletions(-) create mode 100644 dlls/dbghelp/pdb.c diff --git a/dlls/dbghelp/Makefile.in b/dlls/dbghelp/Makefile.in index 152ef80611a5..dd27e159f182 100644 --- a/dlls/dbghelp/Makefile.in +++ b/dlls/dbghelp/Makefile.in @@ -19,6 +19,7 @@ SOURCES = \ module.c \ msc.c \ path.c \ + pdb.c \ pe_module.c \ source.c \ stabs.c \ diff --git a/dlls/dbghelp/dbghelp_private.h b/dlls/dbghelp/dbghelp_private.h index 90b41691f331..03c0f7be1d7c 100644 --- a/dlls/dbghelp/dbghelp_private.h +++ b/dlls/dbghelp/dbghelp_private.h @@ -985,3 +985,11 @@ extern struct symt_function* #define IFC_DEPTH_MASK 0x3FFFFFFF #define IFC_MODE(x) ((x) & ~IFC_DEPTH_MASK) #define IFC_DEPTH(x) ((x) & IFC_DEPTH_MASK) + +/* temporary helpers for PDB rewriting */ +struct _PDB_FPO_DATA; +extern BOOL pdb_fpo_unwind_parse_cmd_string(struct cpu_stack_walk* csw, struct _PDB_FPO_DATA* fpoext, + const char* cmd, struct pdb_cmd_pair* cpair); +extern BOOL pdb_old_virtual_unwind(struct cpu_stack_walk *csw, DWORD_PTR ip, + union ctx *context, struct pdb_cmd_pair *cpair); +extern BOOL pdb_hack_get_main_info(struct module_format *modfmt, HANDLE *file, unsigned *fpoext_stream); diff --git a/dlls/dbghelp/msc.c b/dlls/dbghelp/msc.c index 2e6fd07d4e79..8acd4e70f3aa 100644 --- a/dlls/dbghelp/msc.c +++ b/dlls/dbghelp/msc.c @@ -37,7 +37,6 @@ #include #include -#include #include "windef.h" #include "winbase.h" #include "winternl.h" @@ -62,6 +61,7 @@ enum pdb_kind {PDB_JG, PDB_DS}; struct pdb_file_info { enum pdb_kind kind; + HANDLE file_handle; HANDLE hMap; const char* image; struct pdb_stream_name* stream_dict; @@ -97,6 +97,18 @@ struct cv_module_snarf const PDB_STRING_TABLE* strimage; }; +BOOL pdb_hack_get_main_info(struct module_format *modfmt, HANDLE *file, unsigned *fpoext_stream) +{ + struct pdb_module_info* pdb_info; + + if (!modfmt) return FALSE; + pdb_info = modfmt->u.pdb_info; + *file = pdb_info->pdb_files[0].file_handle; + if (fpoext_stream) + *fpoext_stream = pdb_info->pdb_files[0].fpoext_stream; + return TRUE; +} + /*======================================================================== * Debug file access helper routines */ @@ -3260,6 +3272,8 @@ static void pdb_module_remove(struct process* pcsn, struct module_format* modfmt UnmapViewOfFile(modfmt->u.pdb_info->pdb_files[i].image); if (modfmt->u.pdb_info->pdb_files[i].hMap) CloseHandle(modfmt->u.pdb_info->pdb_files[i].hMap); + if (modfmt->u.pdb_info->pdb_files[i].file_handle) + CloseHandle(modfmt->u.pdb_info->pdb_files[i].file_handle); } HeapFree(GetProcessHeap(), 0, modfmt); } @@ -3785,13 +3799,21 @@ static BOOL pdb_process_internal(const struct process *pcs, CloseHandle(hFile); return FALSE; } - CloseHandle(hFile); + if (!pdb_init(pdb_file, image)) { + CloseHandle(hFile); CloseHandle(hMap); UnmapViewOfFile(image); return FALSE; } + if (getenv("WINE_DBGHELP_OLD_PDB")) /* keep using old pdb reader */ + { + pdb_file->file_handle = NULL; + CloseHandle(hFile); + } + else + pdb_file->file_handle = hFile; pdb_file->hMap = hMap; pdb_file->image = image; @@ -4000,356 +4022,6 @@ static BOOL pdb_process_file(const struct process *pcs, return FALSE; } -/*======================================================================== - * FPO unwinding code - */ - -/* Stack unwinding is based on postfixed operations. - * Let's define our Postfix EValuator - */ -#define PEV_MAX_LEN 32 -struct pevaluator -{ - struct cpu_stack_walk* csw; - struct pool pool; - struct vector stack; - unsigned stk_index; - struct hash_table values; - char error[64]; -}; - -struct zvalue -{ - DWORD_PTR value; - struct hash_table_elt elt; -}; - -static void pev_set_error(struct pevaluator* pev, const char* msg, ...) __WINE_PRINTF_ATTR(2,3); -static void pev_set_error(struct pevaluator* pev, const char* msg, ...) -{ - va_list args; - - va_start(args, msg); - vsnprintf(pev->error, sizeof(pev->error), msg, args); - va_end(args); -} - -#if 0 -static void pev_dump_stack(struct pevaluator* pev) -{ - unsigned i; - struct hash_table_iter hti; - - FIXME("stack #%d\n", pev->stk_index); - for (i = 0; i < pev->stk_index; i++) - { - FIXME("\t%d) %s\n", i, *(char**)vector_at(&pev->stack, i)); - } - hash_table_iter_init(&pev->values, &hti, str); - FIXME("hash\n"); - while ((ptr = hash_table_iter_up(&hti))) - { - struct zvalue* zval = CONTAINING_RECORD(ptr, struct zvalue, elt); - FIXME("\t%s: Ix\n", zval->elt.name, zval->value); - } - -} -#endif - -/* get the value out of an operand (variable or literal) */ -static BOOL pev_get_val(struct pevaluator* pev, const char* str, DWORD_PTR* val) -{ - char* n; - struct hash_table_iter hti; - void* ptr; - - switch (str[0]) - { - case '$': - case '.': - hash_table_iter_init(&pev->values, &hti, str); - while ((ptr = hash_table_iter_up(&hti))) - { - if (!strcmp(CONTAINING_RECORD(ptr, struct zvalue, elt)->elt.name, str)) - { - *val = CONTAINING_RECORD(ptr, struct zvalue, elt)->value; - return TRUE; - } - } - pev_set_error(pev, "get_zvalue: no value found (%s)", str); - return FALSE; - default: - *val = strtol(str, &n, 10); - if (n != str && *n == '\0') return TRUE; - pev_set_error(pev, "get_val: not a literal (%s)", str); - return FALSE; - } -} - -/* push an operand onto the stack */ -static BOOL pev_push(struct pevaluator* pev, const char* elt) -{ - char** at; - if (pev->stk_index < vector_length(&pev->stack)) - at = vector_at(&pev->stack, pev->stk_index); - else - at = vector_add(&pev->stack, &pev->pool); - if (!at) - { - pev_set_error(pev, "push: out of memory"); - return FALSE; - } - *at = pool_strdup(&pev->pool, elt); - pev->stk_index++; - return TRUE; -} - -/* pop an operand from the stack */ -static BOOL pev_pop(struct pevaluator* pev, char* elt) -{ - char** at = vector_at(&pev->stack, --pev->stk_index); - if (!at) - { - pev_set_error(pev, "pop: stack empty"); - return FALSE; - } - strcpy(elt, *at); - return TRUE; -} - -/* pop an operand from the stack, and gets its value */ -static BOOL pev_pop_val(struct pevaluator* pev, DWORD_PTR* val) -{ - char p[PEV_MAX_LEN]; - - return pev_pop(pev, p) && pev_get_val(pev, p, val); -} - -/* set var 'name' a new value (creates the var if it doesn't exist) */ -static BOOL pev_set_value(struct pevaluator* pev, const char* name, DWORD_PTR val) -{ - struct hash_table_iter hti; - void* ptr; - - hash_table_iter_init(&pev->values, &hti, name); - while ((ptr = hash_table_iter_up(&hti))) - { - if (!strcmp(CONTAINING_RECORD(ptr, struct zvalue, elt)->elt.name, name)) - { - CONTAINING_RECORD(ptr, struct zvalue, elt)->value = val; - break; - } - } - if (!ptr) - { - struct zvalue* zv = pool_alloc(&pev->pool, sizeof(*zv)); - if (!zv) - { - pev_set_error(pev, "set_value: out of memory"); - return FALSE; - } - zv->value = val; - - zv->elt.name = pool_strdup(&pev->pool, name); - hash_table_add(&pev->values, &zv->elt); - } - return TRUE; -} - -/* execute a binary operand from the two top most values on the stack. - * puts result on top of the stack */ -static BOOL pev_binop(struct pevaluator* pev, char op) -{ - char res[PEV_MAX_LEN]; - DWORD_PTR v1, v2, c; - - if (!pev_pop_val(pev, &v2) || !pev_pop_val(pev, &v1)) return FALSE; - if ((op == '/' || op == '%') && v2 == 0) - { - pev_set_error(pev, "binop: division by zero"); - return FALSE; - } - switch (op) - { - case '+': c = v1 + v2; break; - case '-': c = v1 - v2; break; - case '*': c = v1 * v2; break; - case '/': c = v1 / v2; break; - case '%': c = v1 % v2; break; - default: - pev_set_error(pev, "binop: unknown op (%c)", op); - return FALSE; - } - snprintf(res, sizeof(res), "%Id", c); - pev_push(pev, res); - return TRUE; -} - -/* pops top most operand, dereference it, on pushes the result on top of the stack */ -static BOOL pev_deref(struct pevaluator* pev) -{ - char res[PEV_MAX_LEN]; - DWORD_PTR v1, v2 = 0; - - if (!pev_pop_val(pev, &v1)) return FALSE; - if (!sw_read_mem(pev->csw, v1, &v2, pev->csw->cpu->word_size)) - { - pev_set_error(pev, "deref: cannot read mem at %Ix", v1); - return FALSE; - } - snprintf(res, sizeof(res), "%Id", v2); - pev_push(pev, res); - return TRUE; -} - -/* assign value to variable (from two top most operands) */ -static BOOL pev_assign(struct pevaluator* pev) -{ - char p2[PEV_MAX_LEN]; - DWORD_PTR v1; - - if (!pev_pop_val(pev, &v1) || !pev_pop(pev, p2)) return FALSE; - if (p2[0] != '$') - { - pev_set_error(pev, "assign: %s isn't a variable", p2); - return FALSE; - } - pev_set_value(pev, p2, v1); - - return TRUE; -} - -/* initializes the postfix evaluator */ -static void pev_init(struct pevaluator* pev, struct cpu_stack_walk* csw, - const PDB_FPO_DATA* fpoext, struct pdb_cmd_pair* cpair) -{ - pev->csw = csw; - pool_init(&pev->pool, 512); - vector_init(&pev->stack, sizeof(char*), 0); - pev->stk_index = 0; - hash_table_init(&pev->pool, &pev->values, 8); - pev->error[0] = '\0'; - for (; cpair->name; cpair++) - pev_set_value(pev, cpair->name, *cpair->pvalue); - pev_set_value(pev, ".raSearchStart", fpoext->start); - pev_set_value(pev, ".cbLocals", fpoext->locals_size); - pev_set_value(pev, ".cbParams", fpoext->params_size); - pev_set_value(pev, ".cbSavedRegs", fpoext->savedregs_size); -} - -static BOOL pev_free(struct pevaluator* pev, struct pdb_cmd_pair* cpair) -{ - DWORD_PTR val; - - if (cpair) for (; cpair->name; cpair++) - { - if (pev_get_val(pev, cpair->name, &val)) - *cpair->pvalue = val; - } - pool_destroy(&pev->pool); - return TRUE; -} - -static BOOL pdb_parse_cmd_string(struct cpu_stack_walk* csw, PDB_FPO_DATA* fpoext, - const char* cmd, struct pdb_cmd_pair* cpair) -{ - char token[PEV_MAX_LEN]; - char* ptok = token; - const char* ptr; - BOOL over = FALSE; - struct pevaluator pev; - - if (!cmd) return FALSE; - pev_init(&pev, csw, fpoext, cpair); - for (ptr = cmd; !over; ptr++) - { - if (*ptr == ' ' || (over = *ptr == '\0')) - { - *ptok = '\0'; - - if (!strcmp(token, "+") || !strcmp(token, "-") || !strcmp(token, "*") || - !strcmp(token, "/") || !strcmp(token, "%")) - { - if (!pev_binop(&pev, token[0])) goto done; - } - else if (!strcmp(token, "^")) - { - if (!pev_deref(&pev)) goto done; - } - else if (!strcmp(token, "=")) - { - if (!pev_assign(&pev)) goto done; - } - else - { - if (!pev_push(&pev, token)) goto done; - } - ptok = token; - } - else - { - if (ptok - token >= PEV_MAX_LEN - 1) - { - pev_set_error(&pev, "parse: token too long (%s)", ptr - (ptok - token)); - goto done; - } - *ptok++ = *ptr; - } - } - pev_free(&pev, cpair); - return TRUE; -done: - FIXME("Couldn't evaluate %s => %s\n", debugstr_a(cmd), pev.error); - pev_free(&pev, NULL); - return FALSE; -} - -BOOL pdb_virtual_unwind(struct cpu_stack_walk *csw, DWORD_PTR ip, - union ctx *context, struct pdb_cmd_pair *cpair) -{ - struct module_pair pair; - struct pdb_module_info* pdb_info; - PDB_FPO_DATA* fpoext; - unsigned i, size; - PDB_STRING_TABLE* strbase; - BOOL ret = TRUE; - - if (!module_init_pair(&pair, csw->hProcess, ip)) return FALSE; - if (!pair.effective->format_info[DFI_PDB]) return FALSE; - pdb_info = pair.effective->format_info[DFI_PDB]->u.pdb_info; - TRACE("searching %Ix => %Ix\n", ip, ip - (DWORD_PTR)pair.effective->module.BaseOfImage); - ip -= (DWORD_PTR)pair.effective->module.BaseOfImage; - - strbase = pdb_read_strings(&pdb_info->pdb_files[0]); - if (!strbase) return FALSE; - fpoext = pdb_read_stream(&pdb_info->pdb_files[0], pdb_info->pdb_files[0].fpoext_stream); - size = pdb_get_stream_size(&pdb_info->pdb_files[0], pdb_info->pdb_files[0].fpoext_stream); - if (fpoext && (size % sizeof(*fpoext)) == 0) - { - size /= sizeof(*fpoext); - for (i = 0; i < size; i++) - { - if (fpoext[i].start <= ip && ip < fpoext[i].start + fpoext[i].func_size) - { - TRACE("\t%08x %08x %8x %8x %4x %4x %4x %08x %s\n", - fpoext[i].start, fpoext[i].func_size, fpoext[i].locals_size, - fpoext[i].params_size, fpoext[i].maxstack_size, fpoext[i].prolog_size, - fpoext[i].savedregs_size, fpoext[i].flags, - debugstr_a(pdb_get_string_table_entry(strbase, fpoext[i].str_offset))); - ret = pdb_parse_cmd_string(csw, &fpoext[i], - pdb_get_string_table_entry(strbase, fpoext[i].str_offset), - cpair); - break; - } - } - } - else ret = FALSE; - pdb_free(fpoext); - pdb_free(strbase); - - return ret; -} - /*======================================================================== * Process CodeView debug information. */ @@ -4675,3 +4347,49 @@ DWORD dbg_get_file_indexinfo(void* image, DWORD size, SYMSRV_INDEX_INFOW* info) return msc_get_file_indexinfo(image, dbg, num_directories, info); } + +BOOL pdb_old_virtual_unwind(struct cpu_stack_walk *csw, DWORD_PTR ip, + union ctx *context, struct pdb_cmd_pair *cpair) +{ + struct module_pair pair; + struct pdb_module_info* pdb_info; + PDB_FPO_DATA* fpoext; + unsigned i, size; + PDB_STRING_TABLE* strbase; + BOOL ret = TRUE; + + if (!module_init_pair(&pair, csw->hProcess, ip)) return FALSE; + if (!pair.effective->format_info[DFI_PDB]) return FALSE; + pdb_info = pair.effective->format_info[DFI_PDB]->u.pdb_info; + TRACE("searching %Ix => %Ix\n", ip, ip - (DWORD_PTR)pair.effective->module.BaseOfImage); + ip -= (DWORD_PTR)pair.effective->module.BaseOfImage; + + strbase = pdb_read_strings(&pdb_info->pdb_files[0]); + if (!strbase) return FALSE; + fpoext = pdb_read_stream(&pdb_info->pdb_files[0], pdb_info->pdb_files[0].fpoext_stream); + size = pdb_get_stream_size(&pdb_info->pdb_files[0], pdb_info->pdb_files[0].fpoext_stream); + if (fpoext && (size % sizeof(*fpoext)) == 0) + { + size /= sizeof(*fpoext); + for (i = 0; i < size; i++) + { + if (fpoext[i].start <= ip && ip < fpoext[i].start + fpoext[i].func_size) + { + TRACE("\t%08x %08x %8x %8x %4x %4x %4x %08x %s\n", + fpoext[i].start, fpoext[i].func_size, fpoext[i].locals_size, + fpoext[i].params_size, fpoext[i].maxstack_size, fpoext[i].prolog_size, + fpoext[i].savedregs_size, fpoext[i].flags, + debugstr_a(pdb_get_string_table_entry(strbase, fpoext[i].str_offset))); + ret = pdb_fpo_unwind_parse_cmd_string(csw, &fpoext[i], + pdb_get_string_table_entry(strbase, fpoext[i].str_offset), + cpair); + break; + } + } + } + else ret = FALSE; + pdb_free(fpoext); + pdb_free(strbase); + + return ret; +} diff --git a/dlls/dbghelp/pdb.c b/dlls/dbghelp/pdb.c new file mode 100644 index 000000000000..e99b96de3093 --- /dev/null +++ b/dlls/dbghelp/pdb.c @@ -0,0 +1,788 @@ +/* + * File pdb.c - read debug information out of PDB files. + * + * Copyright (C) 1996, Eric Youngdale. + * Copyright (C) 1999-2000, Ulrich Weigand. + * Copyright (C) 2004-2009, Eric Pouech. + * Copyright (C) 2004-2025, Eric Pouech for CodeWeavers. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include +#include +#include +#include +#include + +#include "windef.h" +#include "winbase.h" +#include "winternl.h" + +#include "wine/exception.h" +#include "wine/debug.h" +#include "dbghelp_private.h" +#include "wine/mscvpdb.h" + +WINE_DEFAULT_DEBUG_CHANNEL(dbghelp_pdb); + +/* Note: this file contains the new implementation for reading PDB files. + * msc.c contains the old implementation. + */ + +/*======================================================================== + * PDB reader. + * Design goal: + * - maximize on-the-fly operations (doesn't use dbghelp internal representation) + * - limit loaded and cached memory size + * Limitations: + * - doesn't support old JG format (could be added, but to be proven worthwile) + */ + +/* Note: + * - we use integer with known size to replicate serialized data inside the PDB file + * and plain integers for the rest of the code. + * - except for file offset and stream offsets + * - functions prefixed with pdb_reader_internal are for internal helpers and shouldn't be used + */ + +/* some internal types */ +typedef uint64_t pdboff_t; /* offset in whole PDB file (64bit) */ +typedef uint32_t pdbsize_t; /* size inside a stream (including offset from beg of stream) (2G max) */ + +struct pdb_reader; +typedef enum pdb_result (*pdb_reader_fetch_t)(struct pdb_reader *pdb, void *buffer, pdboff_t offset, pdbsize_t size); + +struct pdb_reader +{ + HANDLE file; + HANDLE heap; + + /* header */ + unsigned block_size; + struct PDB_DS_TOC *toc; + + /* stream information */ + struct + { + const uint32_t *blocks; /* points into toc */ + char *name; + } *streams; + char *stream_names; + + pdb_reader_fetch_t fetch; +}; + +enum pdb_result +{ + R_PDB_SUCCESS, + R_PDB_IOERROR, + R_PDB_OUT_OF_MEMORY, + R_PDB_INVALID_ARGUMENT, + R_PDB_INVALID_PDB_FILE, + R_PDB_BUFFER_TOO_SMALL, +}; + +static enum pdb_result pdb_reader_fetch_file(struct pdb_reader *pdb, void *buffer, pdboff_t offset, pdbsize_t size) +{ + OVERLAPPED ov = {.Offset = offset, .OffsetHigh = offset >> 32, .hEvent = (HANDLE)(DWORD_PTR)1}; + DWORD num_read; + + return ReadFile(pdb->file, buffer, size, &num_read, &ov) && num_read == size ? R_PDB_SUCCESS : R_PDB_IOERROR; +} + +static const char PDB_JG_IDENT[] = "Microsoft C/C++ program database 2.00\r\n\032JG\0"; +static const char PDB_DS_IDENT[] = "Microsoft C/C++ MSF 7.00\r\n\032DS\0"; + +static inline enum pdb_result pdb_reader_alloc(struct pdb_reader *pdb, size_t size, void **ptr) +{ + return (*ptr = HeapAlloc(pdb->heap, 0, size)) ? R_PDB_SUCCESS : R_PDB_OUT_OF_MEMORY; +} + +static inline enum pdb_result pdb_reader_realloc(struct pdb_reader *pdb, void **ptr, size_t size) +{ + void *new = HeapReAlloc(pdb->heap, 0, *ptr, size); + if (!new) return R_PDB_OUT_OF_MEMORY; + *ptr = new; + return R_PDB_SUCCESS; +} + +static inline void pdb_reader_free(struct pdb_reader *pdb, void *ptr) +{ + HeapFree(pdb->heap, 0, ptr); +} + +static inline unsigned pdb_reader_num_blocks(struct pdb_reader *pdb, pdbsize_t size) +{ + return (size + pdb->block_size - 1) / pdb->block_size; +} + +static enum pdb_result pdb_reader_get_stream_size(struct pdb_reader *pdb, unsigned stream_id, pdbsize_t *size) +{ + if (stream_id >= pdb->toc->num_streams) return R_PDB_INVALID_ARGUMENT; + *size = pdb->toc->stream_size[stream_id]; + return R_PDB_SUCCESS; +} + +static enum pdb_result pdb_reader_internal_read_from_blocks(struct pdb_reader *pdb, const uint32_t *blocks, pdbsize_t delta, + void *buffer, pdbsize_t size, pdbsize_t *num_read) +{ + enum pdb_result result; + pdbsize_t initial_size = size; + pdbsize_t toread; + + while (size) + { + toread = min(pdb->block_size - delta, size); + + if ((result = (*pdb->fetch)(pdb, buffer, (pdboff_t)*blocks * pdb->block_size + delta, toread))) + return result; + size -= toread; + blocks++; + buffer = (char*)buffer + toread; + delta = 0; + } + + if (num_read) *num_read = initial_size - size; + return size != initial_size ? R_PDB_SUCCESS : R_PDB_INVALID_ARGUMENT; +} + +struct pdb_reader_walker +{ + unsigned stream_id; + pdbsize_t offset; + pdbsize_t last; +}; + +static inline enum pdb_result pdb_reader_walker_init(struct pdb_reader *pdb, unsigned stream_id, struct pdb_reader_walker *walker) +{ + walker->stream_id = stream_id; + walker->offset = 0; + return pdb_reader_get_stream_size(pdb, stream_id, &walker->last); +} + +static enum pdb_result pdb_reader_read_from_stream(struct pdb_reader *pdb, const struct pdb_reader_walker *walker, + void *buffer, pdbsize_t size, pdbsize_t *num_read) +{ + enum pdb_result result; + const uint32_t *blocks; + pdbsize_t delta; + + if (walker->stream_id >= pdb->toc->num_streams) return R_PDB_INVALID_ARGUMENT; + if (walker->offset >= pdb->toc->stream_size[walker->stream_id]) return R_PDB_INVALID_ARGUMENT; + if (walker->offset >= walker->last) return R_PDB_INVALID_ARGUMENT; + blocks = pdb->streams[walker->stream_id].blocks + walker->offset / pdb->block_size; + + if (walker->offset + size > pdb->toc->stream_size[walker->stream_id]) + { + size = pdb->toc->stream_size[walker->stream_id] - walker->offset; + } + if (walker->offset + size > walker->last) + { + size = walker->last - walker->offset; + } + delta = walker->offset % pdb->block_size; + + if ((result = pdb_reader_internal_read_from_blocks(pdb, blocks, delta, buffer, size, num_read))) + return result; + return R_PDB_SUCCESS; +} + +static enum pdb_result pdb_reader_init(struct pdb_reader *pdb, HANDLE file, HANDLE heap) +{ + enum pdb_result result; + struct PDB_DS_HEADER hdr; + struct PDB_DS_TOC *toc = NULL; + unsigned i; + unsigned toc_blocks_size; + uint32_t *toc_blocks = NULL; + uint32_t *blocks; + + memset(pdb, 0, sizeof(*pdb)); + pdb->file = file; + pdb->heap = heap; + pdb->fetch = &pdb_reader_fetch_file; + + if ((result = (*pdb->fetch)(pdb, &hdr, 0, sizeof(hdr)))) return result; + if (!memcmp(hdr.signature, PDB_JG_IDENT, sizeof(PDB_JG_IDENT))) + { + FIXME("PDB reader doesn't support old PDB JG file format\n"); + return R_PDB_INVALID_PDB_FILE; + } + if (memcmp(hdr.signature, PDB_DS_IDENT, sizeof(PDB_DS_IDENT))) + { + ERR("PDB reader doesn't recognize format (%s)\n", wine_dbgstr_a((char*)&hdr)); + return R_PDB_INVALID_PDB_FILE; + } + pdb->block_size = hdr.block_size; + toc_blocks_size = pdb_reader_num_blocks(pdb, hdr.toc_size) * sizeof(uint32_t); + if ((result = pdb_reader_alloc(pdb, toc_blocks_size, (void**)&toc_blocks)) || + (result = (*pdb->fetch)(pdb, toc_blocks, (pdboff_t)hdr.toc_block * hdr.block_size, toc_blocks_size)) || + (result = pdb_reader_alloc(pdb, hdr.toc_size, (void**)&toc)) || + (result = pdb_reader_internal_read_from_blocks(pdb, toc_blocks, 0, toc, hdr.toc_size, NULL)) || + (result = pdb_reader_alloc(pdb, toc->num_streams * sizeof(pdb->streams[0]), (void**)&pdb->streams))) + goto failure; + pdb_reader_free(pdb, toc_blocks); + + pdb->toc = toc; + blocks = &toc->stream_size[toc->num_streams]; + for (i = 0; i < pdb->toc->num_streams; i++) + { + if (toc->stream_size[i] == 0 || toc->stream_size[i] == ~0u) + { + pdb->streams[i].blocks = NULL; + pdb->toc->stream_size[i] = 0; + } + else + { + pdb->streams[i].blocks = blocks; + blocks += pdb_reader_num_blocks(pdb, toc->stream_size[i]); + } + pdb->streams[i].name = NULL; + } + return R_PDB_SUCCESS; + +failure: + WARN("Failed to load PDB header\n"); + pdb_reader_free(pdb, toc); + pdb_reader_free(pdb, toc_blocks); + return result; +} + +static void pdb_reader_dispose(struct pdb_reader *pdb) +{ + unsigned i; + + for (i = 0; i < pdb->toc->num_streams; i++) + pdb_reader_free(pdb, pdb->streams[i].name); + pdb_reader_free(pdb, pdb->streams); + pdb_reader_free(pdb, pdb->toc); +} + +static enum pdb_result pdb_reader_internal_read_advance(struct pdb_reader *pdb, struct pdb_reader_walker *walker, + void *buffer, pdbsize_t size) +{ + pdbsize_t num_read; + enum pdb_result result = pdb_reader_read_from_stream(pdb, walker, buffer, size, &num_read); + if (result) return result; + if (num_read != size) return R_PDB_INVALID_ARGUMENT; + walker->offset += size; + return R_PDB_SUCCESS; +} +/* Handy macro to deserialize: ensure that read length is the type length, and advance offset in case of success */ +#define pdb_reader_READ(pdb, walker, ptr) pdb_reader_internal_read_advance((pdb), (walker), (ptr), sizeof(*(ptr))) + +static enum pdb_result pdb_reader_alloc_and_read(struct pdb_reader *pdb, struct pdb_reader_walker *walker, + pdbsize_t size, void **buffer) +{ + enum pdb_result result; + + if (walker->offset + size > walker->last) return R_PDB_INVALID_ARGUMENT; + if ((result = pdb_reader_alloc(pdb, size, buffer))) return result; + if ((result = pdb_reader_internal_read_advance(pdb, walker, *buffer, size))) + { + pdb_reader_free(pdb, *buffer); + *buffer = NULL; + return result; + } + return R_PDB_SUCCESS; +} + + +static enum pdb_result pdb_reader_fetch_string_from_stream(struct pdb_reader *pdb, struct pdb_reader_walker *walker, char *buffer, pdbsize_t length) +{ + enum pdb_result result; + pdbsize_t num_read; + char *zero; + + if ((result = pdb_reader_read_from_stream(pdb, walker, buffer, length, &num_read))) + return result; + if (!(zero = memchr(buffer, '\0', num_read))) + return num_read == length ? R_PDB_BUFFER_TOO_SMALL : R_PDB_INVALID_ARGUMENT; + walker->offset += zero - buffer + 1; + return R_PDB_SUCCESS; +} + +static enum pdb_result pdb_reader_load_stream_name_table(struct pdb_reader *pdb) +{ + struct pdb_reader_walker walker; + struct pdb_reader_walker ok_bits_walker; + struct PDB_DS_ROOT ds_root; + enum pdb_result result; + uint32_t count, numok, len, bitfield; + unsigned i; + + if ((result = pdb_reader_walker_init(pdb, 1, &walker)) || + (result = pdb_reader_READ(pdb, &walker, &ds_root))) + return result; + + if (ds_root.Version != 20000404) + { + ERR("-Unknown root block version %u\n", ds_root.Version); + return R_PDB_INVALID_ARGUMENT; + } + + if ((result = pdb_reader_alloc_and_read(pdb, &walker, ds_root.cbNames, (void**)&pdb->stream_names))) + goto failure; + + if ((result = pdb_reader_READ(pdb, &walker, &numok)) || + (result = pdb_reader_READ(pdb, &walker, &count))) + goto failure; + + /* account bitfield vector */ + if ((result = pdb_reader_READ(pdb, &walker, &len))) goto failure; + ok_bits_walker = walker; + walker.offset += len * sizeof(uint32_t); + + /* skip deleted vector */ + if ((result = pdb_reader_READ(pdb, &walker, &len))) goto failure; + walker.offset += len * sizeof(uint32_t); + + for (i = 0; i < count; i++) + { + if ((i & 31u) == 0) + { + if ((result = pdb_reader_READ(pdb, &ok_bits_walker, &bitfield))) + goto failure; + } + if (bitfield & (1u << (i & 31))) + { + uint32_t str_offset, stream_id; + if ((result = pdb_reader_READ(pdb, &walker, &str_offset)) || + (result = pdb_reader_READ(pdb, &walker, &stream_id))) + goto failure; + pdb->streams[stream_id].name = pdb->stream_names + str_offset; + } + } + return R_PDB_SUCCESS; +failure: + pdb_reader_free(pdb, pdb->stream_names); + pdb->stream_names = NULL; + return result; +} + +static enum pdb_result pdb_reader_get_stream_index_from_name(struct pdb_reader *pdb, const char *name, + unsigned *stream_id) +{ + unsigned i; + + if (!pdb->stream_names) + { + enum pdb_result result = pdb_reader_load_stream_name_table(pdb); + if (result) return result; + } + for (i = 0; i < pdb->toc->num_streams; i++) + if (pdb->streams[i].name && !strcmp(pdb->streams[i].name, name)) + { + *stream_id = i; + return R_PDB_SUCCESS; + } + return R_PDB_INVALID_ARGUMENT; +} + +static enum pdb_result pdb_reader_alloc_and_fetch_string(struct pdb_reader *pdb, struct pdb_reader_walker *walker, char **string) +{ + static const unsigned int alloc_step = 256; + enum pdb_result result; + unsigned len = alloc_step; + char *buffer; + + /* get string by chunks of alloc_step bytes... */ + /* Note: we never shrink the alloc buffer to its optimal size */ + for (buffer = NULL;; len += alloc_step) + { + if ((result = pdb_reader_realloc(pdb, (void**)&buffer, len))) + { + pdb_reader_free(pdb, buffer); + return result; + } + result = pdb_reader_fetch_string_from_stream(pdb, walker, buffer + len - alloc_step, alloc_step); + if (result != R_PDB_BUFFER_TOO_SMALL) + { + if (result == R_PDB_SUCCESS) + *string = buffer; + else + pdb_reader_free(pdb, buffer); + return result; + } + walker->offset += alloc_step; + } +} + +static enum pdb_result pdb_reader_alloc_and_fetch_global_string(struct pdb_reader *pdb, pdbsize_t str_offset, char **buffer) +{ + enum pdb_result result; + struct pdb_reader_walker walker; + unsigned stream_id; + + if ((result = pdb_reader_get_stream_index_from_name(pdb, "/names", &stream_id))) + return result; + if ((result = pdb_reader_walker_init(pdb, stream_id, &walker))) return result; + walker.offset = sizeof(PDB_STRING_TABLE) + str_offset; + return pdb_reader_alloc_and_fetch_string(pdb, &walker, buffer); +} + +/*======================================================================== + * FPO unwinding code + */ + +/* Stack unwinding is based on postfixed operations. + * Let's define our Postfix EValuator + */ +#define PEV_MAX_LEN 32 +struct pevaluator +{ + struct cpu_stack_walk* csw; + struct pool pool; + struct vector stack; + unsigned stk_index; + struct hash_table values; + char error[64]; +}; + +struct zvalue +{ + DWORD_PTR value; + struct hash_table_elt elt; +}; + +static void pev_set_error(struct pevaluator* pev, const char* msg, ...) __WINE_PRINTF_ATTR(2,3); +static void pev_set_error(struct pevaluator* pev, const char* msg, ...) +{ + va_list args; + + va_start(args, msg); + vsnprintf(pev->error, sizeof(pev->error), msg, args); + va_end(args); +} + +#if 0 +static void pev_dump_stack(struct pevaluator* pev) +{ + unsigned i; + struct hash_table_iter hti; + + FIXME("stack #%d\n", pev->stk_index); + for (i = 0; i < pev->stk_index; i++) + { + FIXME("\t%d) %s\n", i, *(char**)vector_at(&pev->stack, i)); + } + hash_table_iter_init(&pev->values, &hti, str); + FIXME("hash\n"); + while ((ptr = hash_table_iter_up(&hti))) + { + struct zvalue* zval = CONTAINING_RECORD(ptr, struct zvalue, elt); + FIXME("\t%s: Ix\n", zval->elt.name, zval->value); + } + +} +#endif + +/* get the value out of an operand (variable or literal) */ +static BOOL pev_get_val(struct pevaluator* pev, const char* str, DWORD_PTR* val) +{ + char* n; + struct hash_table_iter hti; + void* ptr; + + switch (str[0]) + { + case '$': + case '.': + hash_table_iter_init(&pev->values, &hti, str); + while ((ptr = hash_table_iter_up(&hti))) + { + if (!strcmp(CONTAINING_RECORD(ptr, struct zvalue, elt)->elt.name, str)) + { + *val = CONTAINING_RECORD(ptr, struct zvalue, elt)->value; + return TRUE; + } + } + pev_set_error(pev, "get_zvalue: no value found (%s)", str); + return FALSE; + default: + *val = strtol(str, &n, 10); + if (n != str && *n == '\0') return TRUE; + pev_set_error(pev, "get_val: not a literal (%s)", str); + return FALSE; + } +} + +/* push an operand onto the stack */ +static BOOL pev_push(struct pevaluator* pev, const char* elt) +{ + char** at; + if (pev->stk_index < vector_length(&pev->stack)) + at = vector_at(&pev->stack, pev->stk_index); + else + at = vector_add(&pev->stack, &pev->pool); + if (!at) + { + pev_set_error(pev, "push: out of memory"); + return FALSE; + } + *at = pool_strdup(&pev->pool, elt); + pev->stk_index++; + return TRUE; +} + +/* pop an operand from the stack */ +static BOOL pev_pop(struct pevaluator* pev, char* elt) +{ + char** at = vector_at(&pev->stack, --pev->stk_index); + if (!at) + { + pev_set_error(pev, "pop: stack empty"); + return FALSE; + } + strcpy(elt, *at); + return TRUE; +} + +/* pop an operand from the stack, and gets its value */ +static BOOL pev_pop_val(struct pevaluator* pev, DWORD_PTR* val) +{ + char p[PEV_MAX_LEN]; + + return pev_pop(pev, p) && pev_get_val(pev, p, val); +} + +/* set var 'name' a new value (creates the var if it doesn't exist) */ +static BOOL pev_set_value(struct pevaluator* pev, const char* name, DWORD_PTR val) +{ + struct hash_table_iter hti; + void* ptr; + + hash_table_iter_init(&pev->values, &hti, name); + while ((ptr = hash_table_iter_up(&hti))) + { + if (!strcmp(CONTAINING_RECORD(ptr, struct zvalue, elt)->elt.name, name)) + { + CONTAINING_RECORD(ptr, struct zvalue, elt)->value = val; + break; + } + } + if (!ptr) + { + struct zvalue* zv = pool_alloc(&pev->pool, sizeof(*zv)); + if (!zv) + { + pev_set_error(pev, "set_value: out of memory"); + return FALSE; + } + zv->value = val; + + zv->elt.name = pool_strdup(&pev->pool, name); + hash_table_add(&pev->values, &zv->elt); + } + return TRUE; +} + +/* execute a binary operand from the two top most values on the stack. + * puts result on top of the stack */ +static BOOL pev_binop(struct pevaluator* pev, char op) +{ + char res[PEV_MAX_LEN]; + DWORD_PTR v1, v2, c; + + if (!pev_pop_val(pev, &v2) || !pev_pop_val(pev, &v1)) return FALSE; + if ((op == '/' || op == '%') && v2 == 0) + { + pev_set_error(pev, "binop: division by zero"); + return FALSE; + } + switch (op) + { + case '+': c = v1 + v2; break; + case '-': c = v1 - v2; break; + case '*': c = v1 * v2; break; + case '/': c = v1 / v2; break; + case '%': c = v1 % v2; break; + default: + pev_set_error(pev, "binop: unknown op (%c)", op); + return FALSE; + } + snprintf(res, sizeof(res), "%Id", c); + pev_push(pev, res); + return TRUE; +} + +/* pops top most operand, dereference it, on pushes the result on top of the stack */ +static BOOL pev_deref(struct pevaluator* pev) +{ + char res[PEV_MAX_LEN]; + DWORD_PTR v1, v2 = 0; + + if (!pev_pop_val(pev, &v1)) return FALSE; + if (!sw_read_mem(pev->csw, v1, &v2, pev->csw->cpu->word_size)) + { + pev_set_error(pev, "deref: cannot read mem at %Ix", v1); + return FALSE; + } + snprintf(res, sizeof(res), "%Id", v2); + pev_push(pev, res); + return TRUE; +} + +/* assign value to variable (from two top most operands) */ +static BOOL pev_assign(struct pevaluator* pev) +{ + char p2[PEV_MAX_LEN]; + DWORD_PTR v1; + + if (!pev_pop_val(pev, &v1) || !pev_pop(pev, p2)) return FALSE; + if (p2[0] != '$') + { + pev_set_error(pev, "assign: %s isn't a variable", p2); + return FALSE; + } + pev_set_value(pev, p2, v1); + + return TRUE; +} + +/* initializes the postfix evaluator */ +static void pev_init(struct pevaluator* pev, struct cpu_stack_walk* csw, + const PDB_FPO_DATA* fpoext, struct pdb_cmd_pair* cpair) +{ + pev->csw = csw; + pool_init(&pev->pool, 512); + vector_init(&pev->stack, sizeof(char*), 0); + pev->stk_index = 0; + hash_table_init(&pev->pool, &pev->values, 8); + pev->error[0] = '\0'; + for (; cpair->name; cpair++) + pev_set_value(pev, cpair->name, *cpair->pvalue); + pev_set_value(pev, ".raSearchStart", fpoext->start); + pev_set_value(pev, ".cbLocals", fpoext->locals_size); + pev_set_value(pev, ".cbParams", fpoext->params_size); + pev_set_value(pev, ".cbSavedRegs", fpoext->savedregs_size); +} + +static BOOL pev_free(struct pevaluator* pev, struct pdb_cmd_pair* cpair) +{ + DWORD_PTR val; + + if (cpair) for (; cpair->name; cpair++) + { + if (pev_get_val(pev, cpair->name, &val)) + *cpair->pvalue = val; + } + pool_destroy(&pev->pool); + return TRUE; +} + +BOOL pdb_fpo_unwind_parse_cmd_string(struct cpu_stack_walk* csw, PDB_FPO_DATA* fpoext, + const char* cmd, struct pdb_cmd_pair* cpair) +{ + char token[PEV_MAX_LEN]; + char* ptok = token; + const char* ptr; + BOOL over = FALSE; + struct pevaluator pev; + + if (!cmd) return FALSE; + pev_init(&pev, csw, fpoext, cpair); + for (ptr = cmd; !over; ptr++) + { + if (*ptr == ' ' || (over = *ptr == '\0')) + { + *ptok = '\0'; + + if (!strcmp(token, "+") || !strcmp(token, "-") || !strcmp(token, "*") || + !strcmp(token, "/") || !strcmp(token, "%")) + { + if (!pev_binop(&pev, token[0])) goto done; + } + else if (!strcmp(token, "^")) + { + if (!pev_deref(&pev)) goto done; + } + else if (!strcmp(token, "=")) + { + if (!pev_assign(&pev)) goto done; + } + else + { + if (!pev_push(&pev, token)) goto done; + } + ptok = token; + } + else + { + if (ptok - token >= PEV_MAX_LEN - 1) + { + pev_set_error(&pev, "parse: token too long (%s)", ptr - (ptok - token)); + goto done; + } + *ptok++ = *ptr; + } + } + pev_free(&pev, cpair); + return TRUE; +done: + FIXME("Couldn't evaluate %s => %s\n", debugstr_a(cmd), pev.error); + pev_free(&pev, NULL); + return FALSE; +} + +BOOL pdb_virtual_unwind(struct cpu_stack_walk *csw, DWORD_PTR ip, + union ctx *context, struct pdb_cmd_pair *cpair) +{ + struct pdb_reader_walker walker; + struct pdb_reader pdb; + struct module_pair pair; + HANDLE file_handle; + unsigned fpoext_stream; + PDB_FPO_DATA fpoext; + BOOL ret = FALSE; + + if (!module_init_pair(&pair, csw->hProcess, ip)) return FALSE; + if (!pdb_hack_get_main_info(pair.effective->format_info[DFI_PDB], &file_handle, &fpoext_stream)) return FALSE; + + if (!file_handle) + return pdb_old_virtual_unwind(csw, ip, context, cpair); + TRACE("searching %Ix => %Ix\n", ip, ip - (DWORD_PTR)pair.effective->module.BaseOfImage); + ip -= (DWORD_PTR)pair.effective->module.BaseOfImage; + + if (pdb_reader_init(&pdb, file_handle, pair.effective->pool.heap)) + return FALSE; + + if (!pdb_reader_walker_init(&pdb, fpoext_stream, &walker) && + (walker.last % sizeof(fpoext)) == 0) + { + /* FIXME likely a binary search should be more appropriate here */ + while (pdb_reader_READ(&pdb, &walker, &fpoext) == R_PDB_SUCCESS) + { + if (fpoext.start <= ip && ip < fpoext.start + fpoext.func_size) + { + char *cmd; + + if (pdb_reader_alloc_and_fetch_global_string(&pdb, fpoext.str_offset, &cmd)) break; + TRACE("\t%08x %08x %8x %8x %4x %4x %4x %08x %s\n", + fpoext.start, fpoext.func_size, fpoext.locals_size, + fpoext.params_size, fpoext.maxstack_size, fpoext.prolog_size, + fpoext.savedregs_size, fpoext.flags, + debugstr_a(cmd)); + ret = pdb_fpo_unwind_parse_cmd_string(csw, &fpoext, cmd, cpair); + pdb_reader_free(&pdb, cmd); + break; + } + } + } + pdb_reader_dispose(&pdb); + + return ret; +} diff --git a/dlls/dbghelp/storage.c b/dlls/dbghelp/storage.c index 0589ed699eeb..d3fea4759013 100644 --- a/dlls/dbghelp/storage.c +++ b/dlls/dbghelp/storage.c @@ -50,10 +50,6 @@ void* pool_realloc(struct pool* pool, void* ptr, size_t len) void pool_free(struct pool* pool, void* ptr) { -#ifdef USE_STATS - if (ptr) - mem_stats_down(&pool->stats, HeapSize(pool->heap, 0, ptr)); -#endif HeapFree(pool->heap, 0, ptr); } From 3133f3fb96b32c37ee921ad1a2a32ec96995c934 Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Mon, 4 Nov 2024 11:21:09 +0100 Subject: [PATCH 235/454] dbghelp: Don't keep PDB files mapped after parsing is done. Signed-off-by: Eric Pouech (cherry picked from commit 2b5fdd8918cf0c2d65fdbee5d30364d6a23d6130) --- dlls/dbghelp/dbghelp_private.h | 5 +++- dlls/dbghelp/msc.c | 39 +++++++++++++++---------- dlls/dbghelp/pdb.c | 53 ++++++++++++++++++---------------- 3 files changed, 56 insertions(+), 41 deletions(-) diff --git a/dlls/dbghelp/dbghelp_private.h b/dlls/dbghelp/dbghelp_private.h index 03c0f7be1d7c..d7ffdb2e03b2 100644 --- a/dlls/dbghelp/dbghelp_private.h +++ b/dlls/dbghelp/dbghelp_private.h @@ -992,4 +992,7 @@ extern BOOL pdb_fpo_unwind_parse_cmd_string(struct cpu_stack_walk* csw, struct _ const char* cmd, struct pdb_cmd_pair* cpair); extern BOOL pdb_old_virtual_unwind(struct cpu_stack_walk *csw, DWORD_PTR ip, union ctx *context, struct pdb_cmd_pair *cpair); -extern BOOL pdb_hack_get_main_info(struct module_format *modfmt, HANDLE *file, unsigned *fpoext_stream); +struct pdb_reader; +extern BOOL pdb_hack_get_main_info(struct module_format *modfmt, struct pdb_reader **pdb, unsigned *fpoext_stream); +extern void pdb_reader_dispose(struct pdb_reader *pdb); +extern struct pdb_reader *pdb_hack_reader_init(struct module *module, HANDLE file); diff --git a/dlls/dbghelp/msc.c b/dlls/dbghelp/msc.c index 8acd4e70f3aa..b3e15e2eda35 100644 --- a/dlls/dbghelp/msc.c +++ b/dlls/dbghelp/msc.c @@ -61,8 +61,7 @@ enum pdb_kind {PDB_JG, PDB_DS}; struct pdb_file_info { enum pdb_kind kind; - HANDLE file_handle; - HANDLE hMap; + struct pdb_reader *pdb_reader; /* new pdb reader */ const char* image; struct pdb_stream_name* stream_dict; unsigned fpoext_stream; @@ -97,13 +96,13 @@ struct cv_module_snarf const PDB_STRING_TABLE* strimage; }; -BOOL pdb_hack_get_main_info(struct module_format *modfmt, HANDLE *file, unsigned *fpoext_stream) +BOOL pdb_hack_get_main_info(struct module_format *modfmt, struct pdb_reader **pdb, unsigned *fpoext_stream) { struct pdb_module_info* pdb_info; if (!modfmt) return FALSE; pdb_info = modfmt->u.pdb_info; - *file = pdb_info->pdb_files[0].file_handle; + *pdb = pdb_info->pdb_files[0].pdb_reader; if (fpoext_stream) *fpoext_stream = pdb_info->pdb_files[0].fpoext_stream; return TRUE; @@ -3174,7 +3173,7 @@ static void pdb_free(void* buffer) HeapFree(GetProcessHeap(), 0, buffer); } -static void pdb_free_file(struct pdb_file_info* pdb_file) +static void pdb_free_file(struct pdb_file_info* pdb_file, BOOL unmap) { switch (pdb_file->kind) { @@ -3188,6 +3187,12 @@ static void pdb_free_file(struct pdb_file_info* pdb_file) break; } HeapFree(GetProcessHeap(), 0, pdb_file->stream_dict); + pdb_file->stream_dict = NULL; + if (unmap) + { + UnmapViewOfFile(pdb_file->image); + pdb_file->image = NULL; + } } static struct pdb_stream_name* pdb_load_stream_name_table(const char* str, unsigned cb) @@ -3267,13 +3272,11 @@ static void pdb_module_remove(struct process* pcsn, struct module_format* modfmt for (i = 0; i < modfmt->u.pdb_info->used_subfiles; i++) { - pdb_free_file(&modfmt->u.pdb_info->pdb_files[i]); + pdb_free_file(&modfmt->u.pdb_info->pdb_files[i], TRUE); if (modfmt->u.pdb_info->pdb_files[i].image) UnmapViewOfFile(modfmt->u.pdb_info->pdb_files[i].image); - if (modfmt->u.pdb_info->pdb_files[i].hMap) - CloseHandle(modfmt->u.pdb_info->pdb_files[i].hMap); - if (modfmt->u.pdb_info->pdb_files[i].file_handle) - CloseHandle(modfmt->u.pdb_info->pdb_files[i].file_handle); + if (modfmt->u.pdb_info->pdb_files[i].pdb_reader) + pdb_reader_dispose(modfmt->u.pdb_info->pdb_files[i].pdb_reader); } HeapFree(GetProcessHeap(), 0, modfmt); } @@ -3800,22 +3803,26 @@ static BOOL pdb_process_internal(const struct process *pcs, return FALSE; } + CloseHandle(hMap); + /* old pdb reader */ if (!pdb_init(pdb_file, image)) { CloseHandle(hFile); - CloseHandle(hMap); UnmapViewOfFile(image); return FALSE; } if (getenv("WINE_DBGHELP_OLD_PDB")) /* keep using old pdb reader */ { - pdb_file->file_handle = NULL; + pdb_file->pdb_reader = NULL; CloseHandle(hFile); } - else - pdb_file->file_handle = hFile; + else if (!(pdb_file->pdb_reader = pdb_hack_reader_init(msc_dbg->module, hFile))) + { + CloseHandle(hFile); + UnmapViewOfFile(image); + return FALSE; + } - pdb_file->hMap = hMap; pdb_file->image = image; symbols_image = pdb_read_stream(pdb_file, 3); if (symbols_image) @@ -3960,6 +3967,8 @@ static BOOL pdb_process_internal(const struct process *pcs, pdb_free(symbols_image); pdb_free(files_image); + pdb_free_file(pdb_file, pdb_file->pdb_reader != NULL); + return TRUE; } diff --git a/dlls/dbghelp/pdb.c b/dlls/dbghelp/pdb.c index e99b96de3093..7948dfd5f0b7 100644 --- a/dlls/dbghelp/pdb.c +++ b/dlls/dbghelp/pdb.c @@ -67,8 +67,10 @@ typedef enum pdb_result (*pdb_reader_fetch_t)(struct pdb_reader *pdb, void *buff struct pdb_reader { + struct module *module; HANDLE file; - HANDLE heap; + /* using ad hoc pool (not the module one), so that we can measure memory of each PDB reader during transition */ + struct pool pool; /* header */ unsigned block_size; @@ -108,12 +110,12 @@ static const char PDB_DS_IDENT[] = "Microsoft C/C++ MSF 7.00\r\n\032DS\0"; static inline enum pdb_result pdb_reader_alloc(struct pdb_reader *pdb, size_t size, void **ptr) { - return (*ptr = HeapAlloc(pdb->heap, 0, size)) ? R_PDB_SUCCESS : R_PDB_OUT_OF_MEMORY; + return (*ptr = pool_alloc(&pdb->pool, size)) ? R_PDB_SUCCESS : R_PDB_OUT_OF_MEMORY; } static inline enum pdb_result pdb_reader_realloc(struct pdb_reader *pdb, void **ptr, size_t size) { - void *new = HeapReAlloc(pdb->heap, 0, *ptr, size); + void *new = pool_realloc(&pdb->pool, *ptr, size); if (!new) return R_PDB_OUT_OF_MEMORY; *ptr = new; return R_PDB_SUCCESS; @@ -121,7 +123,7 @@ static inline enum pdb_result pdb_reader_realloc(struct pdb_reader *pdb, void ** static inline void pdb_reader_free(struct pdb_reader *pdb, void *ptr) { - HeapFree(pdb->heap, 0, ptr); + pool_free(&pdb->pool, ptr); } static inline unsigned pdb_reader_num_blocks(struct pdb_reader *pdb, pdbsize_t size) @@ -200,7 +202,7 @@ static enum pdb_result pdb_reader_read_from_stream(struct pdb_reader *pdb, const return R_PDB_SUCCESS; } -static enum pdb_result pdb_reader_init(struct pdb_reader *pdb, HANDLE file, HANDLE heap) +static enum pdb_result pdb_reader_init(struct pdb_reader *pdb, struct module *module, HANDLE file) { enum pdb_result result; struct PDB_DS_HEADER hdr; @@ -211,8 +213,9 @@ static enum pdb_result pdb_reader_init(struct pdb_reader *pdb, HANDLE file, HAND uint32_t *blocks; memset(pdb, 0, sizeof(*pdb)); + pdb->module = module; pdb->file = file; - pdb->heap = heap; + pool_init(&pdb->pool, 65536); pdb->fetch = &pdb_reader_fetch_file; if ((result = (*pdb->fetch)(pdb, &hdr, 0, sizeof(hdr)))) return result; @@ -261,14 +264,10 @@ static enum pdb_result pdb_reader_init(struct pdb_reader *pdb, HANDLE file, HAND return result; } -static void pdb_reader_dispose(struct pdb_reader *pdb) +void pdb_reader_dispose(struct pdb_reader *pdb) { - unsigned i; - - for (i = 0; i < pdb->toc->num_streams; i++) - pdb_reader_free(pdb, pdb->streams[i].name); - pdb_reader_free(pdb, pdb->streams); - pdb_reader_free(pdb, pdb->toc); + CloseHandle(pdb->file); + pool_destroy(&pdb->pool); } static enum pdb_result pdb_reader_internal_read_advance(struct pdb_reader *pdb, struct pdb_reader_walker *walker, @@ -434,6 +433,14 @@ static enum pdb_result pdb_reader_alloc_and_fetch_global_string(struct pdb_reade return pdb_reader_alloc_and_fetch_string(pdb, &walker, buffer); } +struct pdb_reader *pdb_hack_reader_init(struct module *module, HANDLE file) +{ + struct pdb_reader *pdb = pool_alloc(&module->pool, sizeof(*pdb)); + if (pdb && pdb_reader_init(pdb, module, file) == R_PDB_SUCCESS) return pdb; + pool_free(&module->pool, pdb); + return NULL; +} + /*======================================================================== * FPO unwinding code */ @@ -741,48 +748,44 @@ BOOL pdb_fpo_unwind_parse_cmd_string(struct cpu_stack_walk* csw, PDB_FPO_DATA* BOOL pdb_virtual_unwind(struct cpu_stack_walk *csw, DWORD_PTR ip, union ctx *context, struct pdb_cmd_pair *cpair) { + struct pdb_reader *pdb; struct pdb_reader_walker walker; - struct pdb_reader pdb; struct module_pair pair; - HANDLE file_handle; unsigned fpoext_stream; PDB_FPO_DATA fpoext; BOOL ret = FALSE; if (!module_init_pair(&pair, csw->hProcess, ip)) return FALSE; - if (!pdb_hack_get_main_info(pair.effective->format_info[DFI_PDB], &file_handle, &fpoext_stream)) return FALSE; + if (!pdb_hack_get_main_info(pair.effective->format_info[DFI_PDB], &pdb, &fpoext_stream)) return FALSE; - if (!file_handle) + if (!pdb) return pdb_old_virtual_unwind(csw, ip, context, cpair); TRACE("searching %Ix => %Ix\n", ip, ip - (DWORD_PTR)pair.effective->module.BaseOfImage); ip -= (DWORD_PTR)pair.effective->module.BaseOfImage; - if (pdb_reader_init(&pdb, file_handle, pair.effective->pool.heap)) - return FALSE; - - if (!pdb_reader_walker_init(&pdb, fpoext_stream, &walker) && + if (!pdb_reader_walker_init(pdb, fpoext_stream, &walker) && (walker.last % sizeof(fpoext)) == 0) { /* FIXME likely a binary search should be more appropriate here */ - while (pdb_reader_READ(&pdb, &walker, &fpoext) == R_PDB_SUCCESS) + while (pdb_reader_READ(pdb, &walker, &fpoext) == R_PDB_SUCCESS) { if (fpoext.start <= ip && ip < fpoext.start + fpoext.func_size) { char *cmd; - if (pdb_reader_alloc_and_fetch_global_string(&pdb, fpoext.str_offset, &cmd)) break; + if (pdb_reader_alloc_and_fetch_global_string(pdb, fpoext.str_offset, &cmd)) break; TRACE("\t%08x %08x %8x %8x %4x %4x %4x %08x %s\n", fpoext.start, fpoext.func_size, fpoext.locals_size, fpoext.params_size, fpoext.maxstack_size, fpoext.prolog_size, fpoext.savedregs_size, fpoext.flags, debugstr_a(cmd)); + ret = pdb_fpo_unwind_parse_cmd_string(csw, &fpoext, cmd, cpair); - pdb_reader_free(&pdb, cmd); + pdb_reader_free(pdb, cmd); break; } } } - pdb_reader_dispose(&pdb); return ret; } From 16b2b27d947a963ef7d0887fcc747e90c7868384 Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Fri, 8 Nov 2024 09:00:10 +0100 Subject: [PATCH 236/454] dbghelp: Introduce a vtable per module_format. In following patches, we'll add a bunch of new interfaces here. Also, hide partially module_format walking with some helpers. Signed-off-by: Eric Pouech (cherry picked from commit c66721795f3ee0f150446dce70f6796252e3cb23) --- dlls/dbghelp/dbghelp_private.h | 41 +++++++++++++++++++++++++++++----- dlls/dbghelp/dwarf.c | 21 ++++++++++------- dlls/dbghelp/elf_module.c | 11 ++++++--- dlls/dbghelp/macho_module.c | 11 ++++++--- dlls/dbghelp/module.c | 11 ++++----- dlls/dbghelp/msc.c | 16 ++++++++----- dlls/dbghelp/pe_module.c | 11 ++++++--- dlls/dbghelp/symbol.c | 14 +++++------- dlls/dbghelp/type.c | 16 +++++-------- 9 files changed, 98 insertions(+), 54 deletions(-) diff --git a/dlls/dbghelp/dbghelp_private.h b/dlls/dbghelp/dbghelp_private.h index d7ffdb2e03b2..809199a9ac5e 100644 --- a/dlls/dbghelp/dbghelp_private.h +++ b/dlls/dbghelp/dbghelp_private.h @@ -414,14 +414,22 @@ enum format_info DFI_LAST }; -struct module_format +struct module_format; +struct module_format_vtable { - struct module* module; - void (*remove)(struct process* pcs, struct module_format* modfmt); - void (*loc_compute)(struct process* pcs, - const struct module_format* modfmt, + /* module handling */ + void (*remove)(struct module_format* modfmt); + /* stack walk */ + void (*loc_compute)(const struct module_format* modfmt, const struct symt_function* func, struct location* loc); +}; + +struct module_format +{ + struct module* module; + const struct module_format_vtable* vtable; + union { struct elf_module_info* elf_info; @@ -478,6 +486,29 @@ struct module struct wine_rb_tree sources_offsets_tree; }; +struct module_format_vtable_iterator +{ + int dfi; + struct module_format *modfmt; +}; + +#define MODULE_FORMAT_VTABLE_INDEX(f) (offsetof(struct module_format_vtable, f) / sizeof(void*)) + +static inline BOOL module_format_vtable_iterator_next(struct module *module, struct module_format_vtable_iterator *iter, size_t method_index) +{ + for ( ; iter->dfi < DFI_LAST; iter->dfi++) + { + iter->modfmt = module->format_info[iter->dfi]; + if (iter->modfmt && ((const void**)iter->modfmt->vtable)[method_index]) + { + iter->dfi++; + return TRUE; + } + } + iter->modfmt = NULL; + return FALSE; +} + typedef BOOL (*enum_modules_cb)(const WCHAR*, ULONG_PTR addr, void* user); struct loader_ops diff --git a/dlls/dbghelp/dwarf.c b/dlls/dbghelp/dwarf.c index c1789182ba38..ce53e37e72cb 100644 --- a/dlls/dbghelp/dwarf.c +++ b/dlls/dbghelp/dwarf.c @@ -3129,12 +3129,12 @@ static const dwarf2_cuhead_t* get_cuhead_from_func(const struct symt_function* f static enum location_error compute_call_frame_cfa(struct module* module, ULONG_PTR ip, struct location* frame); -static enum location_error loc_compute_frame(struct process* pcs, - const struct module_format* modfmt, +static enum location_error loc_compute_frame(const struct module_format* modfmt, const struct symt_function* func, DWORD_PTR ip, const dwarf2_cuhead_t* head, struct location* frame) { + struct process *pcs = modfmt->module->process; struct symt** psym = NULL; struct location* pframe; dwarf2_traverse_context_t lctx; @@ -4007,8 +4007,7 @@ static enum location_error compute_call_frame_cfa(struct module* module, ULONG_P return 0; } -static void dwarf2_location_compute(struct process* pcs, - const struct module_format* modfmt, +static void dwarf2_location_compute(const struct module_format* modfmt, const struct symt_function* func, struct location* loc) { @@ -4026,10 +4025,11 @@ static void dwarf2_location_compute(struct process* pcs, } else { + struct process *pcs = modfmt->module->process; /* instruction pointer relative to compiland's start */ ip = pcs->localscope_pc - ((struct symt_compiland*)func->container)->address; - if ((err = loc_compute_frame(pcs, modfmt, func, ip, head, &frame)) == 0) + if ((err = loc_compute_frame(modfmt, func, ip, head, &frame)) == 0) { switch (loc->kind) { @@ -4187,7 +4187,7 @@ static inline void dwarf2_fini_section(dwarf2_section_t* section) HeapFree(GetProcessHeap(), 0, (void*)section->address); } -static void dwarf2_module_remove(struct process* pcs, struct module_format* modfmt) +static void dwarf2_module_remove(struct module_format* modfmt) { dwarf2_fini_section(&modfmt->u.dwarf2_info->debug_loc); dwarf2_fini_section(&modfmt->u.dwarf2_info->debug_frame); @@ -4293,6 +4293,12 @@ static BOOL dwarf2_unload_CU_module(dwarf2_parse_module_context_t* module_ctx) return TRUE; } +static const struct module_format_vtable dwarf2_module_format_vtable = +{ + dwarf2_module_remove, + dwarf2_location_compute, +}; + BOOL dwarf2_parse(struct module* module, ULONG_PTR load_offset, const struct elf_thunk_area* thunks, struct image_file_map* fmap) @@ -4345,8 +4351,7 @@ return FALSE; goto leave; } dwarf2_modfmt->module = module; - dwarf2_modfmt->remove = dwarf2_module_remove; - dwarf2_modfmt->loc_compute = dwarf2_location_compute; + dwarf2_modfmt->vtable = &dwarf2_module_format_vtable; dwarf2_modfmt->u.dwarf2_info = (struct dwarf2_module_info_s*)(dwarf2_modfmt + 1); dwarf2_modfmt->u.dwarf2_info->word_size = fmap->addr_size / 8; /* set the word_size for eh_frame parsing */ dwarf2_modfmt->module->format_info[DFI_DWARF] = dwarf2_modfmt; diff --git a/dlls/dbghelp/elf_module.c b/dlls/dbghelp/elf_module.c index 7f5c05b46826..803cf360f856 100644 --- a/dlls/dbghelp/elf_module.c +++ b/dlls/dbghelp/elf_module.c @@ -602,7 +602,7 @@ BOOL elf_map_handle(HANDLE handle, struct image_file_map* fmap) return elf_map_file(&emfd, fmap); } -static void elf_module_remove(struct process* pcs, struct module_format* modfmt) +static void elf_module_remove(struct module_format* modfmt) { image_unmap_file(&modfmt->u.elf_info->file_map); HeapFree(GetProcessHeap(), 0, modfmt); @@ -1142,6 +1142,12 @@ static BOOL elf_fetch_file_info(struct process* process, const WCHAR* name, ULON return TRUE; } +static const struct module_format_vtable elf_module_format_vtable = +{ + elf_module_remove, + NULL, +}; + static BOOL elf_load_file_from_fmap(struct process* pcs, const WCHAR* filename, struct image_file_map* fmap, ULONG_PTR load_offset, ULONG_PTR dyn_addr, struct elf_info* elf_info) @@ -1253,8 +1259,7 @@ static BOOL elf_load_file_from_fmap(struct process* pcs, const WCHAR* filename, elf_module_info = (void*)(modfmt + 1); elf_info->module->format_info[DFI_ELF] = modfmt; modfmt->module = elf_info->module; - modfmt->remove = elf_module_remove; - modfmt->loc_compute = NULL; + modfmt->vtable = &elf_module_format_vtable; modfmt->u.elf_info = elf_module_info; elf_module_info->elf_addr = load_offset; diff --git a/dlls/dbghelp/macho_module.c b/dlls/dbghelp/macho_module.c index b1f457d122d2..5f66509be7b3 100644 --- a/dlls/dbghelp/macho_module.c +++ b/dlls/dbghelp/macho_module.c @@ -1479,12 +1479,18 @@ static BOOL macho_fetch_file_info(struct process* process, const WCHAR* name, UL /****************************************************************** * macho_module_remove */ -static void macho_module_remove(struct process* pcs, struct module_format* modfmt) +static void macho_module_remove(struct module_format* modfmt) { macho_unmap_file(&modfmt->u.macho_info->file_map); HeapFree(GetProcessHeap(), 0, modfmt); } +static const struct module_format_vtable macho_module_format_vtable = +{ + macho_module_remove, + NULL, +}; + /****************************************************************** * macho_load_file * @@ -1538,8 +1544,7 @@ static BOOL macho_load_file(struct process* pcs, const WCHAR* filename, macho_info->module->format_info[DFI_MACHO] = modfmt; modfmt->module = macho_info->module; - modfmt->remove = macho_module_remove; - modfmt->loc_compute = NULL; + modfmt->vtable = &macho_module_format_vtable; modfmt->u.macho_info = macho_module_info; macho_module_info->load_addr = load_addr; diff --git a/dlls/dbghelp/module.c b/dlls/dbghelp/module.c index 8960973c0660..d6f924be726a 100644 --- a/dlls/dbghelp/module.c +++ b/dlls/dbghelp/module.c @@ -1079,9 +1079,8 @@ DWORD64 WINAPI SymLoadModule64(HANDLE hProcess, HANDLE hFile, PCSTR ImageName, */ BOOL module_remove(struct process* pcs, struct module* module) { - struct module_format*modfmt; + struct module_format_vtable_iterator iter = {}; struct module** p; - unsigned i; TRACE("%s (%p)\n", debugstr_w(module->modulename), module); @@ -1102,11 +1101,9 @@ BOOL module_remove(struct process* pcs, struct module* module) } } } - for (i = 0; i < DFI_LAST; i++) - { - if ((modfmt = module->format_info[i]) && modfmt->remove) - modfmt->remove(pcs, module->format_info[i]); - } + while (module_format_vtable_iterator_next(module, &iter, MODULE_FORMAT_VTABLE_INDEX(remove))) + iter.modfmt->vtable->remove(iter.modfmt); + hash_table_destroy(&module->ht_symbols); hash_table_destroy(&module->ht_types); HeapFree(GetProcessHeap(), 0, module->sources); diff --git a/dlls/dbghelp/msc.c b/dlls/dbghelp/msc.c index b3e15e2eda35..db06b186edb6 100644 --- a/dlls/dbghelp/msc.c +++ b/dlls/dbghelp/msc.c @@ -2866,8 +2866,7 @@ static BOOL codeview_is_inside(const struct cv_local_info* locinfo, const struct return TRUE; } -static void pdb_location_compute(struct process* pcs, - const struct module_format* modfmt, +static void pdb_location_compute(const struct module_format* modfmt, const struct symt_function* func, struct location* loc) { @@ -2880,7 +2879,7 @@ static void pdb_location_compute(struct process* pcs, locinfo->kind != 0; locinfo = (const struct cv_local_info*)((const char*)(locinfo + 1) + locinfo->ngaps * sizeof(locinfo->gaps[0]))) { - if (!codeview_is_inside(locinfo, func, pcs->localscope_pc)) continue; + if (!codeview_is_inside(locinfo, func, modfmt->module->process->localscope_pc)) continue; switch (locinfo->kind) { case S_DEFRANGE: @@ -3266,7 +3265,7 @@ static const char* pdb_get_string_table_entry(const PDB_STRING_TABLE* table, uns return (!table || offset >= table->length) ? NULL : (const char*)(table + 1) + offset; } -static void pdb_module_remove(struct process* pcsn, struct module_format* modfmt) +static void pdb_module_remove(struct module_format* modfmt) { unsigned i; @@ -3972,6 +3971,12 @@ static BOOL pdb_process_internal(const struct process *pcs, return TRUE; } +static const struct module_format_vtable pdb_module_format_vtable = +{ + pdb_module_remove, + pdb_location_compute, +}; + static BOOL pdb_process_file(const struct process *pcs, const struct msc_debug_info *msc_dbg, const char *filename, const GUID *guid, DWORD timestamp, DWORD age) @@ -3991,8 +3996,7 @@ static BOOL pdb_process_file(const struct process *pcs, pdb_module_info = (void*)(modfmt + 1); msc_dbg->module->format_info[DFI_PDB] = modfmt; modfmt->module = msc_dbg->module; - modfmt->remove = pdb_module_remove; - modfmt->loc_compute = pdb_location_compute; + modfmt->vtable = &pdb_module_format_vtable; modfmt->u.pdb_info = pdb_module_info; memset(cv_zmodules, 0, sizeof(cv_zmodules)); diff --git a/dlls/dbghelp/pe_module.c b/dlls/dbghelp/pe_module.c index bd3d6000535b..1f2022fc1af1 100644 --- a/dlls/dbghelp/pe_module.c +++ b/dlls/dbghelp/pe_module.c @@ -387,7 +387,7 @@ BOOL pe_unlock_region(struct module *module, const BYTE* region) return TRUE; } -static void pe_module_remove(struct process* pcs, struct module_format* modfmt) +static void pe_module_remove(struct module_format* modfmt) { image_unmap_file(&modfmt->u.pe_info->fmap); HeapFree(GetProcessHeap(), 0, modfmt); @@ -802,6 +802,12 @@ static BOOL search_builtin_pe(void *param, HANDLE handle, const WCHAR *path) return TRUE; } +static const struct module_format_vtable pe_module_format_vtable = +{ + pe_module_remove, + NULL, +}; + /****************************************************************** * pe_load_native_module * @@ -877,8 +883,7 @@ struct module* pe_load_native_module(struct process* pcs, const WCHAR* name, { module->real_path = real_path ? pool_wcsdup(&module->pool, real_path) : NULL; modfmt->module = module; - modfmt->remove = pe_module_remove; - modfmt->loc_compute = NULL; + modfmt->vtable = &pe_module_format_vtable; module->format_info[DFI_PE] = modfmt; module->reloc_delta = base - PE_FROM_OPTHDR(&modfmt->u.pe_info->fmap, ImageBase); } diff --git a/dlls/dbghelp/symbol.c b/dlls/dbghelp/symbol.c index 4fdd0a141be3..7edf11a19b99 100644 --- a/dlls/dbghelp/symbol.c +++ b/dlls/dbghelp/symbol.c @@ -733,17 +733,13 @@ static void symt_fill_sym_info(struct module_pair* pair, if (loc.kind >= loc_user) { - unsigned i; - struct module_format* modfmt; + struct module_format_vtable_iterator iter = {}; - for (i = 0; i < DFI_LAST; i++) + while ((module_format_vtable_iterator_next(pair->effective, &iter, + MODULE_FORMAT_VTABLE_INDEX(loc_compute)))) { - modfmt = pair->effective->format_info[i]; - if (modfmt && modfmt->loc_compute) - { - modfmt->loc_compute(pair->pcs, modfmt, func, &loc); - break; - } + iter.modfmt->vtable->loc_compute(iter.modfmt, func, &loc); + break; } } switch (loc.kind) diff --git a/dlls/dbghelp/type.c b/dlls/dbghelp/type.c index a3ad80aacbb0..8be905116c7c 100644 --- a/dlls/dbghelp/type.c +++ b/dlls/dbghelp/type.c @@ -1039,19 +1039,15 @@ BOOL symt_get_info(struct module* module, const struct symt* type, case DataIsParam: { struct location loc = ((const struct symt_data*)type)->u.var; - unsigned i; - struct module_format* modfmt; + struct module_format_vtable_iterator iter = { 0 }; if (loc.kind < loc_user) return FALSE; - for (i = 0; i < DFI_LAST; i++) + while ((module_format_vtable_iterator_next(module, &iter, + MODULE_FORMAT_VTABLE_INDEX(loc_compute)))) { - modfmt = module->format_info[i]; - if (modfmt && modfmt->loc_compute) - { - modfmt->loc_compute(module->process, modfmt, - (const struct symt_function*)((const struct symt_data*)type)->container, &loc); - break; - } + iter.modfmt->vtable->loc_compute(iter.modfmt, + (const struct symt_function*)((const struct symt_data*)type)->container, &loc); + break; } if (loc.kind != loc_absolute) return FALSE; V_VT(&X(VARIANT)) = VT_UI4; /* FIXME */ From a7c97a77eee5950ec0bdfda3fd69d172d2893c11 Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Fri, 21 Mar 2025 18:21:13 +0100 Subject: [PATCH 237/454] dbghelp: Rename struct internal_line into lineinfo. Used in next patches as the structure & helpers are going to be used in other CUs. Signed-off-by: Eric Pouech (cherry picked from commit 5aca9df8d9ebd00263e8ce7f0ebbb2dc178207ee) --- dlls/dbghelp/symbol.c | 208 +++++++++++++++++++++--------------------- 1 file changed, 104 insertions(+), 104 deletions(-) diff --git a/dlls/dbghelp/symbol.c b/dlls/dbghelp/symbol.c index 7edf11a19b99..52c284f2c1ea 100644 --- a/dlls/dbghelp/symbol.c +++ b/dlls/dbghelp/symbol.c @@ -1754,7 +1754,7 @@ BOOL WINAPI SymGetSymFromName(HANDLE hProcess, PCSTR Name, PIMAGEHLP_SYMBOL Symb return TRUE; } -struct internal_line_t +struct lineinfo_t { BOOL unicode; PVOID key; @@ -1767,95 +1767,95 @@ struct internal_line_t DWORD64 address; }; -static void init_internal_line(struct internal_line_t* intl, BOOL unicode) +static void init_lineinfo(struct lineinfo_t* line_info, BOOL unicode) { - intl->unicode = unicode; - intl->key = NULL; - intl->line_number = 0; - intl->file_nameA = NULL; - intl->address = 0; + line_info->unicode = unicode; + line_info->key = NULL; + line_info->line_number = 0; + line_info->file_nameA = NULL; + line_info->address = 0; } -static BOOL internal_line_copy_toA32(const struct internal_line_t* intl, IMAGEHLP_LINE* l32) +static BOOL lineinfo_copy_toA32(const struct lineinfo_t* line_info, IMAGEHLP_LINE* l32) { - if (intl->unicode) return FALSE; - l32->Key = intl->key; - l32->LineNumber = intl->line_number; - l32->FileName = intl->file_nameA; - l32->Address = intl->address; + if (line_info->unicode) return FALSE; + l32->Key = line_info->key; + l32->LineNumber = line_info->line_number; + l32->FileName = line_info->file_nameA; + l32->Address = line_info->address; return TRUE; } -static BOOL internal_line_copy_toA64(const struct internal_line_t* intl, IMAGEHLP_LINE64* l64) +static BOOL lineinfo_copy_toA64(const struct lineinfo_t* line_info, IMAGEHLP_LINE64* l64) { - if (intl->unicode) return FALSE; - l64->Key = intl->key; - l64->LineNumber = intl->line_number; - l64->FileName = intl->file_nameA; - l64->Address = intl->address; + if (line_info->unicode) return FALSE; + l64->Key = line_info->key; + l64->LineNumber = line_info->line_number; + l64->FileName = line_info->file_nameA; + l64->Address = line_info->address; return TRUE; } -static BOOL internal_line_copy_toW64(const struct internal_line_t* intl, IMAGEHLP_LINEW64* l64) +static BOOL lineinfo_copy_toW64(const struct lineinfo_t* line_info, IMAGEHLP_LINEW64* l64) { - if (!intl->unicode) return FALSE; - l64->Key = intl->key; - l64->LineNumber = intl->line_number; - l64->FileName = intl->file_nameW; - l64->Address = intl->address; + if (!line_info->unicode) return FALSE; + l64->Key = line_info->key; + l64->LineNumber = line_info->line_number; + l64->FileName = line_info->file_nameW; + l64->Address = line_info->address; return TRUE; } -static BOOL internal_line_set_nameA(struct process* pcs, struct internal_line_t* intl, char* str, BOOL copy) +static BOOL lineinfo_set_nameA(struct process* pcs, struct lineinfo_t* line_info, char* str, BOOL copy) { DWORD len; - if (intl->unicode) + if (line_info->unicode) { len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0); - if (!(intl->file_nameW = fetch_buffer(pcs, len * sizeof(WCHAR)))) return FALSE; - MultiByteToWideChar(CP_ACP, 0, str, -1, intl->file_nameW, len); + if (!(line_info->file_nameW = fetch_buffer(pcs, len * sizeof(WCHAR)))) return FALSE; + MultiByteToWideChar(CP_ACP, 0, str, -1, line_info->file_nameW, len); } else { if (copy) { len = strlen(str) + 1; - if (!(intl->file_nameA = fetch_buffer(pcs, len))) return FALSE; - memcpy(intl->file_nameA, str, len); + if (!(line_info->file_nameA = fetch_buffer(pcs, len))) return FALSE; + memcpy(line_info->file_nameA, str, len); } else - intl->file_nameA = str; + line_info->file_nameA = str; } return TRUE; } -static BOOL internal_line_set_nameW(struct process* pcs, struct internal_line_t* intl, WCHAR* wstr, BOOL copy) +static BOOL lineinfo_set_nameW(struct process* pcs, struct lineinfo_t* line_info, WCHAR* wstr, BOOL copy) { DWORD len; - if (intl->unicode) + if (line_info->unicode) { if (copy) { len = (lstrlenW(wstr) + 1) * sizeof(WCHAR); - if (!(intl->file_nameW = fetch_buffer(pcs, len))) return FALSE; - memcpy(intl->file_nameW, wstr, len); + if (!(line_info->file_nameW = fetch_buffer(pcs, len))) return FALSE; + memcpy(line_info->file_nameW, wstr, len); } else - intl->file_nameW = wstr; + line_info->file_nameW = wstr; } else { DWORD len = WideCharToMultiByte(CP_ACP, 0, wstr, -1, NULL, 0, NULL, NULL); - if (!(intl->file_nameA = fetch_buffer(pcs, len))) return FALSE; - WideCharToMultiByte(CP_ACP, 0, wstr, -1, intl->file_nameA, len, NULL, NULL); + if (!(line_info->file_nameA = fetch_buffer(pcs, len))) return FALSE; + WideCharToMultiByte(CP_ACP, 0, wstr, -1, line_info->file_nameA, len, NULL, NULL); } return TRUE; } static BOOL get_line_from_function(struct module_pair* pair, struct symt_function* func, DWORD64 addr, - PDWORD pdwDisplacement, struct internal_line_t* intl) + PDWORD pdwDisplacement, struct lineinfo_t* line_info) { struct line_info* dli = NULL; struct line_info* found_dli = NULL; @@ -1867,9 +1867,9 @@ static BOOL get_line_from_function(struct module_pair* pair, struct symt_functio if (!dli->is_source_file) { if (found_dli || dli->u.address > addr) continue; - intl->line_number = dli->line_number; - intl->address = dli->u.address; - intl->key = dli; + line_info->line_number = dli->line_number; + line_info->address = dli->u.address; + line_info->key = dli; found_dli = dli; continue; } @@ -1879,12 +1879,12 @@ static BOOL get_line_from_function(struct module_pair* pair, struct symt_functio if (dbghelp_opt_source_actual_path) { /* Return native file paths when using winedbg */ - ret = internal_line_set_nameA(pair->pcs, intl, (char*)source_get(pair->effective, dli->u.source_file), FALSE); + ret = lineinfo_set_nameA(pair->pcs, line_info, (char*)source_get(pair->effective, dli->u.source_file), FALSE); } else { WCHAR *dospath = wine_get_dos_file_name(source_get(pair->effective, dli->u.source_file)); - ret = internal_line_set_nameW(pair->pcs, intl, dospath, TRUE); + ret = lineinfo_set_nameW(pair->pcs, line_info, dospath, TRUE); HeapFree( GetProcessHeap(), 0, dospath ); } if (ret && pdwDisplacement) *pdwDisplacement = addr - found_dli->u.address; @@ -1900,7 +1900,7 @@ static BOOL get_line_from_function(struct module_pair* pair, struct symt_functio * fills source file information from an address */ static BOOL get_line_from_addr(HANDLE hProcess, DWORD64 addr, - PDWORD pdwDisplacement, struct internal_line_t* intl) + PDWORD pdwDisplacement, struct lineinfo_t* line_info) { struct module_pair pair; struct symt_ht* symt; @@ -1909,7 +1909,7 @@ static BOOL get_line_from_addr(HANDLE hProcess, DWORD64 addr, symt = symt_find_symbol_at(pair.effective, addr); if (!symt_check_tag(&symt->symt, SymTagFunction)) return FALSE; - return get_line_from_function(&pair, (struct symt_function*)symt, addr, pdwDisplacement, intl); + return get_line_from_function(&pair, (struct symt_function*)symt, addr, pdwDisplacement, line_info); } /*********************************************************************** @@ -1965,14 +1965,14 @@ BOOL WINAPI SymGetSymPrev(HANDLE hProcess, PIMAGEHLP_SYMBOL Symbol) BOOL WINAPI SymGetLineFromAddr(HANDLE hProcess, DWORD dwAddr, PDWORD pdwDisplacement, PIMAGEHLP_LINE Line) { - struct internal_line_t intl; + struct lineinfo_t line_info; TRACE("(%p %p)\n", hProcess, Line); if (Line->SizeOfStruct < sizeof(*Line)) return FALSE; - init_internal_line(&intl, FALSE); - if (!get_line_from_addr(hProcess, dwAddr, pdwDisplacement, &intl)) return FALSE; - return internal_line_copy_toA32(&intl, Line); + init_lineinfo(&line_info, FALSE); + if (!get_line_from_addr(hProcess, dwAddr, pdwDisplacement, &line_info)) return FALSE; + return lineinfo_copy_toA32(&line_info, Line); } /****************************************************************** @@ -1982,14 +1982,14 @@ BOOL WINAPI SymGetLineFromAddr(HANDLE hProcess, DWORD dwAddr, BOOL WINAPI SymGetLineFromAddr64(HANDLE hProcess, DWORD64 dwAddr, PDWORD pdwDisplacement, PIMAGEHLP_LINE64 Line) { - struct internal_line_t intl; + struct lineinfo_t line_info; TRACE("(%p %p)\n", hProcess, Line); if (Line->SizeOfStruct < sizeof(*Line)) return FALSE; - init_internal_line(&intl, FALSE); - if (!get_line_from_addr(hProcess, dwAddr, pdwDisplacement, &intl)) return FALSE; - return internal_line_copy_toA64(&intl, Line); + init_lineinfo(&line_info, FALSE); + if (!get_line_from_addr(hProcess, dwAddr, pdwDisplacement, &line_info)) return FALSE; + return lineinfo_copy_toA64(&line_info, Line); } /****************************************************************** @@ -1999,17 +1999,17 @@ BOOL WINAPI SymGetLineFromAddr64(HANDLE hProcess, DWORD64 dwAddr, BOOL WINAPI SymGetLineFromAddrW64(HANDLE hProcess, DWORD64 dwAddr, PDWORD pdwDisplacement, PIMAGEHLP_LINEW64 Line) { - struct internal_line_t intl; + struct lineinfo_t line_info; TRACE("(%p %p)\n", hProcess, Line); if (Line->SizeOfStruct < sizeof(*Line)) return FALSE; - init_internal_line(&intl, TRUE); - if (!get_line_from_addr(hProcess, dwAddr, pdwDisplacement, &intl)) return FALSE; - return internal_line_copy_toW64(&intl, Line); + init_lineinfo(&line_info, TRUE); + if (!get_line_from_addr(hProcess, dwAddr, pdwDisplacement, &line_info)) return FALSE; + return lineinfo_copy_toW64(&line_info, Line); } -static BOOL symt_get_func_line_prev(HANDLE hProcess, struct internal_line_t* intl, void* key, DWORD64 addr) +static BOOL symt_get_func_line_prev(HANDLE hProcess, struct lineinfo_t* line_info, void* key, DWORD64 addr) { struct module_pair pair; struct line_info* li; @@ -2026,13 +2026,13 @@ static BOOL symt_get_func_line_prev(HANDLE hProcess, struct internal_line_t* int li--; if (!li->is_source_file) { - intl->line_number = li->line_number; - intl->address = li->u.address; - intl->key = li; + line_info->line_number = li->line_number; + line_info->address = li->u.address; + line_info->key = li; /* search source file */ for (srcli = li; !srcli->is_source_file; srcli--); - return internal_line_set_nameA(pair.pcs, intl, (char*)source_get(pair.effective, srcli->u.source_file), FALSE); + return lineinfo_set_nameA(pair.pcs, line_info, (char*)source_get(pair.effective, srcli->u.source_file), FALSE); } } SetLastError(ERROR_NO_MORE_ITEMS); /* FIXME */ @@ -2045,14 +2045,14 @@ static BOOL symt_get_func_line_prev(HANDLE hProcess, struct internal_line_t* int */ BOOL WINAPI SymGetLinePrev64(HANDLE hProcess, PIMAGEHLP_LINE64 Line) { - struct internal_line_t intl; + struct lineinfo_t line_info; TRACE("(%p %p)\n", hProcess, Line); if (Line->SizeOfStruct < sizeof(*Line)) return FALSE; - init_internal_line(&intl, FALSE); - if (!symt_get_func_line_prev(hProcess, &intl, Line->Key, Line->Address)) return FALSE; - return internal_line_copy_toA64(&intl, Line); + init_lineinfo(&line_info, FALSE); + if (!symt_get_func_line_prev(hProcess, &line_info, Line->Key, Line->Address)) return FALSE; + return lineinfo_copy_toA64(&line_info, Line); } /****************************************************************** @@ -2061,14 +2061,14 @@ BOOL WINAPI SymGetLinePrev64(HANDLE hProcess, PIMAGEHLP_LINE64 Line) */ BOOL WINAPI SymGetLinePrev(HANDLE hProcess, PIMAGEHLP_LINE Line) { - struct internal_line_t intl; + struct lineinfo_t line_info; TRACE("(%p %p)\n", hProcess, Line); if (Line->SizeOfStruct < sizeof(*Line)) return FALSE; - init_internal_line(&intl, FALSE); - if (!symt_get_func_line_prev(hProcess, &intl, Line->Key, Line->Address)) return FALSE; - return internal_line_copy_toA32(&intl, Line); + init_lineinfo(&line_info, FALSE); + if (!symt_get_func_line_prev(hProcess, &line_info, Line->Key, Line->Address)) return FALSE; + return lineinfo_copy_toA32(&line_info, Line); } /****************************************************************** @@ -2077,17 +2077,17 @@ BOOL WINAPI SymGetLinePrev(HANDLE hProcess, PIMAGEHLP_LINE Line) */ BOOL WINAPI SymGetLinePrevW64(HANDLE hProcess, PIMAGEHLP_LINEW64 Line) { - struct internal_line_t intl; + struct lineinfo_t line_info; TRACE("(%p %p)\n", hProcess, Line); if (Line->SizeOfStruct < sizeof(*Line)) return FALSE; - init_internal_line(&intl, TRUE); - if (!symt_get_func_line_prev(hProcess, &intl, Line->Key, Line->Address)) return FALSE; - return internal_line_copy_toW64(&intl, Line); + init_lineinfo(&line_info, TRUE); + if (!symt_get_func_line_prev(hProcess, &line_info, Line->Key, Line->Address)) return FALSE; + return lineinfo_copy_toW64(&line_info, Line); } -static BOOL symt_get_func_line_next(HANDLE hProcess, struct internal_line_t* intl, void* key, DWORD64 addr) +static BOOL symt_get_func_line_next(HANDLE hProcess, struct lineinfo_t* line_info, void* key, DWORD64 addr) { struct module_pair pair; struct line_info* li; @@ -2105,10 +2105,10 @@ static BOOL symt_get_func_line_next(HANDLE hProcess, struct internal_line_t* int li++; if (!li->is_source_file) { - intl->line_number = li->line_number; - intl->address = li->u.address; - intl->key = li; - return internal_line_set_nameA(pair.pcs, intl, (char*)source_get(pair.effective, srcli->u.source_file), FALSE); + line_info->line_number = li->line_number; + line_info->address = li->u.address; + line_info->key = li; + return lineinfo_set_nameA(pair.pcs, line_info, (char*)source_get(pair.effective, srcli->u.source_file), FALSE); } srcli = li; } @@ -2122,14 +2122,14 @@ static BOOL symt_get_func_line_next(HANDLE hProcess, struct internal_line_t* int */ BOOL WINAPI SymGetLineNext64(HANDLE hProcess, PIMAGEHLP_LINE64 Line) { - struct internal_line_t intl; + struct lineinfo_t line_info; TRACE("(%p %p)\n", hProcess, Line); if (Line->SizeOfStruct < sizeof(*Line)) return FALSE; - init_internal_line(&intl, FALSE); - if (!symt_get_func_line_next(hProcess, &intl, Line->Key, Line->Address)) return FALSE; - return internal_line_copy_toA64(&intl, Line); + init_lineinfo(&line_info, FALSE); + if (!symt_get_func_line_next(hProcess, &line_info, Line->Key, Line->Address)) return FALSE; + return lineinfo_copy_toA64(&line_info, Line); } /****************************************************************** @@ -2138,14 +2138,14 @@ BOOL WINAPI SymGetLineNext64(HANDLE hProcess, PIMAGEHLP_LINE64 Line) */ BOOL WINAPI SymGetLineNext(HANDLE hProcess, PIMAGEHLP_LINE Line) { - struct internal_line_t intl; + struct lineinfo_t line_info; TRACE("(%p %p)\n", hProcess, Line); if (Line->SizeOfStruct < sizeof(*Line)) return FALSE; - init_internal_line(&intl, FALSE); - if (!symt_get_func_line_next(hProcess, &intl, Line->Key, Line->Address)) return FALSE; - return internal_line_copy_toA32(&intl, Line); + init_lineinfo(&line_info, FALSE); + if (!symt_get_func_line_next(hProcess, &line_info, Line->Key, Line->Address)) return FALSE; + return lineinfo_copy_toA32(&line_info, Line); } /****************************************************************** @@ -2154,14 +2154,14 @@ BOOL WINAPI SymGetLineNext(HANDLE hProcess, PIMAGEHLP_LINE Line) */ BOOL WINAPI SymGetLineNextW64(HANDLE hProcess, PIMAGEHLP_LINEW64 Line) { - struct internal_line_t intl; + struct lineinfo_t line_info; TRACE("(%p %p)\n", hProcess, Line); if (Line->SizeOfStruct < sizeof(*Line)) return FALSE; - init_internal_line(&intl, TRUE); - if (!symt_get_func_line_next(hProcess, &intl, Line->Key, Line->Address)) return FALSE; - return internal_line_copy_toW64(&intl, Line); + init_lineinfo(&line_info, TRUE); + if (!symt_get_func_line_next(hProcess, &line_info, Line->Key, Line->Address)) return FALSE; + return lineinfo_copy_toW64(&line_info, Line); } /*********************************************************************** @@ -2727,7 +2727,7 @@ BOOL WINAPI SymFromInlineContextW(HANDLE hProcess, DWORD64 addr, ULONG inline_ct } static BOOL get_line_from_inline_context(HANDLE hProcess, DWORD64 addr, ULONG inline_ctx, DWORD64 mod_addr, PDWORD disp, - struct internal_line_t* intl) + struct lineinfo_t* line_info) { struct module_pair pair; struct symt_function* inlined; @@ -2737,12 +2737,12 @@ static BOOL get_line_from_inline_context(HANDLE hProcess, DWORD64 addr, ULONG in { case IFC_MODE_INLINE: inlined = symt_find_inlined_site(pair.effective, addr, inline_ctx); - if (inlined && get_line_from_function(&pair, inlined, addr, disp, intl)) + if (inlined && get_line_from_function(&pair, inlined, addr, disp, line_info)) return TRUE; /* fall through: check if we can find line info at top function level */ case IFC_MODE_IGNORE: case IFC_MODE_REGULAR: - return get_line_from_addr(hProcess, addr, disp, intl); + return get_line_from_addr(hProcess, addr, disp, line_info); default: SetLastError(ERROR_INVALID_PARAMETER); return FALSE; @@ -2755,16 +2755,16 @@ static BOOL get_line_from_inline_context(HANDLE hProcess, DWORD64 addr, ULONG in */ BOOL WINAPI SymGetLineFromInlineContext(HANDLE hProcess, DWORD64 addr, ULONG inline_ctx, DWORD64 mod_addr, PDWORD disp, PIMAGEHLP_LINE64 line) { - struct internal_line_t intl; + struct lineinfo_t line_info; TRACE("(%p, %#I64x, 0x%lx, %#I64x, %p, %p)\n", hProcess, addr, inline_ctx, mod_addr, disp, line); if (line->SizeOfStruct < sizeof(*line)) return FALSE; - init_internal_line(&intl, FALSE); + init_lineinfo(&line_info, FALSE); - if (!get_line_from_inline_context(hProcess, addr, inline_ctx, mod_addr, disp, &intl)) return FALSE; - return internal_line_copy_toA64(&intl, line); + if (!get_line_from_inline_context(hProcess, addr, inline_ctx, mod_addr, disp, &line_info)) return FALSE; + return lineinfo_copy_toA64(&line_info, line); } /****************************************************************** @@ -2773,16 +2773,16 @@ BOOL WINAPI SymGetLineFromInlineContext(HANDLE hProcess, DWORD64 addr, ULONG inl */ BOOL WINAPI SymGetLineFromInlineContextW(HANDLE hProcess, DWORD64 addr, ULONG inline_ctx, DWORD64 mod_addr, PDWORD disp, PIMAGEHLP_LINEW64 line) { - struct internal_line_t intl; + struct lineinfo_t line_info; TRACE("(%p, %#I64x, 0x%lx, %#I64x, %p, %p)\n", hProcess, addr, inline_ctx, mod_addr, disp, line); if (line->SizeOfStruct < sizeof(*line)) return FALSE; - init_internal_line(&intl, TRUE); + init_lineinfo(&line_info, TRUE); - if (!get_line_from_inline_context(hProcess, addr, inline_ctx, mod_addr, disp, &intl)) return FALSE; - return internal_line_copy_toW64(&intl, line); + if (!get_line_from_inline_context(hProcess, addr, inline_ctx, mod_addr, disp, &line_info)) return FALSE; + return lineinfo_copy_toW64(&line_info, line); } /****************************************************************** From d71987cda3c5531761650a1abf47a5b90f7402d6 Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Fri, 21 Mar 2025 18:24:23 +0100 Subject: [PATCH 238/454] dbghelp: Always copy the source file string. Note: we were using string stored in source hash tree in line information. Better always copy into the internal buffer to avoid dbghelp caller to temper with internal data. Signed-off-by: Eric Pouech (cherry picked from commit 8470325d911c09c458179029dc9e258d9837fa98) --- dlls/dbghelp/symbol.c | 34 ++++++++++++---------------------- 1 file changed, 12 insertions(+), 22 deletions(-) diff --git a/dlls/dbghelp/symbol.c b/dlls/dbghelp/symbol.c index 52c284f2c1ea..70b20b073bba 100644 --- a/dlls/dbghelp/symbol.c +++ b/dlls/dbghelp/symbol.c @@ -1806,7 +1806,7 @@ static BOOL lineinfo_copy_toW64(const struct lineinfo_t* line_info, IMAGEHLP_LIN return TRUE; } -static BOOL lineinfo_set_nameA(struct process* pcs, struct lineinfo_t* line_info, char* str, BOOL copy) +static BOOL lineinfo_set_nameA(struct process* pcs, struct lineinfo_t* line_info, char* str) { DWORD len; @@ -1818,32 +1818,22 @@ static BOOL lineinfo_set_nameA(struct process* pcs, struct lineinfo_t* line_info } else { - if (copy) - { - len = strlen(str) + 1; - if (!(line_info->file_nameA = fetch_buffer(pcs, len))) return FALSE; - memcpy(line_info->file_nameA, str, len); - } - else - line_info->file_nameA = str; + len = strlen(str) + 1; + if (!(line_info->file_nameA = fetch_buffer(pcs, len))) return FALSE; + memcpy(line_info->file_nameA, str, len); } return TRUE; } -static BOOL lineinfo_set_nameW(struct process* pcs, struct lineinfo_t* line_info, WCHAR* wstr, BOOL copy) +static BOOL lineinfo_set_nameW(struct process* pcs, struct lineinfo_t* line_info, WCHAR* wstr) { DWORD len; if (line_info->unicode) { - if (copy) - { - len = (lstrlenW(wstr) + 1) * sizeof(WCHAR); - if (!(line_info->file_nameW = fetch_buffer(pcs, len))) return FALSE; - memcpy(line_info->file_nameW, wstr, len); - } - else - line_info->file_nameW = wstr; + len = (lstrlenW(wstr) + 1) * sizeof(WCHAR); + if (!(line_info->file_nameW = fetch_buffer(pcs, len))) return FALSE; + memcpy(line_info->file_nameW, wstr, len); } else { @@ -1879,12 +1869,12 @@ static BOOL get_line_from_function(struct module_pair* pair, struct symt_functio if (dbghelp_opt_source_actual_path) { /* Return native file paths when using winedbg */ - ret = lineinfo_set_nameA(pair->pcs, line_info, (char*)source_get(pair->effective, dli->u.source_file), FALSE); + ret = lineinfo_set_nameA(pair->pcs, line_info, (char*)source_get(pair->effective, dli->u.source_file)); } else { WCHAR *dospath = wine_get_dos_file_name(source_get(pair->effective, dli->u.source_file)); - ret = lineinfo_set_nameW(pair->pcs, line_info, dospath, TRUE); + ret = lineinfo_set_nameW(pair->pcs, line_info, dospath); HeapFree( GetProcessHeap(), 0, dospath ); } if (ret && pdwDisplacement) *pdwDisplacement = addr - found_dli->u.address; @@ -2032,7 +2022,7 @@ static BOOL symt_get_func_line_prev(HANDLE hProcess, struct lineinfo_t* line_inf /* search source file */ for (srcli = li; !srcli->is_source_file; srcli--); - return lineinfo_set_nameA(pair.pcs, line_info, (char*)source_get(pair.effective, srcli->u.source_file), FALSE); + return lineinfo_set_nameA(pair.pcs, line_info, (char*)source_get(pair.effective, srcli->u.source_file)); } } SetLastError(ERROR_NO_MORE_ITEMS); /* FIXME */ @@ -2108,7 +2098,7 @@ static BOOL symt_get_func_line_next(HANDLE hProcess, struct lineinfo_t* line_inf line_info->line_number = li->line_number; line_info->address = li->u.address; line_info->key = li; - return lineinfo_set_nameA(pair.pcs, line_info, (char*)source_get(pair.effective, srcli->u.source_file), FALSE); + return lineinfo_set_nameA(pair.pcs, line_info, (char*)source_get(pair.effective, srcli->u.source_file)); } srcli = li; } From db5645bcbb59fad01a69dd1c7ad836d60e19538b Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Fri, 8 Nov 2024 14:32:34 +0100 Subject: [PATCH 239/454] dbghelp: Introduce interface for line info access. Introduce two new module_format methods: - source_info_search_by_addr - source_info_advance And move the current line info from vector inside SymTagFunction into it for new PDB reader. Signed-off-by: Eric Pouech (cherry picked from commit 9ec5389865d7338fe7e3700a5dc0d0911a2bc78c) --- dlls/dbghelp/dbghelp_private.h | 20 +- dlls/dbghelp/msc.c | 6 +- dlls/dbghelp/pdb.c | 332 ++++++++++++++++++++++++++++++++- dlls/dbghelp/symbol.c | 67 ++++--- 4 files changed, 393 insertions(+), 32 deletions(-) diff --git a/dlls/dbghelp/dbghelp_private.h b/dlls/dbghelp/dbghelp_private.h index 809199a9ac5e..b49ce8564cda 100644 --- a/dlls/dbghelp/dbghelp_private.h +++ b/dlls/dbghelp/dbghelp_private.h @@ -414,7 +414,21 @@ enum format_info DFI_LAST }; +struct lineinfo_t +{ + BOOL unicode; + PVOID key; + DWORD line_number; + union + { + CHAR* file_nameA; + WCHAR* file_nameW; + }; + DWORD64 address; +}; + struct module_format; +enum method_result {MR_SUCCESS, MR_FAILURE, MR_NOT_FOUND}; struct module_format_vtable { /* module handling */ @@ -423,6 +437,9 @@ struct module_format_vtable void (*loc_compute)(const struct module_format* modfmt, const struct symt_function* func, struct location* loc); + /* line information */ + enum method_result (*get_line_from_address)(struct module_format *modfmt, + DWORD64 address, struct lineinfo_t *line_info); }; struct module_format @@ -941,6 +958,7 @@ extern DWORD symt_ptr2index(struct module* module, const struct symt* sym extern struct symt_custom* symt_new_custom(struct module* module, const char* name, DWORD64 addr, DWORD size); +extern BOOL lineinfo_set_nameA(struct process* pcs, struct lineinfo_t* intl, char* str); /* type.c */ extern void symt_init_basic(struct module* module); @@ -1026,4 +1044,4 @@ extern BOOL pdb_old_virtual_unwind(struct cpu_stack_walk *csw, DWORD_PTR ip, struct pdb_reader; extern BOOL pdb_hack_get_main_info(struct module_format *modfmt, struct pdb_reader **pdb, unsigned *fpoext_stream); extern void pdb_reader_dispose(struct pdb_reader *pdb); -extern struct pdb_reader *pdb_hack_reader_init(struct module *module, HANDLE file); +extern struct pdb_reader *pdb_hack_reader_init(struct module *module, HANDLE file, const IMAGE_SECTION_HEADER *sections, unsigned num_sections); diff --git a/dlls/dbghelp/msc.c b/dlls/dbghelp/msc.c index db06b186edb6..5fd0b28894ec 100644 --- a/dlls/dbghelp/msc.c +++ b/dlls/dbghelp/msc.c @@ -3815,7 +3815,7 @@ static BOOL pdb_process_internal(const struct process *pcs, pdb_file->pdb_reader = NULL; CloseHandle(hFile); } - else if (!(pdb_file->pdb_reader = pdb_hack_reader_init(msc_dbg->module, hFile))) + else if (!(pdb_file->pdb_reader = pdb_hack_reader_init(msc_dbg->module, hFile, msc_dbg->sectp, msc_dbg->nsect))) { CloseHandle(hFile); UnmapViewOfFile(image); @@ -3971,7 +3971,7 @@ static BOOL pdb_process_internal(const struct process *pcs, return TRUE; } -static const struct module_format_vtable pdb_module_format_vtable = +static const struct module_format_vtable old_pdb_module_format_vtable = { pdb_module_remove, pdb_location_compute, @@ -3996,7 +3996,7 @@ static BOOL pdb_process_file(const struct process *pcs, pdb_module_info = (void*)(modfmt + 1); msc_dbg->module->format_info[DFI_PDB] = modfmt; modfmt->module = msc_dbg->module; - modfmt->vtable = &pdb_module_format_vtable; + modfmt->vtable = &old_pdb_module_format_vtable; modfmt->u.pdb_info = pdb_module_info; memset(cv_zmodules, 0, sizeof(cv_zmodules)); diff --git a/dlls/dbghelp/pdb.c b/dlls/dbghelp/pdb.c index 7948dfd5f0b7..994cae3c0e88 100644 --- a/dlls/dbghelp/pdb.c +++ b/dlls/dbghelp/pdb.c @@ -84,6 +84,12 @@ struct pdb_reader } *streams; char *stream_names; + /* cache PE module sections for mapping... + * we should rather use pe_module information + */ + const IMAGE_SECTION_HEADER *sections; + unsigned num_sections; + pdb_reader_fetch_t fetch; }; @@ -94,6 +100,8 @@ enum pdb_result R_PDB_OUT_OF_MEMORY, R_PDB_INVALID_ARGUMENT, R_PDB_INVALID_PDB_FILE, + R_PDB_MISSING_INFORMATION, + R_PDB_NOT_FOUND, R_PDB_BUFFER_TOO_SMALL, }; @@ -108,6 +116,14 @@ static enum pdb_result pdb_reader_fetch_file(struct pdb_reader *pdb, void *buffe static const char PDB_JG_IDENT[] = "Microsoft C/C++ program database 2.00\r\n\032JG\0"; static const char PDB_DS_IDENT[] = "Microsoft C/C++ MSF 7.00\r\n\032DS\0"; +static enum pdb_result pdb_reader_get_segment_address(struct pdb_reader *pdb, unsigned segment, unsigned offset, DWORD64 *address) +{ + if (!segment || segment > pdb->num_sections) return R_PDB_INVALID_PDB_FILE; + *address = pdb->module->module.BaseOfImage + + pdb->sections[segment - 1].VirtualAddress + offset; + return R_PDB_SUCCESS; +} + static inline enum pdb_result pdb_reader_alloc(struct pdb_reader *pdb, size_t size, void **ptr) { return (*ptr = pool_alloc(&pdb->pool, size)) ? R_PDB_SUCCESS : R_PDB_OUT_OF_MEMORY; @@ -267,6 +283,7 @@ static enum pdb_result pdb_reader_init(struct pdb_reader *pdb, struct module *mo void pdb_reader_dispose(struct pdb_reader *pdb) { CloseHandle(pdb->file); + /* note: pdb is allocated inside its pool, so this must be last line */ pool_destroy(&pdb->pool); } @@ -274,9 +291,12 @@ static enum pdb_result pdb_reader_internal_read_advance(struct pdb_reader *pdb, void *buffer, pdbsize_t size) { pdbsize_t num_read; - enum pdb_result result = pdb_reader_read_from_stream(pdb, walker, buffer, size, &num_read); + enum pdb_result result; + + if (walker->offset + size > walker->last) return R_PDB_INVALID_ARGUMENT; + result = pdb_reader_read_from_stream(pdb, walker, buffer, size, &num_read); if (result) return result; - if (num_read != size) return R_PDB_INVALID_ARGUMENT; + if (num_read != size) return R_PDB_IOERROR; walker->offset += size; return R_PDB_SUCCESS; } @@ -306,6 +326,8 @@ static enum pdb_result pdb_reader_fetch_string_from_stream(struct pdb_reader *pd pdbsize_t num_read; char *zero; + if (walker->offset + length > walker->last) + length = walker->last - walker->offset; if ((result = pdb_reader_read_from_stream(pdb, walker, buffer, length, &num_read))) return result; if (!(zero = memchr(buffer, '\0', num_read))) @@ -330,7 +352,7 @@ static enum pdb_result pdb_reader_load_stream_name_table(struct pdb_reader *pdb) if (ds_root.Version != 20000404) { ERR("-Unknown root block version %u\n", ds_root.Version); - return R_PDB_INVALID_ARGUMENT; + return R_PDB_INVALID_PDB_FILE; } if ((result = pdb_reader_alloc_and_read(pdb, &walker, ds_root.cbNames, (void**)&pdb->stream_names))) @@ -388,7 +410,7 @@ static enum pdb_result pdb_reader_get_stream_index_from_name(struct pdb_reader * *stream_id = i; return R_PDB_SUCCESS; } - return R_PDB_INVALID_ARGUMENT; + return R_PDB_NOT_FOUND; } static enum pdb_result pdb_reader_alloc_and_fetch_string(struct pdb_reader *pdb, struct pdb_reader_walker *walker, char **string) @@ -420,6 +442,19 @@ static enum pdb_result pdb_reader_alloc_and_fetch_string(struct pdb_reader *pdb, } } +static enum pdb_result pdb_reader_skip_string(struct pdb_reader *pdb, struct pdb_reader_walker *walker) +{ + char tmp[256]; + enum pdb_result result; + + while ((result = pdb_reader_fetch_string_from_stream(pdb, walker, tmp, ARRAY_SIZE(tmp)))) + { + if (result != R_PDB_BUFFER_TOO_SMALL) break; + walker->offset += ARRAY_SIZE(tmp); + } + return result; +} + static enum pdb_result pdb_reader_alloc_and_fetch_global_string(struct pdb_reader *pdb, pdbsize_t str_offset, char **buffer) { enum pdb_result result; @@ -433,10 +468,293 @@ static enum pdb_result pdb_reader_alloc_and_fetch_global_string(struct pdb_reade return pdb_reader_alloc_and_fetch_string(pdb, &walker, buffer); } -struct pdb_reader *pdb_hack_reader_init(struct module *module, HANDLE file) +static enum pdb_result pdb_reader_read_DBI_header(struct pdb_reader* pdb, PDB_SYMBOLS *dbi_header, struct pdb_reader_walker *walker) +{ + enum pdb_result result; + + /* assuming we always have that size (even for old format) in stream */ + if ((result = pdb_reader_walker_init(pdb, 3, walker)) || + (result = pdb_reader_READ(pdb, walker, dbi_header))) return result; + if (dbi_header->signature != 0xffffffff) + { + /* Old version of the symbols record header */ + PDB_SYMBOLS_OLD old_dbi_header = *(const PDB_SYMBOLS_OLD*)dbi_header; + + dbi_header->version = 0; + dbi_header->module_size = old_dbi_header.module_size; + dbi_header->sectcontrib_size = old_dbi_header.sectcontrib_size; + dbi_header->segmap_size = old_dbi_header.segmap_size; + dbi_header->srcmodule_size = old_dbi_header.srcmodule_size; + dbi_header->pdbimport_size = 0; + dbi_header->global_hash_stream = old_dbi_header.global_hash_stream; + dbi_header->public_stream = old_dbi_header.public_stream; + dbi_header->gsym_stream = old_dbi_header.gsym_stream; + + walker->offset = sizeof(PDB_SYMBOLS_OLD); + } + + return R_PDB_SUCCESS; +} + +static enum pdb_result pdb_reader_read_DBI_cu_header(struct pdb_reader* pdb, DWORD dbi_header_version, + struct pdb_reader_walker *walker, + PDB_SYMBOL_FILE_EX *dbi_cu_header) +{ + enum pdb_result result; + + if (dbi_header_version >= 19970000) + { + result = pdb_reader_READ(pdb, walker, dbi_cu_header); + } + else + { + PDB_SYMBOL_FILE old_dbi_cu_header; + if (!(result = pdb_reader_READ(pdb, walker, &old_dbi_cu_header))) + { + memset(dbi_cu_header, 0, sizeof(*dbi_cu_header)); + dbi_cu_header->stream = old_dbi_cu_header.stream; + dbi_cu_header->range.index = old_dbi_cu_header.range.index; + dbi_cu_header->symbol_size = old_dbi_cu_header.symbol_size; + dbi_cu_header->lineno_size = old_dbi_cu_header.lineno_size; + dbi_cu_header->lineno2_size = old_dbi_cu_header.lineno2_size; + } + } + return result; +} + +struct pdb_reader_compiland_iterator +{ + struct pdb_reader_walker dbi_walker; /* in DBI stream */ + PDB_SYMBOLS dbi_header; + PDB_SYMBOL_FILE_EX dbi_cu_header; +}; + +static enum pdb_result pdb_reader_compiland_iterator_init(struct pdb_reader *pdb, struct pdb_reader_compiland_iterator *iter) { - struct pdb_reader *pdb = pool_alloc(&module->pool, sizeof(*pdb)); - if (pdb && pdb_reader_init(pdb, module, file) == R_PDB_SUCCESS) return pdb; + enum pdb_result result; + if ((result = pdb_reader_read_DBI_header(pdb, &iter->dbi_header, &iter->dbi_walker))) return result; + iter->dbi_walker.last = iter->dbi_walker.offset + iter->dbi_header.module_size; + return pdb_reader_read_DBI_cu_header(pdb, iter->dbi_header.version, &iter->dbi_walker, &iter->dbi_cu_header); +} + +static enum pdb_result pdb_reader_compiland_iterator_next(struct pdb_reader *pdb, struct pdb_reader_compiland_iterator *iter) +{ + enum pdb_result result; + + if ((result = pdb_reader_skip_string(pdb, &iter->dbi_walker)) || + (result = pdb_reader_skip_string(pdb, &iter->dbi_walker))) + { + return result; + } + iter->dbi_walker.offset = (iter->dbi_walker.offset + 3) & ~3u; + return pdb_reader_read_DBI_cu_header(pdb, iter->dbi_header.version, &iter->dbi_walker, &iter->dbi_cu_header); +} + +static enum pdb_result pdb_reader_subsection_next(struct pdb_reader *pdb, struct pdb_reader_walker *in_walker, + enum DEBUG_S_SUBSECTION_TYPE subsection_type, + struct pdb_reader_walker *sub_walker) +{ + enum pdb_result result; + struct CV_DebugSSubsectionHeader_t hdr; + + for (; !(result = pdb_reader_READ(pdb, in_walker, &hdr)); in_walker->offset += hdr.cbLen) + { + if (hdr.type & DEBUG_S_IGNORE) continue; + if (subsection_type && hdr.type != subsection_type) continue; + *sub_walker = *in_walker; + sub_walker->last = sub_walker->offset + hdr.cbLen; + in_walker->offset += hdr.cbLen; + return R_PDB_SUCCESS; + } + return result && result != R_PDB_INVALID_ARGUMENT ? result : R_PDB_NOT_FOUND; +} + +struct pdb_reader_linetab2_location +{ + pdbsize_t dbi_cu_header_offset; /* in DBI stream */ + unsigned cu_stream_id; /* compilation unit stream id */ + pdbsize_t lines_hdr_offset; /* in cu_stream_id */ + pdbsize_t file_hdr_offset; /* in cu_stream_id (inside lines block) */ + pdbsize_t filename_offset; /* in global stream table (after S_FILECHKSUMS redirection) */ +}; + +static enum pdb_result pdb_find_matching_linetab2(struct CV_Line_t *lines, unsigned num_lines, DWORD64 delta, unsigned *index) +{ + unsigned i; + for (i = 0; i + 1 < num_lines; i++) + { + unsigned j; + for (j = i + 1; j < num_lines; j++) + if (lines[j].offset != lines[i].offset) break; + if (j >= num_lines) break; + if (delta < lines[j].offset) + { + *index = i; + return R_PDB_SUCCESS; + } + } + /* since the the address is inside the file_hdr, we assume then it's matched by last entry + * (for which we don't have the next entry) + */ + *index = num_lines - 1; + return R_PDB_SUCCESS; +} + +static enum pdb_result pdb_reader_walker_init_linetab2(struct pdb_reader *pdb, const PDB_SYMBOL_FILE_EX *dbi_cu_header, struct pdb_reader_walker *walker) +{ + walker->stream_id = dbi_cu_header->stream; + walker->offset = dbi_cu_header->symbol_size; + walker->last = walker->offset + dbi_cu_header->lineno2_size; + return R_PDB_SUCCESS; +} + +static enum pdb_result pdb_reader_locate_filehdr_in_linetab2(struct pdb_reader *pdb, struct pdb_reader_walker linetab2_walker, + DWORD64 address, DWORD64 *lineblk_base, + struct CV_DebugSLinesFileBlockHeader_t *files_hdr, struct CV_Line_t **lines) +{ + struct pdb_reader_walker sub_walker; + enum pdb_result result; + struct CV_DebugSLinesHeader_t lines_hdr; + + while (!(result = pdb_reader_subsection_next(pdb, &linetab2_walker, DEBUG_S_LINES, &sub_walker))) + { + /* Skip blocks that are too small - Intel C Compiler generates these. */ + if (sub_walker.offset + sizeof(lines_hdr) + sizeof(struct CV_DebugSLinesFileBlockHeader_t) > sub_walker.last) + continue; + if ((result = pdb_reader_READ(pdb, &sub_walker, &lines_hdr))) return result; + if ((result = pdb_reader_get_segment_address(pdb, lines_hdr.segCon, lines_hdr.offCon, lineblk_base))) + return result; + if (*lineblk_base > address || address >= *lineblk_base + lines_hdr.cbCon) + continue; + if ((result = pdb_reader_READ(pdb, &sub_walker, files_hdr))) return result; + return pdb_reader_alloc_and_read(pdb, &sub_walker, files_hdr->nLines * sizeof((*lines)[0]),(void**)lines); + /* + if (lines_hdr.flags & CV_LINES_HAVE_COLUMNS) + sub_walker.offset += files_hdr.nLines * sizeof(struct CV_Column_t); + */ + } + return R_PDB_NOT_FOUND; +} + +static enum pdb_result pdb_reader_set_lineinfo_filename(struct pdb_reader *pdb, struct pdb_reader_walker linetab2_walker, + unsigned file_offset, struct lineinfo_t *line_info) +{ + struct pdb_reader_walker checksum_walker; + struct CV_Checksum_t checksum; + enum pdb_result result; + char *string; + + if ((result = pdb_reader_subsection_next(pdb, &linetab2_walker, DEBUG_S_FILECHKSMS, &checksum_walker))) + { + WARN("No DEBUG_S_FILECHKSMS found\n"); + return R_PDB_MISSING_INFORMATION; + } + checksum_walker.offset += file_offset; + if ((result = pdb_reader_READ(pdb, &checksum_walker, &checksum))) return result; + if ((result = pdb_reader_alloc_and_fetch_global_string(pdb, checksum.strOffset, &string))) return result; + if (!lineinfo_set_nameA(pdb->module->process, line_info, string)) + result = R_PDB_OUT_OF_MEMORY; + pdb_reader_free(pdb, string); + return result; +} + +static enum pdb_result pdb_reader_search_linetab2(struct pdb_reader *pdb, const PDB_SYMBOL_FILE_EX *dbi_cu_header, + DWORD64 address, struct lineinfo_t *line_info) +{ + struct pdb_reader_walker linetab2_walker; + struct CV_DebugSLinesFileBlockHeader_t files_hdr; + enum pdb_result result; + DWORD64 lineblk_base; + struct CV_Line_t *lines; + + if ((result = pdb_reader_walker_init_linetab2(pdb, dbi_cu_header, &linetab2_walker))) return result; + + if (!pdb_reader_locate_filehdr_in_linetab2(pdb, linetab2_walker, address, &lineblk_base, &files_hdr, &lines)) + { + unsigned i; + + if (!pdb_find_matching_linetab2(lines, files_hdr.nLines, address - lineblk_base, &i)) + { + /* found block... */ + line_info->address = lineblk_base + lines[i].offset; + line_info->line_number = lines[i].linenumStart; + return pdb_reader_set_lineinfo_filename(pdb, linetab2_walker, files_hdr.offFile, line_info); + } + pdb_reader_free(pdb, lines); + } + return R_PDB_NOT_FOUND; +} + +static enum pdb_result pdb_reader_get_line_from_address_internal(struct pdb_reader *pdb, + DWORD64 address, struct lineinfo_t *line_info, + pdbsize_t *compiland_offset) +{ + enum pdb_result result; + struct pdb_reader_compiland_iterator compiland_iter; + + if ((result = pdb_reader_compiland_iterator_init(pdb, &compiland_iter))) return result; + do + { + if (compiland_iter.dbi_cu_header.lineno2_size) + { + result = pdb_reader_search_linetab2(pdb, &compiland_iter.dbi_cu_header, address, line_info); + if (!result) + { + *compiland_offset = compiland_iter.dbi_walker.offset - sizeof(compiland_iter.dbi_cu_header); + return result; + } + if (result != R_PDB_NOT_FOUND) return result; + } + } while (pdb_reader_compiland_iterator_next(pdb, &compiland_iter) == R_PDB_SUCCESS); + + return R_PDB_NOT_FOUND; +} + +static enum method_result pdb_method_result(enum pdb_result result) +{ + switch (result) + { + case R_PDB_SUCCESS: return MR_SUCCESS; + case R_PDB_NOT_FOUND: return MR_NOT_FOUND; + default: return MR_FAILURE; + } +} + +static enum method_result pdb_method_get_line_from_address(struct module_format *modfmt, + DWORD64 address, struct lineinfo_t *line_info) +{ + enum pdb_result result; + struct pdb_reader *pdb; + pdbsize_t compiland_offset; + + if (!pdb_hack_get_main_info(modfmt, &pdb, NULL)) return MR_FAILURE; + result = pdb_reader_get_line_from_address_internal(pdb, address, line_info, &compiland_offset); + return pdb_method_result(result); +} + +static struct module_format_vtable pdb_module_format_vtable = +{ + NULL,/*pdb_module_remove*/ + NULL,/*pdb_location_compute*/ + pdb_method_get_line_from_address, +}; + +struct pdb_reader *pdb_hack_reader_init(struct module *module, HANDLE file, const IMAGE_SECTION_HEADER *sections, unsigned num_sections) +{ + struct pdb_reader *pdb = pool_alloc(&module->pool, sizeof(*pdb) + num_sections * sizeof(*sections)); + if (pdb && pdb_reader_init(pdb, module, file) == R_PDB_SUCCESS) + { + pdb->sections = (void*)(pdb + 1); + memcpy((void *)pdb->sections, sections, num_sections * sizeof(*sections)); + pdb->num_sections = num_sections; + pdb->module = module; + /* hack (copy old pdb methods until they are moved here) */ + pdb_module_format_vtable.remove = module->format_info[DFI_PDB]->vtable->remove; + pdb_module_format_vtable.loc_compute = module->format_info[DFI_PDB]->vtable->loc_compute; + + module->format_info[DFI_PDB]->vtable = &pdb_module_format_vtable; + return pdb; + } pool_free(&module->pool, pdb); return NULL; } diff --git a/dlls/dbghelp/symbol.c b/dlls/dbghelp/symbol.c index 70b20b073bba..5c3b01be40e4 100644 --- a/dlls/dbghelp/symbol.c +++ b/dlls/dbghelp/symbol.c @@ -1754,19 +1754,6 @@ BOOL WINAPI SymGetSymFromName(HANDLE hProcess, PCSTR Name, PIMAGEHLP_SYMBOL Symb return TRUE; } -struct lineinfo_t -{ - BOOL unicode; - PVOID key; - DWORD line_number; - union - { - CHAR* file_nameA; - WCHAR* file_nameW; - }; - DWORD64 address; -}; - static void init_lineinfo(struct lineinfo_t* line_info, BOOL unicode) { line_info->unicode = unicode; @@ -1806,7 +1793,7 @@ static BOOL lineinfo_copy_toW64(const struct lineinfo_t* line_info, IMAGEHLP_LIN return TRUE; } -static BOOL lineinfo_set_nameA(struct process* pcs, struct lineinfo_t* line_info, char* str) +BOOL lineinfo_set_nameA(struct process* pcs, struct lineinfo_t* line_info, char* str) { DWORD len; @@ -1892,14 +1879,27 @@ static BOOL get_line_from_function(struct module_pair* pair, struct symt_functio static BOOL get_line_from_addr(HANDLE hProcess, DWORD64 addr, PDWORD pdwDisplacement, struct lineinfo_t* line_info) { - struct module_pair pair; - struct symt_ht* symt; - + struct module_pair pair; + struct symt_ht* symt; + struct module_format_vtable_iterator iter = {}; + BOOL ret = FALSE; if (!module_init_pair(&pair, hProcess, addr)) return FALSE; + + while ((module_format_vtable_iterator_next(pair.effective, &iter, + MODULE_FORMAT_VTABLE_INDEX(get_line_from_address)))) + { + if (iter.modfmt->vtable->get_line_from_address(iter.modfmt, addr, line_info) == MR_SUCCESS) + { + if (pdwDisplacement) *pdwDisplacement = addr - line_info->address; + return TRUE; + } + } + symt = symt_find_symbol_at(pair.effective, addr); + if (symt_check_tag(&symt->symt, SymTagFunction)) + ret = get_line_from_function(&pair, (struct symt_function*)symt, addr, pdwDisplacement, line_info); - if (!symt_check_tag(&symt->symt, SymTagFunction)) return FALSE; - return get_line_from_function(&pair, (struct symt_function*)symt, addr, pdwDisplacement, line_info); + return ret; } /*********************************************************************** @@ -2006,7 +2006,19 @@ static BOOL symt_get_func_line_prev(HANDLE hProcess, struct lineinfo_t* line_inf struct line_info* srcli; if (!module_init_pair(&pair, hProcess, addr)) return FALSE; - + if (key == NULL) + { + struct symt_ht* symt; + /* likely line_info has been filled in by pdb reader, but it can advance yet + * so force reloading the old information + */ + symt = symt_find_symbol_at(pair.effective, addr); + if (!symt_check_tag(&symt->symt, SymTagFunction) || + !get_line_from_function(&pair, (struct symt_function*)symt, addr, NULL, line_info)) + return FALSE; + if (line_info->key == NULL) return FALSE; + key = line_info->key; + } if (key == NULL) return FALSE; li = key; @@ -2083,8 +2095,21 @@ static BOOL symt_get_func_line_next(HANDLE hProcess, struct lineinfo_t* line_inf struct line_info* li; struct line_info* srcli; - if (key == NULL) return FALSE; if (!module_init_pair(&pair, hProcess, addr)) return FALSE; + if (key == NULL) + { + struct symt_ht* symt; + /* likely line_info has been filled in by pdb reader, but it can advance yet + * so force reloading the information from old reader + */ + symt = symt_find_symbol_at(pair.effective, addr); + if (!symt_check_tag(&symt->symt, SymTagFunction) || + !get_line_from_function(&pair, (struct symt_function*)symt, addr, NULL, line_info)) + return FALSE; + if (line_info->key == NULL) return FALSE; + key = line_info->key; + } + if (key == NULL) return FALSE; /* search current source file */ for (srcli = key; !srcli->is_source_file; srcli--); From 3ed7fac22d95b50614c0902ddc9be5075609a3cf Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Fri, 22 Nov 2024 13:51:11 +0100 Subject: [PATCH 240/454] dbghelp: Introduce method to get next/prev line information. Implement it in new PDB reader. Signed-off-by: Eric Pouech (cherry picked from commit f26145da7e53e474a2dc029b68c0f691bcd0e70e) --- dlls/dbghelp/dbghelp_private.h | 2 + dlls/dbghelp/pdb.c | 74 ++++++++++++++++++++++++++++++++++ dlls/dbghelp/symbol.c | 48 ++++++++++++---------- 3 files changed, 102 insertions(+), 22 deletions(-) diff --git a/dlls/dbghelp/dbghelp_private.h b/dlls/dbghelp/dbghelp_private.h index b49ce8564cda..f4eaed8cd2b3 100644 --- a/dlls/dbghelp/dbghelp_private.h +++ b/dlls/dbghelp/dbghelp_private.h @@ -440,6 +440,8 @@ struct module_format_vtable /* line information */ enum method_result (*get_line_from_address)(struct module_format *modfmt, DWORD64 address, struct lineinfo_t *line_info); + enum method_result (*advance_line_info)(struct module_format *modfmt, + struct lineinfo_t *line_info, BOOL forward); }; struct module_format diff --git a/dlls/dbghelp/pdb.c b/dlls/dbghelp/pdb.c index 994cae3c0e88..76e0ed413599 100644 --- a/dlls/dbghelp/pdb.c +++ b/dlls/dbghelp/pdb.c @@ -732,11 +732,85 @@ static enum method_result pdb_method_get_line_from_address(struct module_format return pdb_method_result(result); } +static enum pdb_result pdb_reader_advance_line_info(struct pdb_reader *pdb, + struct lineinfo_t *line_info, BOOL forward) +{ + struct pdb_reader_compiland_iterator compiland_iter; + struct pdb_reader_walker linetab2_walker; + struct CV_DebugSLinesFileBlockHeader_t files_hdr; + DWORD64 lineblk_base; + struct CV_Line_t *lines; + enum pdb_result result; + unsigned i; + + if ((result = pdb_reader_compiland_iterator_init(pdb, &compiland_iter))) + return result; + do + { + if (compiland_iter.dbi_cu_header.lineno2_size) + { + if ((result = pdb_reader_walker_init_linetab2(pdb, &compiland_iter.dbi_cu_header, &linetab2_walker))) + return result; + result = pdb_reader_locate_filehdr_in_linetab2(pdb, linetab2_walker, line_info->address, &lineblk_base, &files_hdr, &lines); + if (result == R_PDB_NOT_FOUND) continue; + if (result) return result; + if ((result = pdb_find_matching_linetab2(lines, files_hdr.nLines, line_info->address - lineblk_base, &i))) + return result; + + /* It happens that several entries have same address (yet potentially different line numbers) + * Simplify handling by getting the first entry (forward or backward) with a different address. + * More tests from native are required. + */ + if (forward) + { + for (; i + 1 < files_hdr.nLines; i++) + if (line_info->address != lineblk_base + lines[i + 1].offset) + { + line_info->address = lineblk_base + lines[i + 1].offset; + line_info->line_number = lines[i + 1].linenumStart; + break; + } + if (i + 1 >= files_hdr.nLines) + result = R_PDB_INVALID_ARGUMENT; + } + else + { + for (; i; --i) + { + if (line_info->address != lineblk_base + lines[i - 1].offset) + { + line_info->address = lineblk_base + lines[i - 1].offset; + line_info->line_number = lines[i - 1].linenumStart; + break; + } + } + if (!i) + result = R_PDB_INVALID_ARGUMENT; + } + pdb_reader_free(pdb, lines); + /* refresh filename in case it has been tempered with */ + return result ? result : pdb_reader_set_lineinfo_filename(pdb, linetab2_walker, files_hdr.offFile, line_info); + } + } while (pdb_reader_compiland_iterator_next(pdb, &compiland_iter) == R_PDB_SUCCESS); + + return R_PDB_NOT_FOUND; +} + +static enum method_result pdb_method_advance_line_info(struct module_format *modfmt, + struct lineinfo_t *line_info, BOOL forward) +{ + struct pdb_reader *pdb; + + if (!pdb_hack_get_main_info(modfmt, &pdb, NULL)) return MR_FAILURE; + return pdb_reader_advance_line_info(pdb, line_info, forward) == R_PDB_SUCCESS ? MR_SUCCESS : MR_FAILURE; +} + static struct module_format_vtable pdb_module_format_vtable = { NULL,/*pdb_module_remove*/ NULL,/*pdb_location_compute*/ pdb_method_get_line_from_address, + pdb_method_advance_line_info, }; struct pdb_reader *pdb_hack_reader_init(struct module *module, HANDLE file, const IMAGE_SECTION_HEADER *sections, unsigned num_sections) diff --git a/dlls/dbghelp/symbol.c b/dlls/dbghelp/symbol.c index 5c3b01be40e4..58a7338aed7a 100644 --- a/dlls/dbghelp/symbol.c +++ b/dlls/dbghelp/symbol.c @@ -2004,20 +2004,24 @@ static BOOL symt_get_func_line_prev(HANDLE hProcess, struct lineinfo_t* line_inf struct module_pair pair; struct line_info* li; struct line_info* srcli; + struct module_format_vtable_iterator iter = {}; if (!module_init_pair(&pair, hProcess, addr)) return FALSE; - if (key == NULL) + + line_info->address = addr; + line_info->key = key; + while ((module_format_vtable_iterator_next(pair.effective, &iter, + MODULE_FORMAT_VTABLE_INDEX(advance_line_info)))) { - struct symt_ht* symt; - /* likely line_info has been filled in by pdb reader, but it can advance yet - * so force reloading the old information - */ - symt = symt_find_symbol_at(pair.effective, addr); - if (!symt_check_tag(&symt->symt, SymTagFunction) || - !get_line_from_function(&pair, (struct symt_function*)symt, addr, NULL, line_info)) + switch (iter.modfmt->vtable->advance_line_info(iter.modfmt, line_info, FALSE)) + { + case MR_SUCCESS: + return TRUE; + case MR_NOT_FOUND: /* continue */ + break; + default: return FALSE; - if (line_info->key == NULL) return FALSE; - key = line_info->key; + } } if (key == NULL) return FALSE; @@ -2094,23 +2098,23 @@ static BOOL symt_get_func_line_next(HANDLE hProcess, struct lineinfo_t* line_inf struct module_pair pair; struct line_info* li; struct line_info* srcli; + struct module_format_vtable_iterator iter = {}; if (!module_init_pair(&pair, hProcess, addr)) return FALSE; - if (key == NULL) + + line_info->address = addr; + line_info->key = key; + while ((module_format_vtable_iterator_next(pair.effective, &iter, + MODULE_FORMAT_VTABLE_INDEX(advance_line_info)))) { - struct symt_ht* symt; - /* likely line_info has been filled in by pdb reader, but it can advance yet - * so force reloading the information from old reader - */ - symt = symt_find_symbol_at(pair.effective, addr); - if (!symt_check_tag(&symt->symt, SymTagFunction) || - !get_line_from_function(&pair, (struct symt_function*)symt, addr, NULL, line_info)) - return FALSE; - if (line_info->key == NULL) return FALSE; - key = line_info->key; + switch (iter.modfmt->vtable->advance_line_info(iter.modfmt, line_info, TRUE)) + { + case MR_SUCCESS: return TRUE; + default: break; + } } - if (key == NULL) return FALSE; + if (key == NULL) return FALSE; /* search current source file */ for (srcli = key; !srcli->is_source_file; srcli--); From 344a0521fcee2677a644749df5309c1fb6e86e22 Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Fri, 22 Nov 2024 14:17:20 +0100 Subject: [PATCH 241/454] dbghelp: Introduce method to enumerate line numbers. And implement it for new PDB reader. Signed-off-by: Eric Pouech (cherry picked from commit 98da98881cb9ba589c965dbfef23e4e6156825b2) --- dlls/dbghelp/dbghelp_private.h | 2 + dlls/dbghelp/pdb.c | 121 +++++++++++++++++++++++++++++++++ dlls/dbghelp/symbol.c | 22 ++++-- 3 files changed, 141 insertions(+), 4 deletions(-) diff --git a/dlls/dbghelp/dbghelp_private.h b/dlls/dbghelp/dbghelp_private.h index f4eaed8cd2b3..35b16db01a46 100644 --- a/dlls/dbghelp/dbghelp_private.h +++ b/dlls/dbghelp/dbghelp_private.h @@ -442,6 +442,8 @@ struct module_format_vtable DWORD64 address, struct lineinfo_t *line_info); enum method_result (*advance_line_info)(struct module_format *modfmt, struct lineinfo_t *line_info, BOOL forward); + enum method_result (*enumerate_lines)(struct module_format *modfmt, const WCHAR* compiland_regex, + const WCHAR *source_file_regex, PSYM_ENUMLINES_CALLBACK cb, void *user); }; struct module_format diff --git a/dlls/dbghelp/pdb.c b/dlls/dbghelp/pdb.c index 76e0ed413599..accc6166bddd 100644 --- a/dlls/dbghelp/pdb.c +++ b/dlls/dbghelp/pdb.c @@ -805,12 +805,133 @@ static enum method_result pdb_method_advance_line_info(struct module_format *mod return pdb_reader_advance_line_info(pdb, line_info, forward) == R_PDB_SUCCESS ? MR_SUCCESS : MR_FAILURE; } +static enum pdb_result pdb_reader_enum_lines_linetab2(struct pdb_reader *pdb, const PDB_SYMBOL_FILE_EX *dbi_cu_header, + const WCHAR *source_file_regex, SRCCODEINFO *source_code_info, PSYM_ENUMLINES_CALLBACK cb, void *user) +{ + struct pdb_reader_walker linetab2_walker = {dbi_cu_header->stream, dbi_cu_header->symbol_size, dbi_cu_header->symbol_size + dbi_cu_header->lineno2_size}; + struct pdb_reader_walker walker, sub_walker, checksum_walker; + enum pdb_result result; + struct CV_DebugSLinesHeader_t lines_hdr; + struct CV_DebugSLinesFileBlockHeader_t files_hdr; + DWORD64 lineblk_base; + + for (checksum_walker = linetab2_walker; !(result = pdb_reader_subsection_next(pdb, &checksum_walker, DEBUG_S_FILECHKSMS, &sub_walker)); ) + { + checksum_walker = sub_walker; + break; + } + if (result) + { + WARN("No DEBUG_S_FILECHKSMS found\n"); + return R_PDB_MISSING_INFORMATION; + } + + for (walker = linetab2_walker; + !(result = pdb_reader_subsection_next(pdb, &walker, DEBUG_S_LINES, &sub_walker)); + ) + { + /* Skip blocks that are too small - Intel C Compiler generates these. */ + if (sub_walker.offset + sizeof(lines_hdr) + sizeof(struct CV_DebugSLinesFileBlockHeader_t) > sub_walker.last) + continue; + if ((result = pdb_reader_READ(pdb, &sub_walker, &lines_hdr))) return result; + if ((result = pdb_reader_get_segment_address(pdb, lines_hdr.segCon, lines_hdr.offCon, &lineblk_base))) + return result; + for (; (result = pdb_reader_READ(pdb, &sub_walker, &files_hdr)) == R_PDB_SUCCESS; /*lineblk_base += files_hdr.cbBlock*/) + { + pdbsize_t current_stream_offset; + struct CV_Checksum_t checksum; + struct CV_Line_t *lines; + char *string; + unsigned i; + BOOL match = TRUE; + + if ((result = pdb_reader_alloc_and_read(pdb, &sub_walker, files_hdr.nLines * sizeof(lines[0]), + (void**)&lines))) return result; + + /* should filter on filename */ + current_stream_offset = checksum_walker.offset; + checksum_walker.offset += files_hdr.offFile; + if ((result = pdb_reader_READ(pdb, &checksum_walker, &checksum))) return result; + if ((result = pdb_reader_alloc_and_fetch_global_string(pdb, checksum.strOffset, &string))) return result; + checksum_walker.offset = current_stream_offset; + + if (source_file_regex) + match = symt_match_stringAW(string, source_file_regex, FALSE); + if (!match) + { + sub_walker.offset += files_hdr.nLines * sizeof(struct CV_Line_t); + if (lines_hdr.flags & CV_LINES_HAVE_COLUMNS) + sub_walker.offset += files_hdr.nLines * sizeof(struct CV_Column_t); + continue; + } + if (strlen(string) < ARRAY_SIZE(source_code_info->FileName)) + strcpy(source_code_info->FileName, string); + else + source_code_info->FileName[0] = '\0'; + pdb_reader_free(pdb, string); + + for (i = 0; i < files_hdr.nLines; i++) + { + source_code_info->Address = lineblk_base + lines[i].offset; + source_code_info->LineNumber = lines[i].linenumStart; + if (!cb(source_code_info, user)) return R_PDB_NOT_FOUND; + } + pdb_reader_free(pdb, lines); + if (lines_hdr.flags & CV_LINES_HAVE_COLUMNS) + sub_walker.offset += files_hdr.nLines * sizeof(struct CV_Column_t); + } + } + return result == R_PDB_INVALID_ARGUMENT ? R_PDB_SUCCESS : result; +} + +static BOOL pdb_method_enumerate_lines_internal(struct pdb_reader *pdb, const WCHAR* compiland_regex, + const WCHAR *source_file_regex, PSYM_ENUMLINES_CALLBACK cb, void *user) +{ + struct pdb_reader_compiland_iterator compiland_iter; + enum pdb_result result; + SRCCODEINFO source_code_info; + + source_code_info.SizeOfStruct = sizeof(source_code_info); + source_code_info.ModBase = pdb->module->module.BaseOfImage; + + if ((result = pdb_reader_compiland_iterator_init(pdb, &compiland_iter))) return result; + do + { + struct pdb_reader_walker walker = compiland_iter.dbi_walker; + + if ((result = pdb_reader_fetch_string_from_stream(pdb, &walker, source_code_info.Obj, sizeof(source_code_info.Obj)))) + { + if (result == R_PDB_BUFFER_TOO_SMALL) FIXME("NOT EXPECTED --too small\n"); + return result; + } + + /* FIXME should filter on compiland (if present) */ + if (compiland_iter.dbi_cu_header.lineno2_size) + { + result = pdb_reader_enum_lines_linetab2(pdb, &compiland_iter.dbi_cu_header, source_file_regex, &source_code_info, cb, user); + } + + } while (pdb_reader_compiland_iterator_next(pdb, &compiland_iter) == R_PDB_SUCCESS); + return R_PDB_SUCCESS; +} + +static enum method_result pdb_method_enumerate_lines(struct module_format *modfmt, const WCHAR* compiland_regex, + const WCHAR *source_file_regex, PSYM_ENUMLINES_CALLBACK cb, void *user) +{ + struct pdb_reader *pdb; + + if (!pdb_hack_get_main_info(modfmt, &pdb, NULL)) return MR_FAILURE; + + return pdb_method_result(pdb_method_enumerate_lines_internal(pdb, compiland_regex, source_file_regex, cb, user)); +} + static struct module_format_vtable pdb_module_format_vtable = { NULL,/*pdb_module_remove*/ NULL,/*pdb_location_compute*/ pdb_method_get_line_from_address, pdb_method_advance_line_info, + pdb_method_enumerate_lines, }; struct pdb_reader *pdb_hack_reader_init(struct module *module, HANDLE file, const IMAGE_SECTION_HEADER *sections, unsigned num_sections) diff --git a/dlls/dbghelp/symbol.c b/dlls/dbghelp/symbol.c index 58a7338aed7a..5d376806c1a6 100644 --- a/dlls/dbghelp/symbol.c +++ b/dlls/dbghelp/symbol.c @@ -2550,18 +2550,32 @@ BOOL WINAPI SymEnumLines(HANDLE hProcess, ULONG64 base, PCSTR compiland, struct module_pair pair; struct hash_table_iter hti; struct symt_ht* sym; - WCHAR* srcmask; + WCHAR* compiland_regex; + WCHAR* srcfile_regex; struct line_info* dli; void* ptr; SRCCODEINFO sci; const char* file; + struct module_format_vtable_iterator iter = {}; if (!cb) return FALSE; if (!(dbghelp_options & SYMOPT_LOAD_LINES)) return TRUE; if (!module_init_pair(&pair, hProcess, base)) return FALSE; + if (!(compiland_regex = file_regex(compiland))) return FALSE; + if (!(srcfile_regex = file_regex(srcfile))) return FALSE; + + while ((module_format_vtable_iterator_next(pair.effective, &iter, + MODULE_FORMAT_VTABLE_INDEX(enumerate_lines)))) + { + enum method_result result = iter.modfmt->vtable->enumerate_lines(iter.modfmt, compiland_regex, srcfile_regex, cb, user); + HeapFree(GetProcessHeap(), 0, compiland_regex); + HeapFree(GetProcessHeap(), 0, srcfile_regex); + return result == MR_SUCCESS; + } + if (compiland) FIXME("Unsupported yet (filtering on compiland %s)\n", debugstr_a(compiland)); - if (!(srcmask = file_regex(srcfile))) return FALSE; + HeapFree(GetProcessHeap(), 0, compiland_regex); sci.SizeOfStruct = sizeof(sci); sci.ModBase = base; @@ -2581,7 +2595,7 @@ BOOL WINAPI SymEnumLines(HANDLE hProcess, ULONG64 base, PCSTR compiland, if (dli->is_source_file) { file = source_get(pair.effective, dli->u.source_file); - if (file && symt_match_stringAW(file, srcmask, FALSE)) + if (file && symt_match_stringAW(file, srcfile_regex, FALSE)) strcpy(sci.FileName, file); else sci.FileName[0] = '\0'; @@ -2596,7 +2610,7 @@ BOOL WINAPI SymEnumLines(HANDLE hProcess, ULONG64 base, PCSTR compiland, } } } - HeapFree(GetProcessHeap(), 0, srcmask); + HeapFree(GetProcessHeap(), 0, srcfile_regex); return TRUE; } From 0494f1ff0a6e0eb98db78e5df1b72559bef15180 Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Wed, 27 Nov 2024 15:50:40 +0100 Subject: [PATCH 242/454] dbghelp: Add method to enumerate source files. Signed-off-by: Eric Pouech (cherry picked from commit 6e3ad6e73601f36594831d1e81330346fb43de93) --- dlls/dbghelp/dbghelp_private.h | 4 +++ dlls/dbghelp/pdb.c | 61 ++++++++++++++++++++++++++++++++++ dlls/dbghelp/source.c | 20 ++++++++++- 3 files changed, 84 insertions(+), 1 deletion(-) diff --git a/dlls/dbghelp/dbghelp_private.h b/dlls/dbghelp/dbghelp_private.h index 35b16db01a46..6053d3c7f24b 100644 --- a/dlls/dbghelp/dbghelp_private.h +++ b/dlls/dbghelp/dbghelp_private.h @@ -444,6 +444,10 @@ struct module_format_vtable struct lineinfo_t *line_info, BOOL forward); enum method_result (*enumerate_lines)(struct module_format *modfmt, const WCHAR* compiland_regex, const WCHAR *source_file_regex, PSYM_ENUMLINES_CALLBACK cb, void *user); + + /* source files information */ + enum method_result (*enumerate_sources)(struct module_format *modfmt, const WCHAR *sourcefile_regex, + PSYM_ENUMSOURCEFILES_CALLBACKW cb, void *user); }; struct module_format diff --git a/dlls/dbghelp/pdb.c b/dlls/dbghelp/pdb.c index accc6166bddd..84f94e32338a 100644 --- a/dlls/dbghelp/pdb.c +++ b/dlls/dbghelp/pdb.c @@ -84,6 +84,8 @@ struct pdb_reader } *streams; char *stream_names; + unsigned source_listed : 1; + /* cache PE module sections for mapping... * we should rather use pe_module information */ @@ -925,6 +927,64 @@ static enum method_result pdb_method_enumerate_lines(struct module_format *modfm return pdb_method_result(pdb_method_enumerate_lines_internal(pdb, compiland_regex, source_file_regex, cb, user)); } +static enum pdb_result pdb_reader_load_sources_linetab2(struct pdb_reader *pdb, const PDB_SYMBOL_FILE_EX *dbi_cu_header) +{ + struct pdb_reader_walker linetab2_walker = {dbi_cu_header->stream, dbi_cu_header->symbol_size, dbi_cu_header->symbol_size + dbi_cu_header->lineno2_size}; + struct pdb_reader_walker sub_walker, checksum_walker; + enum pdb_result result; + struct CV_Checksum_t chksum; + + for (checksum_walker = linetab2_walker; !(result = pdb_reader_subsection_next(pdb, &checksum_walker, DEBUG_S_FILECHKSMS, &sub_walker)); ) + { + for (; (result = pdb_reader_READ(pdb, &sub_walker, &chksum)) == R_PDB_SUCCESS; sub_walker.offset = (sub_walker.offset + chksum.size + 3) & ~3) + { + char *string; + if ((result = pdb_reader_alloc_and_fetch_global_string(pdb, chksum.strOffset, &string))) return result; + source_new(pdb->module, NULL, string); + pdb_reader_free(pdb, string); + } + } + return result == R_PDB_NOT_FOUND ? R_PDB_SUCCESS : result; +} + +static enum pdb_result pdb_load_sources_internal(struct pdb_reader *pdb) +{ + enum pdb_result result; + struct pdb_reader_compiland_iterator compiland_iter; + + if ((result = pdb_reader_compiland_iterator_init(pdb, &compiland_iter))) return result; + do + { + if (compiland_iter.dbi_cu_header.lineno2_size) + { + result = pdb_reader_load_sources_linetab2(pdb, &compiland_iter.dbi_cu_header); + } + } while (pdb_reader_compiland_iterator_next(pdb, &compiland_iter) == R_PDB_SUCCESS); + + return R_PDB_SUCCESS; +} + +static enum method_result pdb_method_enumerate_sources(struct module_format *modfmt, const WCHAR *source_file_regex, + PSYM_ENUMSOURCEFILES_CALLBACKW cb, void *user) +{ + struct pdb_reader *pdb; + + if (!pdb_hack_get_main_info(modfmt, &pdb, NULL)) return -1; + + /* Note: in PDB, each compiland lists its used files, which are all in global string table, + * but there's no global source files table AFAICT. + * So, just walk (once) all compilands to grab all sources, and store them in generic source table. + * But don't enumerate here, let generic function take care of it. + */ + if (!pdb->source_listed) + { + enum pdb_result result = pdb_load_sources_internal(pdb); + if (result) return pdb_method_result(result); + pdb->source_listed = 1; + } + return MR_NOT_FOUND; +} + static struct module_format_vtable pdb_module_format_vtable = { NULL,/*pdb_module_remove*/ @@ -932,6 +992,7 @@ static struct module_format_vtable pdb_module_format_vtable = pdb_method_get_line_from_address, pdb_method_advance_line_info, pdb_method_enumerate_lines, + pdb_method_enumerate_sources, }; struct pdb_reader *pdb_hack_reader_init(struct module *module, HANDLE file, const IMAGE_SECTION_HEADER *sections, unsigned num_sections) diff --git a/dlls/dbghelp/source.c b/dlls/dbghelp/source.c index 2ef80deb1dc5..24ba224a4993 100644 --- a/dlls/dbghelp/source.c +++ b/dlls/dbghelp/source.c @@ -145,11 +145,12 @@ BOOL WINAPI SymEnumSourceFilesW(HANDLE hProcess, ULONG64 ModBase, PCWSTR Mask, char* ptr; WCHAR* conversion_buffer = NULL; DWORD conversion_buffer_len = 0; + struct module_format_vtable_iterator iter = {}; if (!cbSrcFiles) return FALSE; pair.pcs = process_find_by_handle(hProcess); if (!pair.pcs) return FALSE; - + if (ModBase) { pair.requested = module_find_by_addr(pair.pcs, ModBase); @@ -176,6 +177,23 @@ BOOL WINAPI SymEnumSourceFilesW(HANDLE hProcess, ULONG64 ModBase, PCWSTR Mask, return FALSE; } } + + while ((module_format_vtable_iterator_next(pair.effective, &iter, + MODULE_FORMAT_VTABLE_INDEX(enumerate_sources)))) + { + enum method_result result = iter.modfmt->vtable->enumerate_sources(iter.modfmt, NULL, cbSrcFiles, UserContext); + switch (result) + { + case MR_SUCCESS: + return TRUE; + case MR_NOT_FOUND: /* continue */ + break; + default: + /* SetLastError(...); */ + return FALSE; + } + } + if (!pair.effective->sources) return FALSE; for (ptr = pair.effective->sources; *ptr; ptr += strlen(ptr) + 1) { From 751b34a251007f5095273095e01852888ee8d3da Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Thu, 28 Nov 2024 12:22:27 +0100 Subject: [PATCH 243/454] dbghelp: No longer store line information from old PDB reader. Signed-off-by: Eric Pouech (cherry picked from commit 761a06fc76e473f9ecd34d704e3e9c0d47732fd5) --- dlls/dbghelp/msc.c | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/dlls/dbghelp/msc.c b/dlls/dbghelp/msc.c index 5fd0b28894ec..f4a8426ffe80 100644 --- a/dlls/dbghelp/msc.c +++ b/dlls/dbghelp/msc.c @@ -3906,25 +3906,25 @@ static BOOL pdb_process_internal(const struct process *pcs, files_image}; codeview_snarf(msc_dbg, modimage, sizeof(DWORD), sfile.symbol_size, &cvmod, file_name); - if (SymGetOptions() & SYMOPT_LOAD_LINES) + if (sfile.lineno_size && sfile.lineno2_size) + FIXME("Both line info present... preferring second\n"); + if (sfile.lineno2_size) { - if (sfile.lineno_size && sfile.lineno2_size) - FIXME("Both line info present... only supporting second\n"); - else if (sfile.lineno_size) - { - if (codeview_snarf_linetab(msc_dbg, - modimage + sfile.symbol_size, - sfile.lineno_size, - pdb_file->kind == PDB_JG)) - *has_linenumber_info = TRUE; - } - else if (sfile.lineno2_size) - { - if (codeview_snarf_linetab2(msc_dbg, &cvmod)) - *has_linenumber_info = TRUE; - } + if (pdb_file->pdb_reader || + ((SymGetOptions() & SYMOPT_LOAD_LINES) && codeview_snarf_linetab2(msc_dbg, &cvmod))) + *has_linenumber_info = TRUE; + } + else if (sfile.lineno_size) + { + if (pdb_file->pdb_reader) + FIXME("New PDB reader doesn't support old line format\n"); + else if ((SymGetOptions() & SYMOPT_LOAD_LINES) && + codeview_snarf_linetab(msc_dbg, + modimage + sfile.symbol_size, + sfile.lineno_size, + pdb_file->kind == PDB_JG)) + *has_linenumber_info = TRUE; } - pdb_free(modimage); } file_name += strlen(file_name) + 1; From 1a31ace4b9982f01498da9d2baea5e8b190db3d2 Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Sun, 6 Apr 2025 15:33:18 +0200 Subject: [PATCH 244/454] dbghelp: Fix line number when multiple entries have same offset. Signed-off-by: Eric Pouech (cherry picked from commit dd840e0909d9acfe119971bb654c845a40d04589) --- dlls/dbghelp/pdb.c | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/dlls/dbghelp/pdb.c b/dlls/dbghelp/pdb.c index 84f94e32338a..708bc1fece33 100644 --- a/dlls/dbghelp/pdb.c +++ b/dlls/dbghelp/pdb.c @@ -583,22 +583,16 @@ struct pdb_reader_linetab2_location static enum pdb_result pdb_find_matching_linetab2(struct CV_Line_t *lines, unsigned num_lines, DWORD64 delta, unsigned *index) { unsigned i; - for (i = 0; i + 1 < num_lines; i++) - { - unsigned j; - for (j = i + 1; j < num_lines; j++) - if (lines[j].offset != lines[i].offset) break; - if (j >= num_lines) break; - if (delta < lines[j].offset) - { - *index = i; - return R_PDB_SUCCESS; - } - } /* since the the address is inside the file_hdr, we assume then it's matched by last entry * (for which we don't have the next entry) */ - *index = num_lines - 1; + for (i = 0; i + 1 < num_lines; i++) + { + if (lines[i].offset == delta || + (lines[i].offset <= delta && delta < lines[i + 1].offset)) + break; + } + *index = i; return R_PDB_SUCCESS; } From 9f9921a8eece189e7e1a6210f65301504ccd489e Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Sun, 6 Apr 2025 15:41:20 +0200 Subject: [PATCH 245/454] dbghelp: Always reset all the fields for local scope enumeration. Signed-off-by: Eric Pouech (cherry picked from commit 4a0ea8bfd2a728a8f8d1750abb2e68784969d555) --- dlls/dbghelp/symbol.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/dlls/dbghelp/symbol.c b/dlls/dbghelp/symbol.c index 5d376806c1a6..8399b113ab37 100644 --- a/dlls/dbghelp/symbol.c +++ b/dlls/dbghelp/symbol.c @@ -715,6 +715,8 @@ static void symt_fill_sym_info(struct module_pair* pair, sym_info->ModBase = pair->requested->module.BaseOfImage; sym_info->Flags = 0; sym_info->Value = 0; + sym_info->Address = 0; + sym_info->Register = 0; switch (sym->tag) { @@ -750,7 +752,6 @@ static void symt_fill_sym_info(struct module_pair* pair, case loc_register: sym_info->Flags |= SYMFLAG_REGISTER; sym_info->Register = loc.reg; - sym_info->Address = 0; break; case loc_regrel: sym_info->Flags |= SYMFLAG_REGREL; @@ -779,7 +780,6 @@ static void symt_fill_sym_info(struct module_pair* pair, /* fall through */ case loc_absolute: symt_get_address(sym, &sym_info->Address); - sym_info->Register = 0; break; default: FIXME("Shouldn't happen (kind=%d), debug reader backend is broken\n", data->u.var.kind); @@ -839,7 +839,6 @@ static void symt_fill_sym_info(struct module_pair* pair, break; default: symt_get_address(sym, &sym_info->Address); - sym_info->Register = 0; break; } sym_info->Scope = 0; /* FIXME */ From 284eeb7422e0a5e3df01c6d1b9d940d235432203 Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Tue, 8 Apr 2025 09:44:03 +0200 Subject: [PATCH 246/454] dbghelp: Don't report local symbols when they are not present. (making use of already set values by Dwarf backend). Signed-off-by: Eric Pouech (cherry picked from commit 48edd4ed7d0a368714de0f38753e6ccf952dc0d3) --- dlls/dbghelp/symbol.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/dlls/dbghelp/symbol.c b/dlls/dbghelp/symbol.c index 8399b113ab37..65eb4fd3106b 100644 --- a/dlls/dbghelp/symbol.c +++ b/dlls/dbghelp/symbol.c @@ -694,7 +694,7 @@ struct symt_custom* symt_new_custom(struct module* module, const char* name, } /* expect sym_info->MaxNameLen to be set before being called */ -static void symt_fill_sym_info(struct module_pair* pair, +static BOOL symt_fill_sym_info(struct module_pair* pair, const struct symt_function* func, const struct symt* sym, SYMBOL_INFO* sym_info) { @@ -741,6 +741,7 @@ static void symt_fill_sym_info(struct module_pair* pair, MODULE_FORMAT_VTABLE_INDEX(loc_compute)))) { iter.modfmt->vtable->loc_compute(iter.modfmt, func, &loc); + if (loc.kind == loc_error && loc.reg == loc_err_out_of_scope) return FALSE; break; } } @@ -856,6 +857,7 @@ static void symt_fill_sym_info(struct module_pair* pair, TRACE_(dbghelp_symt)("%p => %s %lu %I64x\n", sym, debugstr_a(sym_info->Name), sym_info->Size, sym_info->Address); + return TRUE; } struct sym_enum @@ -872,7 +874,7 @@ struct sym_enum static BOOL send_symbol(const struct sym_enum* se, struct module_pair* pair, const struct symt_function* func, const struct symt* sym) { - symt_fill_sym_info(pair, func, sym, se->sym_info); + if (!symt_fill_sym_info(pair, func, sym, se->sym_info)) return FALSE; if (se->index && se->sym_info->Index != se->index) return FALSE; if (se->tag && se->sym_info->Tag != se->tag) return FALSE; if (se->addr && !(se->addr >= se->sym_info->Address && se->addr < se->sym_info->Address + se->sym_info->Size)) return FALSE; From d0aaed8080ed61656e9b16af1a536da58aba373f Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Mon, 2 Dec 2024 15:59:44 +0100 Subject: [PATCH 247/454] dbghelp: Use new pdb reader for DEFRANGE based local variables. Fixing a couple of bugs in the process. Signed-off-by: Eric Pouech (cherry picked from commit bc00c37b3924e74c7840da426d66251790ccb7f9) --- dlls/dbghelp/msc.c | 16 ++++- dlls/dbghelp/pdb.c | 173 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 184 insertions(+), 5 deletions(-) diff --git a/dlls/dbghelp/msc.c b/dlls/dbghelp/msc.c index f4a8426ffe80..6c8f9b3637dc 100644 --- a/dlls/dbghelp/msc.c +++ b/dlls/dbghelp/msc.c @@ -2298,6 +2298,7 @@ static struct symt_compiland* codeview_new_compiland(const struct msc_debug_info } static BOOL codeview_snarf(const struct msc_debug_info* msc_dbg, + unsigned stream_id, const BYTE* root, unsigned offset, unsigned size, const struct cv_module_snarf* cvmod, const char* objname) @@ -2699,7 +2700,15 @@ static BOOL codeview_snarf(const struct msc_debug_info* msc_dbg, */ if (!sym->local_v3.varflags.enreg_global && !sym->local_v3.varflags.enreg_static) { - length += codeview_transform_defrange(msc_dbg, curr_func, sym, &loc); + if (stream_id) + { + loc.kind = loc_user + 1 /* loc_cv_defrange for new PDB reader (see pdb.c) */; + loc.reg = stream_id; + loc.offset = (const BYTE*)sym - root; + length += codeview_defrange_length(sym); + } + else + length += codeview_transform_defrange(msc_dbg, curr_func, sym, &loc); symt_add_func_local(msc_dbg->module, curr_func, sym->local_v3.varflags.is_param ? DataIsParam : DataIsLocal, &loc, block, @@ -3904,7 +3913,8 @@ static BOOL pdb_process_internal(const struct process *pcs, { struct cv_module_snarf cvmod = {ipi_ok ? &ipi_ctp : NULL, (const void*)(modimage + sfile.symbol_size), sfile.lineno2_size, files_image}; - codeview_snarf(msc_dbg, modimage, sizeof(DWORD), sfile.symbol_size, &cvmod, file_name); + codeview_snarf(msc_dbg, pdb_file->pdb_reader ? sfile.stream : 0, modimage, sizeof(DWORD), sfile.symbol_size, + &cvmod, file_name); if (sfile.lineno_size && sfile.lineno2_size) FIXME("Both line info present... preferring second\n"); @@ -4103,7 +4113,7 @@ static BOOL codeview_process_info(const struct process *pcs, if (ent->SubSection == sstAlignSym) { - codeview_snarf(msc_dbg, msc_dbg->root + ent->lfo, sizeof(DWORD), ent->cb, NULL, NULL); + codeview_snarf(msc_dbg, 0, msc_dbg->root + ent->lfo, sizeof(DWORD), ent->cb, NULL, NULL); if (SymGetOptions() & SYMOPT_LOAD_LINES) { diff --git a/dlls/dbghelp/pdb.c b/dlls/dbghelp/pdb.c index 708bc1fece33..6276ee41ee3b 100644 --- a/dlls/dbghelp/pdb.c +++ b/dlls/dbghelp/pdb.c @@ -979,10 +979,180 @@ static enum method_result pdb_method_enumerate_sources(struct module_format *mod return MR_NOT_FOUND; } +#define loc_cv_local_range (loc_user + 0) /* loc.offset contain the copy of all defrange* Codeview records following S_LOCAL */ +#define loc_cv_defrange (loc_user + 1) /* loc.register+offset contain the stream_id+stream_offset of S_LOCAL Codeview record to search into */ + +/* Some data (codeview_symbol, codeview_types...) are layed out with a 2 byte integer, + * designing length of following blob. + * Basic reading of that length + (part) of blob. + * Walker is advanced by 2 only (so that any reading inside blob is possible). + */ +static enum pdb_result pdb_reader_read_partial_blob(struct pdb_reader *pdb, struct pdb_reader_walker *walker, void *blob, unsigned blob_size) +{ + enum pdb_result result; + pdbsize_t num_read, toload; + unsigned short len; + + if ((result = pdb_reader_internal_read_advance(pdb, walker, &len, sizeof(len)))) return result; + toload = min(len, blob_size - sizeof(len)); + + if ((result = pdb_reader_read_from_stream(pdb, walker, (char*)blob + sizeof(len), toload, &num_read))) return result; + if (num_read != toload) return R_PDB_IOERROR; + *(unsigned short*)blob = len; + return R_PDB_SUCCESS; +} + +static enum pdb_result pdb_reader_alloc_and_read_full_blob(struct pdb_reader *pdb, struct pdb_reader_walker *walker, void **blob) +{ + enum pdb_result result; + unsigned short int len; + + if ((result = pdb_reader_READ(pdb, walker, &len))) return result; + if ((result = pdb_reader_alloc(pdb, len + sizeof(len), blob))) + { + walker->offset -= sizeof(len); + return result; + } + + if ((result = pdb_reader_internal_read_advance(pdb, walker, (char*)*blob + sizeof(len), len))) + { + pdb_reader_free(pdb, *blob); + walker->offset -= sizeof(len); + return result; + } + *(unsigned short int*)*blob = len; + return R_PDB_SUCCESS; +} + +/* Read the fixed part of a CodeView symbol (enough to fit inside the union codeview) */ +static enum pdb_result pdb_reader_read_partial_codeview_symbol(struct pdb_reader *pdb, struct pdb_reader_walker *walker, union codeview_symbol *cv_symbol) +{ + return pdb_reader_read_partial_blob(pdb, walker, (void*)cv_symbol, sizeof(*cv_symbol)); +} + +static enum pdb_result pdb_reader_alloc_and_read_full_codeview_symbol(struct pdb_reader *pdb, struct pdb_reader_walker *walker, + union codeview_symbol **cv_symbol) +{ + return pdb_reader_alloc_and_read_full_blob(pdb, walker, (void **)cv_symbol); +} + +static void pdb_method_location_compute(const struct module_format *modfmt, + const struct symt_function *func, + struct location *loc) +{ + enum pdb_result result; + struct pdb_reader_walker walker; + struct pdb_reader *pdb; + union codeview_symbol cv_local; + union codeview_symbol *cv_def; + DWORD64 ip = modfmt->module->process->localscope_pc; + struct location in_loc = *loc; + + loc->kind = loc_error; + loc->reg = loc_err_internal; + + if (!pdb_hack_get_main_info((struct module_format *)modfmt, &pdb, NULL)) return; + if (in_loc.kind != loc_cv_defrange || pdb_reader_walker_init(pdb, in_loc.reg, &walker)) return; + walker.offset = in_loc.offset; + + /* we have in location: in_loc.reg = stream_id, in_loc.offset offset in stream_id to point to S_LOCAL */ + if ((result = pdb_reader_read_partial_codeview_symbol(pdb, &walker, &cv_local))) return; + walker.offset += cv_local.generic.len; + + while ((result = pdb_reader_alloc_and_read_full_codeview_symbol(pdb, &walker, &cv_def)) == R_PDB_SUCCESS && + cv_def->generic.id >= S_DEFRANGE && cv_def->generic.id <= S_DEFRANGE_REGISTER_REL) + { + BOOL inside = TRUE; + + /* S_DEFRANGE_FRAMEPOINTER_REL_FULL_SCOPE matches full function scope... + * Assuming that if we're here, ip matches the function for which we're + * considering the S_LOCAL and S_DEFRANGE_*, there's nothing to do. + */ + if (cv_def->generic.id != S_DEFRANGE_FRAMEPOINTER_REL_FULL_SCOPE) + { + const struct cv_addr_range* range; + const struct cv_addr_gap* gap; + DWORD64 range_start; + + switch (cv_def->generic.id) + { + case S_DEFRANGE: range = &cv_def->defrange_v3.range; break; + case S_DEFRANGE_SUBFIELD: range = &cv_def->defrange_subfield_v3.range; break; + case S_DEFRANGE_REGISTER: range = &cv_def->defrange_register_v3.range; break; + case S_DEFRANGE_FRAMEPOINTER_REL: range = &cv_def->defrange_frameptrrel_v3.range; break; + case S_DEFRANGE_SUBFIELD_REGISTER: range = &cv_def->defrange_subfield_register_v3.range; break; + case S_DEFRANGE_REGISTER_REL: range = &cv_def->defrange_registerrel_v3.range; break; + default: range = NULL; + } + + /* check if inside range */ + if ((result = pdb_reader_get_segment_address(pdb, range->isectStart, range->offStart, &range_start))) + { + pdb_reader_free(pdb, cv_def); + return; + } + inside = range_start <= ip && ip < range_start + range->cbRange; + + /* the gaps describe part which shall be excluded from range */ + for (gap = (const void*)(range + 1); + inside && (const char*)(gap + 1) <= (const char*)cv_def + sizeof(cv_def->generic.len) + cv_def->generic.len; + gap++) + { + if (func->ranges[0].low + gap->gapStartOffset <= ip && + ip < func->ranges[0].low + gap->gapStartOffset + gap->cbRange) + inside = FALSE; + } + } + if (!inside) + { + pdb_reader_free(pdb, cv_def); + continue; + } + + switch (cv_def->generic.id) + { + case S_DEFRANGE: + case S_DEFRANGE_SUBFIELD: + default: + WARN("Unsupported defrange %d\n", cv_def->generic.id); + loc->kind = loc_error; + loc->reg = loc_err_internal; + break; + case S_DEFRANGE_SUBFIELD_REGISTER: + WARN("sub-field part not handled\n"); + /* fall through */ + case S_DEFRANGE_REGISTER: + loc->kind = loc_register; + loc->reg = cv_def->defrange_register_v3.reg; + break; + case S_DEFRANGE_REGISTER_REL: + loc->kind = loc_regrel; + loc->reg = cv_def->defrange_registerrel_v3.baseReg; + loc->offset = cv_def->defrange_registerrel_v3.offBasePointer; + break; + case S_DEFRANGE_FRAMEPOINTER_REL: + loc->kind = loc_regrel; + loc->reg = modfmt->module->cpu->frame_regno; + loc->offset = cv_def->defrange_frameptrrel_v3.offFramePointer; + break; + case S_DEFRANGE_FRAMEPOINTER_REL_FULL_SCOPE: + loc->kind = loc_regrel; + loc->reg = modfmt->module->cpu->frame_regno; + loc->offset = cv_def->defrange_frameptr_relfullscope_v3.offFramePointer; + break; + } + pdb_reader_free(pdb, cv_def); + return; + } + if (result == R_PDB_SUCCESS) pdb_reader_free(pdb, cv_def); + loc->kind = loc_error; + loc->reg = loc_err_out_of_scope; +} + static struct module_format_vtable pdb_module_format_vtable = { NULL,/*pdb_module_remove*/ - NULL,/*pdb_location_compute*/ + pdb_method_location_compute, pdb_method_get_line_from_address, pdb_method_advance_line_info, pdb_method_enumerate_lines, @@ -1000,7 +1170,6 @@ struct pdb_reader *pdb_hack_reader_init(struct module *module, HANDLE file, cons pdb->module = module; /* hack (copy old pdb methods until they are moved here) */ pdb_module_format_vtable.remove = module->format_info[DFI_PDB]->vtable->remove; - pdb_module_format_vtable.loc_compute = module->format_info[DFI_PDB]->vtable->loc_compute; module->format_info[DFI_PDB]->vtable = &pdb_module_format_vtable; return pdb; From fe9385d80afbb69650737767cc4c90756bd827f1 Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Tue, 10 Dec 2024 10:08:10 +0100 Subject: [PATCH 248/454] dbghelp: Introduce helper to query info from index. Signed-off-by: Eric Pouech (cherry picked from commit 6e2c4ad0efb3b1620d81a6ae14481cb4c3983762) --- dlls/dbghelp/dbghelp_private.h | 2 ++ dlls/dbghelp/symbol.c | 5 ++--- dlls/dbghelp/type.c | 8 +++++++- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/dlls/dbghelp/dbghelp_private.h b/dlls/dbghelp/dbghelp_private.h index 6053d3c7f24b..24fea1ce0d5f 100644 --- a/dlls/dbghelp/dbghelp_private.h +++ b/dlls/dbghelp/dbghelp_private.h @@ -972,6 +972,8 @@ extern BOOL lineinfo_set_nameA(struct process* pcs, struct lineinfo_t* i extern void symt_init_basic(struct module* module); extern BOOL symt_get_info(struct module* module, const struct symt* type, IMAGEHLP_SYMBOL_TYPE_INFO req, void* pInfo); +extern BOOL symt_get_info_from_index(struct module* module, DWORD index, + IMAGEHLP_SYMBOL_TYPE_INFO req, void* pInfo); extern struct symt_basic* symt_get_basic(enum BasicType, unsigned size); extern struct symt_udt* diff --git a/dlls/dbghelp/symbol.c b/dlls/dbghelp/symbol.c index 65eb4fd3106b..784f88beb08c 100644 --- a/dlls/dbghelp/symbol.c +++ b/dlls/dbghelp/symbol.c @@ -708,8 +708,7 @@ static BOOL symt_fill_sym_info(struct module_pair* pair, sym_info->Reserved[0] = sym_info->Reserved[1] = 0; if (!symt_get_info(pair->effective, sym, TI_GET_LENGTH, &size) && (!sym_info->TypeIndex || - !symt_get_info(pair->effective, symt_index2ptr(pair->effective, sym_info->TypeIndex), - TI_GET_LENGTH, &size))) + !symt_get_info_from_index(pair->effective, sym_info->TypeIndex, TI_GET_LENGTH, &size))) size = 0; sym_info->Size = (DWORD)size; sym_info->ModBase = pair->requested->module.BaseOfImage; @@ -991,7 +990,7 @@ static void symt_get_length(struct module* module, const struct symt* symt, ULON return; if (symt_get_info(module, symt, TI_GET_TYPE, &type_index) && - symt_get_info(module, symt_index2ptr(module, type_index), TI_GET_LENGTH, size)) return; + symt_get_info_from_index(module, type_index, TI_GET_LENGTH, size)) return; *size = 1; /* no size info */ } diff --git a/dlls/dbghelp/type.c b/dlls/dbghelp/type.c index 8be905116c7c..97abb60f64d9 100644 --- a/dlls/dbghelp/type.c +++ b/dlls/dbghelp/type.c @@ -1111,6 +1111,12 @@ BOOL symt_get_info(struct module* module, const struct symt* type, return TRUE; } +BOOL symt_get_info_from_index(struct module* module, DWORD index, + IMAGEHLP_SYMBOL_TYPE_INFO req, void* pInfo) +{ + return symt_get_info(module, symt_index2ptr(module, index), req, pInfo); +} + /****************************************************************** * SymGetTypeInfo (DBGHELP.@) * @@ -1122,7 +1128,7 @@ BOOL WINAPI SymGetTypeInfo(HANDLE hProcess, DWORD64 ModBase, struct module_pair pair; if (!module_init_pair(&pair, hProcess, ModBase)) return FALSE; - return symt_get_info(pair.effective, symt_index2ptr(pair.effective, TypeId), GetType, pInfo); + return symt_get_info_from_index(pair.effective, TypeId, GetType, pInfo); } /****************************************************************** From 6ed765cd6a975cd90073ddc573414d3e9480c8df Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Thu, 10 Apr 2025 09:22:34 +0200 Subject: [PATCH 249/454] dbghelp: Add SYMFLAG_NULL for out of scope local variables. Signed-off-by: Eric Pouech (cherry picked from commit 548103f355963aa1ae7c4751a88e02f1c71af7b7) --- dlls/dbghelp/symbol.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/dlls/dbghelp/symbol.c b/dlls/dbghelp/symbol.c index 784f88beb08c..6913b82b836d 100644 --- a/dlls/dbghelp/symbol.c +++ b/dlls/dbghelp/symbol.c @@ -740,13 +740,17 @@ static BOOL symt_fill_sym_info(struct module_pair* pair, MODULE_FORMAT_VTABLE_INDEX(loc_compute)))) { iter.modfmt->vtable->loc_compute(iter.modfmt, func, &loc); - if (loc.kind == loc_error && loc.reg == loc_err_out_of_scope) return FALSE; break; } } switch (loc.kind) { case loc_error: + if (loc.reg == loc_err_out_of_scope) + { + sym_info->Flags |= SYMFLAG_NULL; + break; + } /* for now we report error cases as a negative register number */ /* fall through */ case loc_register: From 506587e5824070534018491f45ddb716ae56511e Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Fri, 4 Apr 2025 10:28:39 +0200 Subject: [PATCH 250/454] dbghelp: Rename ptr <> index conversion helpers. We're going to introduce a couple more helpers in next patches, so make it more readable. Signed-off-by: Eric Pouech (cherry picked from commit 137ea34e4ab7986755cf1615c3a5de9543790132) --- dlls/dbghelp/dbghelp.c | 2 +- dlls/dbghelp/dbghelp_private.h | 4 ++-- dlls/dbghelp/symbol.c | 8 +++---- dlls/dbghelp/type.c | 42 +++++++++++++++++----------------- 4 files changed, 28 insertions(+), 28 deletions(-) diff --git a/dlls/dbghelp/dbghelp.c b/dlls/dbghelp/dbghelp.c index 5a023152ec41..73c7f1eb9695 100644 --- a/dlls/dbghelp/dbghelp.c +++ b/dlls/dbghelp/dbghelp.c @@ -722,7 +722,7 @@ BOOL WINAPI SymSetScopeFromIndex(HANDLE hProcess, ULONG64 addr, DWORD index) TRACE("(%p %#I64x %lu)\n", hProcess, addr, index); if (!module_init_pair(&pair, hProcess, addr)) return FALSE; - sym = symt_index2ptr(pair.effective, index); + sym = symt_index_to_ptr(pair.effective, index); if (!symt_check_tag(sym, SymTagFunction)) return FALSE; pair.pcs->localscope_pc = ((struct symt_function*)sym)->ranges[0].low; /* FIXME of FuncDebugStart when it exists? */ diff --git a/dlls/dbghelp/dbghelp_private.h b/dlls/dbghelp/dbghelp_private.h index 24fea1ce0d5f..a821ee9e4acd 100644 --- a/dlls/dbghelp/dbghelp_private.h +++ b/dlls/dbghelp/dbghelp_private.h @@ -961,8 +961,8 @@ extern struct symt_hierarchy_point* symt_new_label(struct module* module, struct symt_compiland* compiland, const char* name, ULONG_PTR address); -extern struct symt* symt_index2ptr(struct module* module, DWORD id); -extern DWORD symt_ptr2index(struct module* module, const struct symt* sym); +extern struct symt* symt_index_to_ptr(struct module* module, DWORD id); +extern DWORD symt_ptr_to_index(struct module* module, const struct symt* sym); extern struct symt_custom* symt_new_custom(struct module* module, const char* name, DWORD64 addr, DWORD size); diff --git a/dlls/dbghelp/symbol.c b/dlls/dbghelp/symbol.c index 6913b82b836d..01d3267808bb 100644 --- a/dlls/dbghelp/symbol.c +++ b/dlls/dbghelp/symbol.c @@ -70,7 +70,7 @@ int __cdecl symt_cmp_addr(const void* p1, const void* p2) * which is exposed to the caller and index is the index of the symbol in * this array */ -DWORD symt_ptr2index(struct module* module, const struct symt* sym) +DWORD symt_ptr_to_index(struct module* module, const struct symt* sym) { struct vector* vector; DWORD offset; @@ -105,7 +105,7 @@ DWORD symt_ptr2index(struct module* module, const struct symt* sym) return len + offset; } -struct symt* symt_index2ptr(struct module* module, DWORD id) +struct symt* symt_index_to_ptr(struct module* module, DWORD id) { struct vector* vector; if (id >= BASE_CUSTOM_SYMT) @@ -704,7 +704,7 @@ static BOOL symt_fill_sym_info(struct module_pair* pair, if (!symt_get_info(pair->effective, sym, TI_GET_TYPE, &sym_info->TypeIndex)) sym_info->TypeIndex = 0; - sym_info->Index = symt_ptr2index(pair->effective, sym); + sym_info->Index = symt_ptr_to_index(pair->effective, sym); sym_info->Reserved[0] = sym_info->Reserved[1] = 0; if (!symt_get_info(pair->effective, sym, TI_GET_LENGTH, &size) && (!sym_info->TypeIndex || @@ -2655,7 +2655,7 @@ BOOL WINAPI SymFromIndex(HANDLE hProcess, ULONG64 BaseOfDll, DWORD index, PSYMBO hProcess, BaseOfDll, index, symbol); if (!module_init_pair(&pair, hProcess, BaseOfDll)) return FALSE; - if ((sym = symt_index2ptr(pair.effective, index)) == NULL) return FALSE; + if ((sym = symt_index_to_ptr(pair.effective, index)) == NULL) return FALSE; symt_fill_sym_info(&pair, NULL, sym, symbol); return TRUE; } diff --git a/dlls/dbghelp/type.c b/dlls/dbghelp/type.c index 97abb60f64d9..0f3a94453a0c 100644 --- a/dlls/dbghelp/type.c +++ b/dlls/dbghelp/type.c @@ -464,7 +464,7 @@ static BOOL sym_enum_types(struct module_pair *pair, const char *type_name, PSYM if (type_name && !SymMatchStringA(type->hash_elt.name, type_name, TRUE)) continue; - sym_info->TypeIndex = symt_ptr2index(pair->effective, &type->symt); + sym_info->TypeIndex = symt_ptr_to_index(pair->effective, &type->symt); sym_info->Index = 0; /* FIXME */ symt_get_info(pair->effective, &type->symt, TI_GET_LENGTH, &size); sym_info->Size = size; @@ -666,7 +666,7 @@ BOOL symt_get_info(struct module* module, const struct symt* type, for (i = 0; i < tifp->Count; i++) { if (!(pt = vector_at(v, tifp->Start + i))) return FALSE; - tifp->ChildId[i] = symt_ptr2index(module, *pt); + tifp->ChildId[i] = symt_ptr_to_index(module, *pt); } } break; @@ -845,25 +845,25 @@ BOOL symt_get_info(struct module* module, const struct symt* type, switch (type->tag) { case SymTagCompiland: - X(DWORD) = symt_ptr2index(module, &((const struct symt_compiland*)type)->container->symt); + X(DWORD) = symt_ptr_to_index(module, &((const struct symt_compiland*)type)->container->symt); break; case SymTagBlock: - X(DWORD) = symt_ptr2index(module, ((const struct symt_block*)type)->container); + X(DWORD) = symt_ptr_to_index(module, ((const struct symt_block*)type)->container); break; case SymTagData: - X(DWORD) = symt_ptr2index(module, ((const struct symt_data*)type)->container); + X(DWORD) = symt_ptr_to_index(module, ((const struct symt_data*)type)->container); break; case SymTagFunction: case SymTagInlineSite: - X(DWORD) = symt_ptr2index(module, ((const struct symt_function*)type)->container); + X(DWORD) = symt_ptr_to_index(module, ((const struct symt_function*)type)->container); break; case SymTagThunk: - X(DWORD) = symt_ptr2index(module, ((const struct symt_thunk*)type)->container); + X(DWORD) = symt_ptr_to_index(module, ((const struct symt_thunk*)type)->container); break; case SymTagFuncDebugStart: case SymTagFuncDebugEnd: case SymTagLabel: - X(DWORD) = symt_ptr2index(module, ((const struct symt_hierarchy_point*)type)->parent); + X(DWORD) = symt_ptr_to_index(module, ((const struct symt_hierarchy_point*)type)->parent); break; case SymTagUDT: case SymTagEnum: @@ -876,7 +876,7 @@ BOOL symt_get_info(struct module* module, const struct symt* type, case SymTagBaseClass: case SymTagPublicSymbol: case SymTagCustom: - X(DWORD) = symt_ptr2index(module, &module->top->symt); + X(DWORD) = symt_ptr_to_index(module, &module->top->symt); break; default: FIXME("Unsupported sym-tag %s for get-lexical-parent\n", @@ -981,30 +981,30 @@ BOOL symt_get_info(struct module* module, const struct symt* type, { /* hierarchical => hierarchical */ case SymTagArrayType: - X(DWORD) = symt_ptr2index(module, ((const struct symt_array*)type)->base_type); + X(DWORD) = symt_ptr_to_index(module, ((const struct symt_array*)type)->base_type); break; case SymTagPointerType: - X(DWORD) = symt_ptr2index(module, ((const struct symt_pointer*)type)->pointsto); + X(DWORD) = symt_ptr_to_index(module, ((const struct symt_pointer*)type)->pointsto); break; case SymTagFunctionType: - X(DWORD) = symt_ptr2index(module, ((const struct symt_function_signature*)type)->rettype); + X(DWORD) = symt_ptr_to_index(module, ((const struct symt_function_signature*)type)->rettype); break; case SymTagTypedef: - X(DWORD) = symt_ptr2index(module, ((const struct symt_typedef*)type)->type); + X(DWORD) = symt_ptr_to_index(module, ((const struct symt_typedef*)type)->type); break; /* lexical => hierarchical */ case SymTagData: - X(DWORD) = symt_ptr2index(module, ((const struct symt_data*)type)->type); + X(DWORD) = symt_ptr_to_index(module, ((const struct symt_data*)type)->type); break; case SymTagFunction: case SymTagInlineSite: - X(DWORD) = symt_ptr2index(module, ((const struct symt_function*)type)->type); + X(DWORD) = symt_ptr_to_index(module, ((const struct symt_function*)type)->type); break; case SymTagEnum: - X(DWORD) = symt_ptr2index(module, ((const struct symt_enum*)type)->base_type); + X(DWORD) = symt_ptr_to_index(module, ((const struct symt_enum*)type)->base_type); break; case SymTagFunctionArgType: - X(DWORD) = symt_ptr2index(module, ((const struct symt_function_arg_type*)type)->arg_type); + X(DWORD) = symt_ptr_to_index(module, ((const struct symt_function_arg_type*)type)->arg_type); break; default: FIXME("Unsupported sym-tag %s for get-type\n", @@ -1069,7 +1069,7 @@ BOOL symt_get_info(struct module* module, const struct symt* type, break; case TI_GET_ARRAYINDEXTYPEID: if (type->tag != SymTagArrayType) return FALSE; - X(DWORD) = symt_ptr2index(module, ((const struct symt_array*)type)->index_type); + X(DWORD) = symt_ptr_to_index(module, ((const struct symt_array*)type)->index_type); break; case TI_GET_SYMINDEX: @@ -1077,7 +1077,7 @@ BOOL symt_get_info(struct module* module, const struct symt* type, * native sometimes (eg for UDT) return id of another instance * of the same UDT definition... maybe forward declaration? */ - X(DWORD) = symt_ptr2index(module, type); + X(DWORD) = symt_ptr_to_index(module, type); break; /* FIXME: we don't support properly C++ for now */ @@ -1114,7 +1114,7 @@ BOOL symt_get_info(struct module* module, const struct symt* type, BOOL symt_get_info_from_index(struct module* module, DWORD index, IMAGEHLP_SYMBOL_TYPE_INFO req, void* pInfo) { - return symt_get_info(module, symt_index2ptr(module, index), req, pInfo); + return symt_get_info(module, symt_index_to_ptr(module, index), req, pInfo); } /****************************************************************** @@ -1145,7 +1145,7 @@ BOOL WINAPI SymGetTypeFromName(HANDLE hProcess, ULONG64 BaseOfDll, if (!module_init_pair(&pair, hProcess, BaseOfDll)) return FALSE; type = symt_find_type_by_name(pair.effective, SymTagNull, Name); if (!type) return FALSE; - Symbol->Index = Symbol->TypeIndex = symt_ptr2index(pair.effective, type); + Symbol->Index = Symbol->TypeIndex = symt_ptr_to_index(pair.effective, type); symbol_setname(Symbol, symt_get_name(type)); symt_get_info(pair.effective, type, TI_GET_LENGTH, &size); Symbol->Size = size; From b0bb46047dcbaa365f7667c3a5f92601885306e0 Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Tue, 10 Dec 2024 16:07:01 +0100 Subject: [PATCH 251/454] dbghelp: Introduce an opaque type to store type of data & function. Signed-off-by: Eric Pouech (cherry picked from commit c42be7921b46c5557f986fd7ca93d2abc31dcd5e) --- dlls/dbghelp/coff.c | 20 +++--- dlls/dbghelp/dbghelp_private.h | 35 ++++++---- dlls/dbghelp/dwarf.c | 19 ++--- dlls/dbghelp/elf_module.c | 4 +- dlls/dbghelp/macho_module.c | 4 +- dlls/dbghelp/msc.c | 122 ++++++++++++++++++--------------- dlls/dbghelp/stabs.c | 28 ++++---- dlls/dbghelp/symbol.c | 34 +++++---- dlls/dbghelp/type.c | 16 +++-- 9 files changed, 158 insertions(+), 124 deletions(-) diff --git a/dlls/dbghelp/coff.c b/dlls/dbghelp/coff.c index 29c75c375e1c..aa7273db24e3 100644 --- a/dlls/dbghelp/coff.c +++ b/dlls/dbghelp/coff.c @@ -280,12 +280,12 @@ BOOL coff_process_info(const struct msc_debug_info* msc_dbg) /* FIXME: was adding symbol to this_file ??? */ coff_add_symbol(&coff_files.files[curr_file_idx], - &symt_new_function(msc_dbg->module, - coff_files.files[curr_file_idx].compiland, + &symt_new_function(msc_dbg->module, + coff_files.files[curr_file_idx].compiland, nampnt, msc_dbg->module->module.BaseOfImage + base + coff_sym->Value, 0 /* FIXME */, - NULL /* FIXME */)->symt); + 0 /* FIXME */)->symt); continue; } @@ -316,15 +316,15 @@ BOOL coff_process_info(const struct msc_debug_info* msc_dbg) if (j < coff_files.nfiles) { coff_add_symbol(&coff_files.files[j], - &symt_new_function(msc_dbg->module, compiland, nampnt, + &symt_new_function(msc_dbg->module, compiland, nampnt, msc_dbg->module->module.BaseOfImage + base + coff_sym->Value, - 0 /* FIXME */, NULL /* FIXME */)->symt); - } - else + 0 /* FIXME */, 0 /* FIXME */)->symt); + } + else { - symt_new_function(msc_dbg->module, NULL, nampnt, + symt_new_function(msc_dbg->module, NULL, nampnt, msc_dbg->module->module.BaseOfImage + base + coff_sym->Value, - 0 /* FIXME */, NULL /* FIXME */); + 0 /* FIXME */, 0 /* FIXME */); } i += naux; continue; @@ -353,7 +353,7 @@ BOOL coff_process_info(const struct msc_debug_info* msc_dbg) loc.reg = 0; loc.offset = msc_dbg->module->module.BaseOfImage + base + coff_sym->Value; symt_new_global_variable(msc_dbg->module, NULL, nampnt, TRUE /* FIXME */, - loc, 0 /* FIXME */, NULL /* FIXME */); + loc, 0 /* FIXME */, 0 /* FIXME */); i += naux; continue; } diff --git a/dlls/dbghelp/dbghelp_private.h b/dlls/dbghelp/dbghelp_private.h index a821ee9e4acd..edf3117550aa 100644 --- a/dlls/dbghelp/dbghelp_private.h +++ b/dlls/dbghelp/dbghelp_private.h @@ -182,6 +182,8 @@ static inline BOOL symt_check_tag(const struct symt* s, enum SymTagEnum tag) return s && s->tag == tag; } +typedef ULONG_PTR symref_t; + /* lexical tree */ struct symt_block { @@ -215,7 +217,7 @@ struct symt_data struct hash_table_elt hash_elt; /* if global symbol */ enum DataKind kind; struct symt* container; - struct symt* type; + symref_t type; union /* depends on kind */ { /* DataIs{Global, FileStatic, StaticLocal}: @@ -292,7 +294,7 @@ struct symt_function struct symt symt; /* SymTagFunction or SymTagInlineSite */ struct hash_table_elt hash_elt; /* if global symbol, inline site */ struct symt* container; /* compiland (for SymTagFunction) or function (for SymTagInlineSite) */ - struct symt* type; /* points to function_signature */ + symref_t type; /* points to function_signature */ struct vector vlines; struct vector vchildren; /* locals, params, blocks, start/end, labels, inline sites */ struct symt_function* next_inlinesite;/* linked list of inline sites in this function */ @@ -900,38 +902,38 @@ extern struct symt_public* ULONG_PTR address, unsigned size); extern struct symt_data* - symt_new_global_variable(struct module* module, + symt_new_global_variable(struct module* module, struct symt_compiland* parent, const char* name, unsigned is_static, struct location loc, ULONG_PTR size, - struct symt* type); + symref_t type); extern struct symt_function* symt_new_function(struct module* module, struct symt_compiland* parent, const char* name, ULONG_PTR addr, ULONG_PTR size, - struct symt* type); + symref_t type); extern struct symt_function* symt_new_inlinesite(struct module* module, struct symt_function* func, struct symt* parent, const char* name, - struct symt* type, + symref_t type, unsigned num_ranges); extern void symt_add_func_line(struct module* module, struct symt_function* func, unsigned source_idx, int line_num, ULONG_PTR offset); extern struct symt_data* - symt_add_func_local(struct module* module, - struct symt_function* func, + symt_add_func_local(struct module* module, + struct symt_function* func, enum DataKind dt, const struct location* loc, struct symt_block* block, - struct symt* type, const char* name); + symref_t, const char* name); extern struct symt_data* symt_add_func_constant(struct module* module, struct symt_function* func, struct symt_block* block, - struct symt* type, const char* name, VARIANT* v); + symref_t, const char* name, VARIANT* v); extern struct symt_block* symt_open_func_block(struct module* module, struct symt_function* func, @@ -955,7 +957,7 @@ extern struct symt_thunk* extern struct symt_data* symt_new_constant(struct module* module, struct symt_compiland* parent, - const char* name, struct symt* type, + const char* name, symref_t type, const VARIANT* v); extern struct symt_hierarchy_point* symt_new_label(struct module* module, @@ -963,6 +965,9 @@ extern struct symt_hierarchy_point* const char* name, ULONG_PTR address); extern struct symt* symt_index_to_ptr(struct module* module, DWORD id); extern DWORD symt_ptr_to_index(struct module* module, const struct symt* sym); +extern DWORD symt_symref_to_index(struct module* module, symref_t ref); +static inline symref_t + symt_ptr_to_symref(const struct symt *symt) {return (ULONG_PTR)symt;} extern struct symt_custom* symt_new_custom(struct module* module, const char* name, DWORD64 addr, DWORD size); @@ -974,6 +979,8 @@ extern BOOL symt_get_info(struct module* module, const struct symt* type IMAGEHLP_SYMBOL_TYPE_INFO req, void* pInfo); extern BOOL symt_get_info_from_index(struct module* module, DWORD index, IMAGEHLP_SYMBOL_TYPE_INFO req, void* pInfo); +extern BOOL symt_get_info_from_symref(struct module* module, symref_t type, + IMAGEHLP_SYMBOL_TYPE_INFO req, void* pInfo); extern struct symt_basic* symt_get_basic(enum BasicType, unsigned size); extern struct symt_udt* @@ -981,10 +988,10 @@ extern struct symt_udt* unsigned size, enum UdtKind kind); extern BOOL symt_set_udt_size(struct module* module, struct symt_udt* type, unsigned size); -extern BOOL symt_add_udt_element(struct module* module, - struct symt_udt* udt_type, +extern BOOL symt_add_udt_element(struct module* module, + struct symt_udt* udt_type, const char* name, - struct symt* elt_type, unsigned offset, + symref_t elt_type, unsigned offset, unsigned bit_offset, unsigned bit_size); extern struct symt_enum* symt_new_enum(struct module* module, const char* typename, diff --git a/dlls/dbghelp/dwarf.c b/dlls/dbghelp/dwarf.c index ce53e37e72cb..3523b84e8be4 100644 --- a/dlls/dbghelp/dwarf.c +++ b/dlls/dbghelp/dwarf.c @@ -1894,7 +1894,8 @@ static void dwarf2_parse_udt_member(dwarf2_debug_info_t* di, bit_offset.u.uvalue = nbytes.u.uvalue * 8 - bit_offset.u.uvalue - bit_size.u.uvalue; } else bit_offset.u.uvalue = 0; - symt_add_udt_element(di->unit_ctx->module_ctx->module, parent, name.u.string, elt_type, + symt_add_udt_element(di->unit_ctx->module_ctx->module, parent, name.u.string, + symt_ptr_to_symref(elt_type), loc.offset, bit_offset.u.uvalue, bit_size.u.uvalue); @@ -2118,14 +2119,14 @@ static void dwarf2_parse_variable(dwarf2_subprogram_t* subpgm, if (ext.u.uvalue) WARN("unexpected global inside a function\n"); symt_add_func_local(subpgm->ctx->module_ctx->module, subpgm->current_func, DataIsStaticLocal, &loc, subpgm->current_block, - param_type, dwarf2_get_cpp_name(di, name.u.string)); + symt_ptr_to_symref(param_type), dwarf2_get_cpp_name(di, name.u.string)); } else { symt_new_global_variable(subpgm->ctx->module_ctx->module, ext.u.uvalue ? NULL : subpgm->ctx->compiland, dwarf2_get_cpp_name(di, name.u.string), !ext.u.uvalue, - loc, 0, param_type); + loc, 0, symt_ptr_to_symref(param_type)); } break; default: @@ -2139,7 +2140,7 @@ static void dwarf2_parse_variable(dwarf2_subprogram_t* subpgm, if (subpgm->current_func) symt_add_func_local(subpgm->ctx->module_ctx->module, subpgm->current_func, is_pmt ? DataIsParam : DataIsLocal, - &loc, subpgm->current_block, param_type, name.u.string); + &loc, subpgm->current_block, symt_ptr_to_symref(param_type), name.u.string); break; } } @@ -2157,11 +2158,11 @@ static void dwarf2_parse_variable(dwarf2_subprogram_t* subpgm, debugstr_a(name.u.string), debugstr_a(subpgm->current_func->hash_elt.name)); di->symt = &symt_add_func_constant(subpgm->ctx->module_ctx->module, subpgm->current_func, subpgm->current_block, - param_type, name.u.string, &v)->symt; + symt_ptr_to_symref(param_type), name.u.string, &v)->symt; } else di->symt = &symt_new_constant(subpgm->ctx->module_ctx->module, subpgm->ctx->compiland, - name.u.string, param_type, &v)->symt; + name.u.string, symt_ptr_to_symref(param_type), &v)->symt; } else { @@ -2172,7 +2173,7 @@ static void dwarf2_parse_variable(dwarf2_subprogram_t* subpgm, loc.reg = loc_err_no_location; symt_add_func_local(subpgm->ctx->module_ctx->module, subpgm->current_func, is_pmt ? DataIsParam : DataIsLocal, - &loc, subpgm->current_block, param_type, name.u.string); + &loc, subpgm->current_block, symt_ptr_to_symref(param_type), name.u.string); } else { @@ -2241,7 +2242,7 @@ static void dwarf2_parse_inlined_subroutine(dwarf2_subprogram_t* subpgm, subpgm->top_func, subpgm->current_block ? &subpgm->current_block->symt : &subpgm->current_func->symt, dwarf2_get_cpp_name(di, name.u.string), - dwarf2_parse_subroutine_type(di), num_ranges); + symt_ptr_to_symref(dwarf2_parse_subroutine_type(di)), num_ranges); subpgm->current_func = inlined; subpgm->current_block = NULL; @@ -2451,7 +2452,7 @@ static struct symt* dwarf2_parse_subprogram(dwarf2_debug_info_t* di) subpgm.top_func = symt_new_function(di->unit_ctx->module_ctx->module, di->unit_ctx->compiland, dwarf2_get_cpp_name(di, name.u.string), addr_ranges[0].low, addr_ranges[0].high - addr_ranges[0].low, - dwarf2_parse_subroutine_type(di)); + symt_ptr_to_symref(dwarf2_parse_subroutine_type(di))); if (num_addr_ranges > 1) WARN("Function %s has multiple address ranges, only using the first one\n", debugstr_a(name.u.string)); free(addr_ranges); diff --git a/dlls/dbghelp/elf_module.c b/dlls/dbghelp/elf_module.c index 803cf360f856..dc06c5f74273 100644 --- a/dlls/dbghelp/elf_module.c +++ b/dlls/dbghelp/elf_module.c @@ -960,7 +960,7 @@ static int elf_new_wine_thunks(struct module* module, const struct hash_table* h { case ELF_STT_FUNC: symt_new_function(module, ste->compiland, ste->ht_elt.name, - addr, ste->sym.st_size, NULL); + addr, ste->sym.st_size, 0); break; case ELF_STT_OBJECT: loc.kind = loc_absolute; @@ -968,7 +968,7 @@ static int elf_new_wine_thunks(struct module* module, const struct hash_table* h loc.offset = addr; symt_new_global_variable(module, ste->compiland, ste->ht_elt.name, elf_is_local_symbol(ste->sym.st_info), - loc, ste->sym.st_size, NULL); + loc, ste->sym.st_size, 0); break; default: FIXME("Shouldn't happen\n"); diff --git a/dlls/dbghelp/macho_module.c b/dlls/dbghelp/macho_module.c index 5f66509be7b3..14ef358697da 100644 --- a/dlls/dbghelp/macho_module.c +++ b/dlls/dbghelp/macho_module.c @@ -1190,7 +1190,7 @@ static void macho_finish_stabs(struct module* module, struct hash_table* ht_symt if (ste->is_code) { symt_new_function(module, ste->compiland, ste->ht_elt.name, - ste->addr, 0, NULL); + ste->addr, 0, 0); } else { @@ -1200,7 +1200,7 @@ static void macho_finish_stabs(struct module* module, struct hash_table* ht_symt loc.reg = 0; loc.offset = ste->addr; symt_new_global_variable(module, ste->compiland, ste->ht_elt.name, - !ste->is_global, loc, 0, NULL); + !ste->is_global, loc, 0, 0); } ste->used = 1; diff --git a/dlls/dbghelp/msc.c b/dlls/dbghelp/msc.c index 6c8f9b3637dc..56e1491ee7b5 100644 --- a/dlls/dbghelp/msc.c +++ b/dlls/dbghelp/msc.c @@ -964,13 +964,13 @@ static void codeview_add_udt_element(struct codeview_type_parse* ctp, { case LF_BITFIELD_V1: symt_add_udt_element(ctp->module, symt, name, - codeview_fetch_type(ctp, cv_type->bitfield_v1.type), + symt_ptr_to_symref(codeview_fetch_type(ctp, cv_type->bitfield_v1.type)), value, cv_type->bitfield_v1.bitoff, cv_type->bitfield_v1.nbits); return; case LF_BITFIELD_V2: symt_add_udt_element(ctp->module, symt, name, - codeview_fetch_type(ctp, cv_type->bitfield_v2.type), + symt_ptr_to_symref(codeview_fetch_type(ctp, cv_type->bitfield_v2.type)), value, cv_type->bitfield_v2.bitoff, cv_type->bitfield_v2.nbits); return; @@ -982,7 +982,8 @@ static void codeview_add_udt_element(struct codeview_type_parse* ctp, { DWORD64 elem_size = 0; symt_get_info(ctp->module, subtype, TI_GET_LENGTH, &elem_size); - symt_add_udt_element(ctp->module, symt, name, subtype, value, 0, 0); + symt_add_udt_element(ctp->module, symt, name, symt_ptr_to_symref(subtype), + value, 0, 0); } } @@ -1815,7 +1816,7 @@ static inline void codeview_add_variable(const struct msc_debug_info* msc_dbg, { if (!is_local || in_tls) WARN("Unsupported construct\n"); symt_add_func_local(msc_dbg->module, func, DataIsStaticLocal, &loc, block, - codeview_get_type(symtype, FALSE), name); + symt_ptr_to_symref(codeview_get_type(symtype, FALSE)), name); return; } if (!dontcheck && !in_tls) @@ -1848,7 +1849,7 @@ static inline void codeview_add_variable(const struct msc_debug_info* msc_dbg, } if (is_local ^ (compiland != NULL)) FIXME("Unsupported construct\n"); symt_new_global_variable(msc_dbg->module, compiland, name, is_local, loc, 0, - codeview_get_type(symtype, FALSE)); + symt_ptr_to_symref(codeview_get_type(symtype, FALSE))); } } @@ -2183,14 +2184,14 @@ static struct symt_function* codeview_create_inline_site(const struct msc_debug_ case LF_FUNC_ID: inlined = symt_new_inlinesite(msc_dbg->module, top_func, container, cvt->func_id_v3.name, - codeview_get_type(cvt->func_id_v3.type, FALSE), + symt_ptr_to_symref(codeview_get_type(cvt->func_id_v3.type, FALSE)), num_ranges); break; case LF_MFUNC_ID: /* FIXME we just declare a function, not a method */ inlined = symt_new_inlinesite(msc_dbg->module, top_func, container, cvt->mfunc_id_v3.name, - codeview_get_type(cvt->mfunc_id_v3.type, FALSE), + symt_ptr_to_symref(codeview_get_type(cvt->mfunc_id_v3.type, FALSE)), num_ranges); break; default: @@ -2305,6 +2306,7 @@ static BOOL codeview_snarf(const struct msc_debug_info* msc_dbg, { struct symt_function* top_func = NULL; struct symt_function* curr_func = NULL; + struct symt* func_signature; int i, length; struct symt_block* block = NULL; struct symt* symt; @@ -2405,47 +2407,59 @@ static BOOL codeview_snarf(const struct msc_debug_info* msc_dbg, case S_GPROC32_16t: case S_LPROC32_16t: if (top_func) FIXME("nested function\n"); - top_func = symt_new_function(msc_dbg->module, compiland, - terminate_string(&sym->proc_v1.p_name), - codeview_get_address(msc_dbg, sym->proc_v1.segment, sym->proc_v1.offset), - sym->proc_v1.proc_len, - codeview_get_type(sym->proc_v1.proctype, FALSE)); - curr_func = top_func; - loc.kind = loc_absolute; - loc.offset = sym->proc_v1.debug_start; - symt_add_function_point(msc_dbg->module, curr_func, SymTagFuncDebugStart, &loc, NULL); - loc.offset = sym->proc_v1.debug_end; - symt_add_function_point(msc_dbg->module, curr_func, SymTagFuncDebugEnd, &loc, NULL); + func_signature = codeview_get_type(sym->proc_v1.proctype, FALSE); + if (!func_signature || func_signature->tag == SymTagFunctionType) + { + top_func = symt_new_function(msc_dbg->module, compiland, + terminate_string(&sym->proc_v1.p_name), + codeview_get_address(msc_dbg, sym->proc_v1.segment, sym->proc_v1.offset), + sym->proc_v1.proc_len, + symt_ptr_to_symref(func_signature)); + curr_func = top_func; + loc.kind = loc_absolute; + loc.offset = sym->proc_v1.debug_start; + symt_add_function_point(msc_dbg->module, curr_func, SymTagFuncDebugStart, &loc, NULL); + loc.offset = sym->proc_v1.debug_end; + symt_add_function_point(msc_dbg->module, curr_func, SymTagFuncDebugEnd, &loc, NULL); + } break; case S_GPROC32_ST: case S_LPROC32_ST: if (top_func) FIXME("nested function\n"); - top_func = symt_new_function(msc_dbg->module, compiland, - terminate_string(&sym->proc_v2.p_name), - codeview_get_address(msc_dbg, sym->proc_v2.segment, sym->proc_v2.offset), - sym->proc_v2.proc_len, - codeview_get_type(sym->proc_v2.proctype, FALSE)); - curr_func = top_func; - loc.kind = loc_absolute; - loc.offset = sym->proc_v2.debug_start; - symt_add_function_point(msc_dbg->module, curr_func, SymTagFuncDebugStart, &loc, NULL); - loc.offset = sym->proc_v2.debug_end; - symt_add_function_point(msc_dbg->module, curr_func, SymTagFuncDebugEnd, &loc, NULL); + func_signature = codeview_get_type(sym->proc_v2.proctype, FALSE); + if (!func_signature || func_signature->tag == SymTagFunctionType) + { + top_func = symt_new_function(msc_dbg->module, compiland, + terminate_string(&sym->proc_v2.p_name), + codeview_get_address(msc_dbg, sym->proc_v2.segment, sym->proc_v2.offset), + sym->proc_v2.proc_len, + symt_ptr_to_symref(func_signature)); + curr_func = top_func; + loc.kind = loc_absolute; + loc.offset = sym->proc_v2.debug_start; + symt_add_function_point(msc_dbg->module, curr_func, SymTagFuncDebugStart, &loc, NULL); + loc.offset = sym->proc_v2.debug_end; + symt_add_function_point(msc_dbg->module, curr_func, SymTagFuncDebugEnd, &loc, NULL); + } break; case S_GPROC32: case S_LPROC32: if (top_func) FIXME("nested function\n"); - top_func = symt_new_function(msc_dbg->module, compiland, - sym->proc_v3.name, - codeview_get_address(msc_dbg, sym->proc_v3.segment, sym->proc_v3.offset), - sym->proc_v3.proc_len, - codeview_get_type(sym->proc_v3.proctype, FALSE)); - curr_func = top_func; - loc.kind = loc_absolute; - loc.offset = sym->proc_v3.debug_start; - symt_add_function_point(msc_dbg->module, curr_func, SymTagFuncDebugStart, &loc, NULL); - loc.offset = sym->proc_v3.debug_end; - symt_add_function_point(msc_dbg->module, curr_func, SymTagFuncDebugEnd, &loc, NULL); + func_signature = codeview_get_type(sym->proc_v3.proctype, FALSE); + if (!func_signature || func_signature->tag == SymTagFunctionType) + { + top_func = symt_new_function(msc_dbg->module, compiland, + sym->proc_v3.name, + codeview_get_address(msc_dbg, sym->proc_v3.segment, sym->proc_v3.offset), + sym->proc_v3.proc_len, + symt_ptr_to_symref(func_signature)); + curr_func = top_func; + loc.kind = loc_absolute; + loc.offset = sym->proc_v3.debug_start; + symt_add_function_point(msc_dbg->module, curr_func, SymTagFuncDebugStart, &loc, NULL); + loc.offset = sym->proc_v3.debug_end; + symt_add_function_point(msc_dbg->module, curr_func, SymTagFuncDebugEnd, &loc, NULL); + } break; /* * Function parameters and stack variables. @@ -2458,7 +2472,7 @@ static BOOL codeview_snarf(const struct msc_debug_info* msc_dbg, symt_add_func_local(msc_dbg->module, curr_func, sym->stack_v1.offset > 0 ? DataIsParam : DataIsLocal, &loc, block, - codeview_get_type(sym->stack_v1.symtype, FALSE), + symt_ptr_to_symref(codeview_get_type(sym->stack_v1.symtype, FALSE)), terminate_string(&sym->stack_v1.p_name)); break; case S_BPREL32_ST: @@ -2469,7 +2483,7 @@ static BOOL codeview_snarf(const struct msc_debug_info* msc_dbg, symt_add_func_local(msc_dbg->module, curr_func, sym->stack_v2.offset > 0 ? DataIsParam : DataIsLocal, &loc, block, - codeview_get_type(sym->stack_v2.symtype, FALSE), + symt_ptr_to_symref(codeview_get_type(sym->stack_v2.symtype, FALSE)), terminate_string(&sym->stack_v2.p_name)); break; case S_BPREL32: @@ -2482,7 +2496,7 @@ static BOOL codeview_snarf(const struct msc_debug_info* msc_dbg, symt_add_func_local(msc_dbg->module, curr_func, sym->stack_v3.offset > 0 ? DataIsParam : DataIsLocal, &loc, block, - codeview_get_type(sym->stack_v3.symtype, FALSE), + symt_ptr_to_symref(codeview_get_type(sym->stack_v3.symtype, FALSE)), sym->stack_v3.name); break; case S_REGREL32: @@ -2495,7 +2509,7 @@ static BOOL codeview_snarf(const struct msc_debug_info* msc_dbg, symt_add_func_local(msc_dbg->module, curr_func, sym->regrel_v3.offset >= top_frame_size ? DataIsParam : DataIsLocal, &loc, block, - codeview_get_type(sym->regrel_v3.symtype, FALSE), + symt_ptr_to_symref(codeview_get_type(sym->regrel_v3.symtype, FALSE)), sym->regrel_v3.name); break; @@ -2504,8 +2518,8 @@ static BOOL codeview_snarf(const struct msc_debug_info* msc_dbg, loc.reg = sym->register_v1.reg; loc.offset = 0; symt_add_func_local(msc_dbg->module, curr_func, - DataIsLocal, &loc, - block, codeview_get_type(sym->register_v1.type, FALSE), + DataIsLocal, &loc, block, + symt_ptr_to_symref(codeview_get_type(sym->register_v1.type, FALSE)), terminate_string(&sym->register_v1.p_name)); break; case S_REGISTER_ST: @@ -2513,8 +2527,8 @@ static BOOL codeview_snarf(const struct msc_debug_info* msc_dbg, loc.reg = sym->register_v2.reg; loc.offset = 0; symt_add_func_local(msc_dbg->module, curr_func, - DataIsLocal, &loc, - block, codeview_get_type(sym->register_v2.type, FALSE), + DataIsLocal, &loc, block, + symt_ptr_to_symref(codeview_get_type(sym->register_v2.type, FALSE)), terminate_string(&sym->register_v2.p_name)); break; case S_REGISTER: @@ -2524,8 +2538,8 @@ static BOOL codeview_snarf(const struct msc_debug_info* msc_dbg, loc.reg = sym->register_v3.reg; loc.offset = 0; symt_add_func_local(msc_dbg->module, curr_func, - DataIsLocal, &loc, - block, codeview_get_type(sym->register_v3.type, FALSE), + DataIsLocal, &loc, block, + symt_ptr_to_symref(codeview_get_type(sym->register_v3.type, FALSE)), sym->register_v3.name); break; @@ -2625,7 +2639,7 @@ static BOOL codeview_snarf(const struct msc_debug_info* msc_dbg, TRACE("S-Constant-V1 %u %s %x\n", V_INT(&v), terminate_string(name), sym->constant_v1.type); symt_new_constant(msc_dbg->module, compiland, terminate_string(name), - se, &v); + symt_ptr_to_symref(se), &v); } break; case S_CONSTANT_ST: @@ -2641,7 +2655,7 @@ static BOOL codeview_snarf(const struct msc_debug_info* msc_dbg, TRACE("S-Constant-V2 %u %s %x\n", V_INT(&v), terminate_string(name), sym->constant_v2.type); symt_new_constant(msc_dbg->module, compiland, terminate_string(name), - se, &v); + symt_ptr_to_symref(se), &v); } break; case S_CONSTANT: @@ -2657,7 +2671,7 @@ static BOOL codeview_snarf(const struct msc_debug_info* msc_dbg, TRACE("S-Constant-V3 %u %s %x\n", V_INT(&v), debugstr_a(name), sym->constant_v3.type); /* FIXME: we should add this as a constant value */ - symt_new_constant(msc_dbg->module, compiland, name, se, &v); + symt_new_constant(msc_dbg->module, compiland, name, symt_ptr_to_symref(se), &v); } break; @@ -2712,7 +2726,7 @@ static BOOL codeview_snarf(const struct msc_debug_info* msc_dbg, symt_add_func_local(msc_dbg->module, curr_func, sym->local_v3.varflags.is_param ? DataIsParam : DataIsLocal, &loc, block, - codeview_get_type(sym->local_v3.symtype, FALSE), + symt_ptr_to_symref(codeview_get_type(sym->local_v3.symtype, FALSE)), sym->local_v3.name); } else diff --git a/dlls/dbghelp/stabs.c b/dlls/dbghelp/stabs.c index 8d9b91eeb4fa..efd2bb2f9432 100644 --- a/dlls/dbghelp/stabs.c +++ b/dlls/dbghelp/stabs.c @@ -618,7 +618,8 @@ static inline int stabs_pts_read_aggregate(struct ParseTypedefData* ptd, strcpy(tmp, "__inherited_class_"); strcat(tmp, symt_get_name(adt)); - symt_add_udt_element(ptd->module, sdt, tmp, adt, ofs, 0, 0); + symt_add_udt_element(ptd->module, sdt, tmp, symt_ptr_to_symref(adt), + ofs, 0, 0); } PTS_ABORTIF(ptd, *ptd->ptr++ != ';'); } @@ -692,7 +693,8 @@ static inline int stabs_pts_read_aggregate(struct ParseTypedefData* ptd, PTS_ABORTIF(ptd, stabs_pts_read_number(ptd, &sz) == -1); PTS_ABORTIF(ptd, *ptd->ptr++ != ';'); - if (doadd) symt_add_udt_element(ptd->module, sdt, ptd->buf + idx, adt, ofs, 0, 0); + if (doadd) symt_add_udt_element(ptd->module, sdt, ptd->buf + idx, symt_ptr_to_symref(adt), + ofs, 0, 0); break; case ':': { @@ -1171,7 +1173,7 @@ static void pending_flush(struct pending_list* pending, struct module* module, case PENDING_VAR: symt_add_func_local(module, func, pending->objs[i].u.var.kind, &pending->objs[i].u.var.loc, - block, pending->objs[i].u.var.type, pending->objs[i].u.var.name); + block, symt_ptr_to_symref(pending->objs[i].u.var.type), pending->objs[i].u.var.name); break; case PENDING_LINE: if (module->type == DMT_MACHO) @@ -1363,7 +1365,7 @@ BOOL stabs_parse(struct module* module, ULONG_PTR load_offset, loc.reg = 0; loc.offset = load_offset + n_value; symt_new_global_variable(module, compiland, symname, TRUE /* FIXME */, - loc, 0, stabs_parse_type(ptr)); + loc, 0, symt_ptr_to_symref(stabs_parse_type(ptr))); break; case N_LCSYM: case N_STSYM: @@ -1378,7 +1380,7 @@ BOOL stabs_parse(struct module* module, ULONG_PTR load_offset, loc.reg = 0; loc.offset = load_offset + n_value; symt_new_global_variable(module, compiland, symname, TRUE /* FIXME */, - loc, 0, stabs_parse_type(ptr)); + loc, 0, symt_ptr_to_symref(stabs_parse_type(ptr))); break; case N_LBRAC: if (curr_func) @@ -1412,9 +1414,9 @@ BOOL stabs_parse(struct module* module, ULONG_PTR load_offset, loc.offset = n_value; symt_add_func_local(module, curr_func, (int)n_value >= 0 ? DataIsParam : DataIsLocal, - &loc, NULL, param_type, symname); - symt_add_function_signature_parameter(module, - (struct symt_function_signature*)curr_func->type, + &loc, NULL, symt_ptr_to_symref(param_type), symname); + symt_add_function_signature_parameter(module, + (struct symt_function_signature*)curr_func->type, param_type); } break; @@ -1476,9 +1478,9 @@ BOOL stabs_parse(struct module* module, ULONG_PTR load_offset, struct symt* param_type = stabs_parse_type(ptr); stab_strcpy(symname, sizeof(symname), ptr); symt_add_func_local(module, curr_func, DataIsParam, &loc, - NULL, param_type, symname); - symt_add_function_signature_parameter(module, - (struct symt_function_signature*)curr_func->type, + NULL, symt_ptr_to_symref(param_type), symname); + symt_add_function_signature_parameter(module, + (struct symt_function_signature*)curr_func->type, param_type); } else @@ -1547,9 +1549,9 @@ BOOL stabs_parse(struct module* module, ULONG_PTR load_offset, } func_type = symt_new_function_signature(module, stabs_parse_type(ptr), -1); - curr_func = symt_new_function(module, compiland, symname, + curr_func = symt_new_function(module, compiland, symname, load_offset + n_value, 0, - &func_type->symt); + symt_ptr_to_symref(&func_type->symt)); pending_flush(&pending_func, module, curr_func, NULL); } else diff --git a/dlls/dbghelp/symbol.c b/dlls/dbghelp/symbol.c index 01d3267808bb..8bf8a5b35a6e 100644 --- a/dlls/dbghelp/symbol.c +++ b/dlls/dbghelp/symbol.c @@ -125,6 +125,11 @@ struct symt* symt_index_to_ptr(struct module* module, DWORD id) return (id >= vector_length(vector)) ? NULL : *(struct symt**)vector_at(vector, id); } +DWORD symt_symref_to_index(struct module *module, symref_t ref) +{ + return symt_ptr_to_index(module, (struct symt*)ref); +} + static BOOL symt_grow_sorttab(struct module* module, unsigned sz) { struct symt_ht** new; @@ -288,13 +293,13 @@ struct symt_data* symt_new_global_variable(struct module* module, struct symt_compiland* compiland, const char* name, unsigned is_static, struct location loc, ULONG_PTR size, - struct symt* type) + symref_t type) { struct symt_data* sym; struct symt** p; DWORD64 tsz; - TRACE_(dbghelp_symt)("Adding global symbol %s:%s %d@%Ix %p\n", + TRACE_(dbghelp_symt)("Adding global symbol %s:%s %d@%Ix %Ix\n", debugstr_w(module->modulename), debugstr_a(name), loc.kind, loc.offset, type); if ((sym = pool_alloc(&module->pool, sizeof(*sym)))) { @@ -304,7 +309,7 @@ struct symt_data* symt_new_global_variable(struct module* module, sym->container = compiland ? &compiland->symt : &module->top->symt; sym->type = type; sym->u.var = loc; - if (type && size && symt_get_info(module, type, TI_GET_LENGTH, &tsz)) + if (type && size && symt_get_info_from_symref(module, type, TI_GET_LENGTH, &tsz)) { if (tsz != size) FIXME("Size mismatch for %s.%s between type (%I64u) and src (%Iu)\n", @@ -321,12 +326,11 @@ static struct symt_function* init_function_or_inlinesite(struct module* module, DWORD tag, struct symt* container, const char* name, - struct symt* sig_type, + symref_t sig_type, unsigned num_ranges) { struct symt_function* sym; - assert(!sig_type || sig_type->tag == SymTagFunctionType); if ((sym = pool_alloc(&module->pool, offsetof(struct symt_function, ranges[num_ranges])))) { sym->symt.tag = tag; @@ -344,7 +348,7 @@ struct symt_function* symt_new_function(struct module* module, struct symt_compiland* compiland, const char* name, ULONG_PTR addr, ULONG_PTR size, - struct symt* sig_type) + symref_t sig_type) { struct symt_function* sym; @@ -370,7 +374,7 @@ struct symt_function* symt_new_inlinesite(struct module* module, struct symt_function* func, struct symt* container, const char* name, - struct symt* sig_type, + symref_t sig_type, unsigned num_ranges) { struct symt_function* sym; @@ -461,17 +465,17 @@ void symt_add_func_line(struct module* module, struct symt_function* func, * Otherwise, the variable is stored on the stack: * - offset is then the offset from the frame register */ -struct symt_data* symt_add_func_local(struct module* module, - struct symt_function* func, +struct symt_data* symt_add_func_local(struct module* module, + struct symt_function* func, enum DataKind dt, const struct location* loc, - struct symt_block* block, - struct symt* type, const char* name) + struct symt_block* block, + symref_t type, const char* name) { struct symt_data* locsym; struct symt** p; - TRACE_(dbghelp_symt)("Adding local symbol (%s:%s): %s %p\n", + TRACE_(dbghelp_symt)("Adding local symbol (%s:%s): %s %Ix\n", debugstr_w(module->modulename), debugstr_a(func->hash_elt.name), debugstr_a(name), type); @@ -504,13 +508,13 @@ struct symt_data* symt_add_func_local(struct module* module, struct symt_data* symt_add_func_constant(struct module* module, struct symt_function* func, struct symt_block* block, - struct symt* type, const char* name, + symref_t type, const char* name, VARIANT* v) { struct symt_data* locsym; struct symt** p; - TRACE_(dbghelp_symt)("Adding local constant (%s:%s): %s %p\n", + TRACE_(dbghelp_symt)("Adding local constant (%s:%s): %s %Ix\n", debugstr_w(module->modulename), debugstr_a(func->hash_elt.name), debugstr_a(name), type); @@ -620,7 +624,7 @@ struct symt_thunk* symt_new_thunk(struct module* module, struct symt_data* symt_new_constant(struct module* module, struct symt_compiland* compiland, - const char* name, struct symt* type, + const char* name, symref_t type, const VARIANT* v) { struct symt_data* sym; diff --git a/dlls/dbghelp/type.c b/dlls/dbghelp/type.c index 0f3a94453a0c..4fa4c8e7b00b 100644 --- a/dlls/dbghelp/type.c +++ b/dlls/dbghelp/type.c @@ -280,7 +280,7 @@ BOOL symt_set_udt_size(struct module* module, struct symt_udt* udt, unsigned siz * the others (bit fields) */ BOOL symt_add_udt_element(struct module* module, struct symt_udt* udt_type, - const char* name, struct symt* elt_type, + const char* name, symref_t elt_type, unsigned offset, unsigned bit_offset, unsigned bit_size) { struct symt_data* m; @@ -356,7 +356,7 @@ BOOL symt_add_enum_element(struct module* module, struct symt_enum* enum_type, e->hash_elt.next = NULL; e->kind = DataIsConstant; e->container = &enum_type->symt; - e->type = enum_type->base_type; + e->type = symt_ptr_to_symref(enum_type->base_type); e->u.value = *variant; p = vector_add(&enum_type->vchildren, &module->pool); @@ -804,7 +804,7 @@ BOOL symt_get_info(struct module* module, const struct symt* type, X(DWORD64) = ((const struct symt_data*)type)->u.member.bit_length; break; default: - if (!symt_get_info(module, ((const struct symt_data*)type)->type, TI_GET_LENGTH, pInfo)) + if (!symt_get_info_from_symref(module, ((const struct symt_data*)type)->type, TI_GET_LENGTH, pInfo)) return FALSE; } break; @@ -994,11 +994,11 @@ BOOL symt_get_info(struct module* module, const struct symt* type, break; /* lexical => hierarchical */ case SymTagData: - X(DWORD) = symt_ptr_to_index(module, ((const struct symt_data*)type)->type); + X(DWORD) = symt_symref_to_index(module, ((const struct symt_data*)type)->type); break; case SymTagFunction: case SymTagInlineSite: - X(DWORD) = symt_ptr_to_index(module, ((const struct symt_function*)type)->type); + X(DWORD) = symt_symref_to_index(module, ((const struct symt_function*)type)->type); break; case SymTagEnum: X(DWORD) = symt_ptr_to_index(module, ((const struct symt_enum*)type)->base_type); @@ -1117,6 +1117,12 @@ BOOL symt_get_info_from_index(struct module* module, DWORD index, return symt_get_info(module, symt_index_to_ptr(module, index), req, pInfo); } +BOOL symt_get_info_from_symref(struct module* module, symref_t ref, + IMAGEHLP_SYMBOL_TYPE_INFO req, void* pInfo) +{ + return symt_get_info(module, (struct symt*)ref, req, pInfo); +} + /****************************************************************** * SymGetTypeInfo (DBGHELP.@) * From 62c769ef2ad9e34c3978a45bc12c058233ae759a Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Tue, 24 Dec 2024 10:45:39 +0100 Subject: [PATCH 252/454] dbghelp: Use opaque symref_t inside typedef symbol. Signed-off-by: Eric Pouech (cherry picked from commit 795dc9caf506cf78bcc8c62ff774b3108a63dacf) --- dlls/dbghelp/dbghelp_private.h | 6 +++--- dlls/dbghelp/dwarf.c | 6 +++--- dlls/dbghelp/msc.c | 12 ++++++------ dlls/dbghelp/type.c | 6 +++--- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/dlls/dbghelp/dbghelp_private.h b/dlls/dbghelp/dbghelp_private.h index edf3117550aa..d89421efa413 100644 --- a/dlls/dbghelp/dbghelp_private.h +++ b/dlls/dbghelp/dbghelp_private.h @@ -388,7 +388,7 @@ struct symt_typedef { struct symt symt; struct hash_table_elt hash_elt; - struct symt* type; + symref_t type; }; struct symt_udt @@ -1010,11 +1010,11 @@ extern BOOL symt_add_function_signature_parameter(struct module* module, struct symt_function_signature* sig, struct symt* param); extern struct symt_pointer* - symt_new_pointer(struct module* module, + symt_new_pointer(struct module* module, struct symt* ref_type, ULONG_PTR size); extern struct symt_typedef* - symt_new_typedef(struct module* module, struct symt* ref, + symt_new_typedef(struct module* module, symref_t ref, const char* name); extern struct symt_function* symt_find_lowest_inlined(struct symt_function* func, DWORD64 addr); diff --git a/dlls/dbghelp/dwarf.c b/dlls/dbghelp/dwarf.c index 3523b84e8be4..068ad53fb4af 100644 --- a/dlls/dbghelp/dwarf.c +++ b/dlls/dbghelp/dwarf.c @@ -715,7 +715,7 @@ static BOOL dwarf2_fill_attr(const dwarf2_parse_context_t* ctx, static struct symt *symt_get_real_type(struct symt *symt) { while (symt && symt->tag == SymTagTypedef) - symt = ((struct symt_typedef*)symt)->type; + symt = (struct symt*)(((struct symt_typedef*)symt)->type); return symt; } @@ -1650,7 +1650,7 @@ static struct symt* dwarf2_parse_typedef(dwarf2_debug_info_t* di) */ if ((is_c_language(di->unit_ctx) || is_cpp_language(di->unit_ctx)) && !strcmp(name.u.string, "WCHAR")) ref_type = &symt_get_basic(btWChar, 2)->symt; - di->symt = &symt_new_typedef(di->unit_ctx->module_ctx->module, ref_type, name.u.string)->symt; + di->symt = &symt_new_typedef(di->unit_ctx->module_ctx->module, symt_ptr_to_symref(ref_type), name.u.string)->symt; } if (dwarf2_get_di_children(di)) FIXME("Unsupported children\n"); return di->symt; @@ -1822,7 +1822,7 @@ static struct symt* dwarf2_parse_unspecified_type(dwarf2_debug_info_t* di) basic = &symt_get_basic(btVoid, 0)->symt; if (dwarf2_find_attribute(di, DW_AT_name, &name)) /* define the missing type as a typedef to void... */ - di->symt = &symt_new_typedef(di->unit_ctx->module_ctx->module, basic, name.u.string)->symt; + di->symt = &symt_new_typedef(di->unit_ctx->module_ctx->module, symt_ptr_to_symref(basic), name.u.string)->symt; else /* or use void if it doesn't even have a name */ di->symt = basic; diff --git a/dlls/dbghelp/msc.c b/dlls/dbghelp/msc.c index 56e1491ee7b5..5163532a41d6 100644 --- a/dlls/dbghelp/msc.c +++ b/dlls/dbghelp/msc.c @@ -2679,7 +2679,7 @@ static BOOL codeview_snarf(const struct msc_debug_info* msc_dbg, if (sym->udt_v1.type) { if ((symt = codeview_get_type(sym->udt_v1.type, FALSE))) - symt_new_typedef(msc_dbg->module, symt, + symt_new_typedef(msc_dbg->module, symt_ptr_to_symref(symt), terminate_string(&sym->udt_v1.p_name)); else FIXME("S-Udt %s: couldn't find type 0x%x\n", @@ -2690,7 +2690,7 @@ static BOOL codeview_snarf(const struct msc_debug_info* msc_dbg, if (sym->udt_v2.type) { if ((symt = codeview_get_type(sym->udt_v2.type, FALSE))) - symt_new_typedef(msc_dbg->module, symt, + symt_new_typedef(msc_dbg->module, symt_ptr_to_symref(symt), terminate_string(&sym->udt_v2.p_name)); else FIXME("S-Udt %s: couldn't find type 0x%x\n", @@ -2701,7 +2701,7 @@ static BOOL codeview_snarf(const struct msc_debug_info* msc_dbg, if (sym->udt_v3.type) { if ((symt = codeview_get_type(sym->udt_v3.type, FALSE))) - symt_new_typedef(msc_dbg->module, symt, sym->udt_v3.name); + symt_new_typedef(msc_dbg->module, symt_ptr_to_symref(symt), sym->udt_v3.name); else FIXME("S-Udt %s: couldn't find type 0x%x\n", debugstr_a(sym->udt_v3.name), sym->udt_v3.type); @@ -2988,7 +2988,7 @@ static BOOL pdb_global_feed_types(const struct msc_debug_info* msc_dbg, const un if (sym->udt_v1.type) { if ((symt = codeview_get_type(sym->udt_v1.type, FALSE))) - symt_new_typedef(msc_dbg->module, symt, + symt_new_typedef(msc_dbg->module, symt_ptr_to_symref(symt), terminate_string(&sym->udt_v1.p_name)); else FIXME("S-Udt %s: couldn't find type 0x%x\n", @@ -2999,7 +2999,7 @@ static BOOL pdb_global_feed_types(const struct msc_debug_info* msc_dbg, const un if (sym->udt_v2.type) { if ((symt = codeview_get_type(sym->udt_v2.type, FALSE))) - symt_new_typedef(msc_dbg->module, symt, + symt_new_typedef(msc_dbg->module, symt_ptr_to_symref(symt), terminate_string(&sym->udt_v2.p_name)); else FIXME("S-Udt %s: couldn't find type 0x%x\n", @@ -3010,7 +3010,7 @@ static BOOL pdb_global_feed_types(const struct msc_debug_info* msc_dbg, const un if (sym->udt_v3.type) { if ((symt = codeview_get_type(sym->udt_v3.type, FALSE))) - symt_new_typedef(msc_dbg->module, symt, sym->udt_v3.name); + symt_new_typedef(msc_dbg->module, symt_ptr_to_symref(symt), sym->udt_v3.name); else FIXME("S-Udt %s: couldn't find type 0x%x\n", debugstr_a(sym->udt_v3.name), sym->udt_v3.type); diff --git a/dlls/dbghelp/type.c b/dlls/dbghelp/type.c index 4fa4c8e7b00b..e47a04401c18 100644 --- a/dlls/dbghelp/type.c +++ b/dlls/dbghelp/type.c @@ -430,7 +430,7 @@ struct symt_pointer* symt_new_pointer(struct module* module, struct symt* ref_ty return sym; } -struct symt_typedef* symt_new_typedef(struct module* module, struct symt* ref, +struct symt_typedef* symt_new_typedef(struct module* module, symref_t ref, const char* typename) { struct symt_typedef* sym; @@ -818,7 +818,7 @@ BOOL symt_get_info(struct module* module, const struct symt* type, X(DWORD64) = ((const struct symt_public*)type)->size; break; case SymTagTypedef: - return symt_get_info(module, ((const struct symt_typedef*)type)->type, TI_GET_LENGTH, pInfo); + return symt_get_info_from_symref(module, ((const struct symt_typedef*)type)->type, TI_GET_LENGTH, pInfo); case SymTagThunk: X(DWORD64) = ((const struct symt_thunk*)type)->size; break; @@ -990,7 +990,7 @@ BOOL symt_get_info(struct module* module, const struct symt* type, X(DWORD) = symt_ptr_to_index(module, ((const struct symt_function_signature*)type)->rettype); break; case SymTagTypedef: - X(DWORD) = symt_ptr_to_index(module, ((const struct symt_typedef*)type)->type); + X(DWORD) = symt_symref_to_index(module, ((const struct symt_typedef*)type)->type); break; /* lexical => hierarchical */ case SymTagData: From 3e8876414d0ce5adec3f27e84d52e8c8e6caa9bf Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Wed, 11 Dec 2024 09:20:06 +0100 Subject: [PATCH 253/454] dbghelp: Introduce helpers to discrimate symref_t owner. It's a ptr when lower 2 bits are unset, an opaque backend value otherwise. Signed-off-by: Eric Pouech (cherry picked from commit 78fe10140330be75c7c15c2f00add82b179a11bd) --- dlls/dbghelp/dbghelp.c | 5 ++++- dlls/dbghelp/dbghelp_private.h | 8 +++++--- dlls/dbghelp/module.c | 4 ++-- dlls/dbghelp/symbol.c | 34 +++++++++++++++------------------- dlls/dbghelp/type.c | 2 +- 5 files changed, 27 insertions(+), 26 deletions(-) diff --git a/dlls/dbghelp/dbghelp.c b/dlls/dbghelp/dbghelp.c index 73c7f1eb9695..75881473c2cc 100644 --- a/dlls/dbghelp/dbghelp.c +++ b/dlls/dbghelp/dbghelp.c @@ -717,12 +717,15 @@ BOOL WINAPI SymSetScopeFromAddr(HANDLE hProcess, ULONG64 addr) BOOL WINAPI SymSetScopeFromIndex(HANDLE hProcess, ULONG64 addr, DWORD index) { struct module_pair pair; + symref_t symref; struct symt* sym; TRACE("(%p %#I64x %lu)\n", hProcess, addr, index); if (!module_init_pair(&pair, hProcess, addr)) return FALSE; - sym = symt_index_to_ptr(pair.effective, index); + symref = symt_index_to_symref(pair.effective, index); + if (!symt_is_symref_ptr(symref)) return FALSE; + sym = (struct symt*)symref; if (!symt_check_tag(sym, SymTagFunction)) return FALSE; pair.pcs->localscope_pc = ((struct symt_function*)sym)->ranges[0].low; /* FIXME of FuncDebugStart when it exists? */ diff --git a/dlls/dbghelp/dbghelp_private.h b/dlls/dbghelp/dbghelp_private.h index d89421efa413..8db72328133b 100644 --- a/dlls/dbghelp/dbghelp_private.h +++ b/dlls/dbghelp/dbghelp_private.h @@ -31,6 +31,7 @@ #include "winnls.h" #include "wine/list.h" #include "wine/rbtree.h" +#include "wine/debug.h" #include "cvconst.h" @@ -963,11 +964,12 @@ extern struct symt_hierarchy_point* symt_new_label(struct module* module, struct symt_compiland* compiland, const char* name, ULONG_PTR address); -extern struct symt* symt_index_to_ptr(struct module* module, DWORD id); -extern DWORD symt_ptr_to_index(struct module* module, const struct symt* sym); -extern DWORD symt_symref_to_index(struct module* module, symref_t ref); static inline symref_t symt_ptr_to_symref(const struct symt *symt) {return (ULONG_PTR)symt;} +extern symref_t symt_index_to_symref(struct module* module, DWORD id); +extern DWORD symt_symref_to_index(struct module* module, symref_t sym); +static inline DWORD symt_ptr_to_index(struct module *module, const struct symt *symt) {return symt_symref_to_index(module, symt_ptr_to_symref(symt));} +static inline BOOL symt_is_symref_ptr(symref_t ref) {return (ref & 3) == 0;} extern struct symt_custom* symt_new_custom(struct module* module, const char* name, DWORD64 addr, DWORD size); diff --git a/dlls/dbghelp/module.c b/dlls/dbghelp/module.c index d6f924be726a..8db470a35fe5 100644 --- a/dlls/dbghelp/module.c +++ b/dlls/dbghelp/module.c @@ -253,8 +253,8 @@ struct module* module_new(struct process* pcs, const WCHAR* name, module->cpu = dbghelp_current_cpu; module->debug_format_bitmask = 0; - vector_init(&module->vsymt, sizeof(struct symt*), 0); - vector_init(&module->vcustom_symt, sizeof(struct symt*), 0); + vector_init(&module->vsymt, sizeof(symref_t), 0); + vector_init(&module->vcustom_symt, sizeof(symref_t), 0); /* FIXME: this seems a bit too high (on a per module basis) * need some statistics about this */ diff --git a/dlls/dbghelp/symbol.c b/dlls/dbghelp/symbol.c index 8bf8a5b35a6e..ade18cc0a947 100644 --- a/dlls/dbghelp/symbol.c +++ b/dlls/dbghelp/symbol.c @@ -70,15 +70,15 @@ int __cdecl symt_cmp_addr(const void* p1, const void* p2) * which is exposed to the caller and index is the index of the symbol in * this array */ -DWORD symt_ptr_to_index(struct module* module, const struct symt* sym) +DWORD symt_symref_to_index(struct module* module, symref_t symref) { struct vector* vector; DWORD offset; - const struct symt** c; + symref_t *c; int len, i; - if (!sym) return 0; - if (sym->tag == SymTagCustom) + if (!symref) return 0; + if (symt_is_symref_ptr(symref) && ((struct symt*)symref)->tag == SymTagCustom) { vector = &module->vcustom_symt; offset = BASE_CUSTOM_SYMT; @@ -89,23 +89,23 @@ DWORD symt_ptr_to_index(struct module* module, const struct symt* sy vector = &module->vsymt; offset = 1; #else - return (DWORD)sym; + return (DWORD)symref; #endif } len = vector_length(vector); /* FIXME: this is inefficient */ for (i = 0; i < len; i++) { - if (*(struct symt**)vector_at(vector, i) == sym) + if (*(symref_t*)vector_at(vector, i) == symref) return i + offset; } /* not found */ c = vector_add(vector, &module->pool); - if (c) *c = sym; + if (c) *c = symref; return len + offset; } -struct symt* symt_index_to_ptr(struct module* module, DWORD id) +symref_t symt_index_to_symref(struct module* module, DWORD id) { struct vector* vector; if (id >= BASE_CUSTOM_SYMT) @@ -116,18 +116,13 @@ struct symt* symt_index_to_ptr(struct module* module, DWORD id) else { #ifdef _WIN64 - if (!id--) return NULL; + if (!id--) return 0; vector = &module->vsymt; #else - return (struct symt*)id; + return (symref_t)id; #endif } - return (id >= vector_length(vector)) ? NULL : *(struct symt**)vector_at(vector, id); -} - -DWORD symt_symref_to_index(struct module *module, symref_t ref) -{ - return symt_ptr_to_index(module, (struct symt*)ref); + return (id >= vector_length(vector)) ? 0 : *(symref_t *)vector_at(vector, id); } static BOOL symt_grow_sorttab(struct module* module, unsigned sz) @@ -2653,14 +2648,15 @@ BOOL WINAPI SymGetLineFromNameW64(HANDLE hProcess, PCWSTR ModuleName, PCWSTR Fil BOOL WINAPI SymFromIndex(HANDLE hProcess, ULONG64 BaseOfDll, DWORD index, PSYMBOL_INFO symbol) { struct module_pair pair; - struct symt* sym; + symref_t symref; TRACE("hProcess = %p, BaseOfDll = %I64x, index = %ld, symbol = %p\n", hProcess, BaseOfDll, index, symbol); if (!module_init_pair(&pair, hProcess, BaseOfDll)) return FALSE; - if ((sym = symt_index_to_ptr(pair.effective, index)) == NULL) return FALSE; - symt_fill_sym_info(&pair, NULL, sym, symbol); + if ((symref = symt_index_to_symref(pair.effective, index)) == 0) return FALSE; + if (!symt_is_symref_ptr(symref)) return FALSE; + symt_fill_sym_info(&pair, NULL, (struct symt*)symref, symbol); return TRUE; } diff --git a/dlls/dbghelp/type.c b/dlls/dbghelp/type.c index e47a04401c18..6d3312fa1084 100644 --- a/dlls/dbghelp/type.c +++ b/dlls/dbghelp/type.c @@ -1114,7 +1114,7 @@ BOOL symt_get_info(struct module* module, const struct symt* type, BOOL symt_get_info_from_index(struct module* module, DWORD index, IMAGEHLP_SYMBOL_TYPE_INFO req, void* pInfo) { - return symt_get_info(module, symt_index_to_ptr(module, index), req, pInfo); + return symt_get_info_from_symref(module, symt_index_to_symref(module, index), req, pInfo); } BOOL symt_get_info_from_symref(struct module* module, symref_t ref, From 07091ae6b42af4c4f5993b29b46e7b8cfed9d513 Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Wed, 11 Dec 2024 10:27:21 +0100 Subject: [PATCH 254/454] dbghelp: Now returning PDB basic types as a symref_t. Add method to handle the TI_ requests. Signed-off-by: Eric Pouech (cherry picked from commit fe41a26efb9bd66d826b691bd402ff4bac1769d0) --- dlls/dbghelp/dbghelp_private.h | 8 ++ dlls/dbghelp/dwarf.c | 1 + dlls/dbghelp/module.c | 8 ++ dlls/dbghelp/msc.c | 111 +++++++++++-------------- dlls/dbghelp/pdb.c | 147 +++++++++++++++++++++++++++++++++ dlls/dbghelp/type.c | 9 +- 6 files changed, 221 insertions(+), 63 deletions(-) diff --git a/dlls/dbghelp/dbghelp_private.h b/dlls/dbghelp/dbghelp_private.h index 8db72328133b..5ba817f62b48 100644 --- a/dlls/dbghelp/dbghelp_private.h +++ b/dlls/dbghelp/dbghelp_private.h @@ -436,6 +436,10 @@ struct module_format_vtable { /* module handling */ void (*remove)(struct module_format* modfmt); + + /* index management */ + enum method_result (*request_symref_t)(struct module_format *modfmt, symref_t ref, IMAGEHLP_SYMBOL_TYPE_INFO req, void *data); + /* stack walk */ void (*loc_compute)(const struct module_format* modfmt, const struct symt_function* func, @@ -489,6 +493,7 @@ struct module /* specific information for debug types */ struct module_format* format_info[DFI_LAST]; unsigned debug_format_bitmask; + struct module_format *ops_symref_modfmt; /* HACK for fast access to the ops table */ /* memory allocation pool */ struct pool pool; @@ -977,6 +982,8 @@ extern BOOL lineinfo_set_nameA(struct process* pcs, struct lineinfo_t* i /* type.c */ extern void symt_init_basic(struct module* module); +extern BOOL symt_get_info_raw(struct module* module, const struct symt* type, + IMAGEHLP_SYMBOL_TYPE_INFO req, void* pInfo); extern BOOL symt_get_info(struct module* module, const struct symt* type, IMAGEHLP_SYMBOL_TYPE_INFO req, void* pInfo); extern BOOL symt_get_info_from_index(struct module* module, DWORD index, @@ -1064,3 +1071,4 @@ struct pdb_reader; extern BOOL pdb_hack_get_main_info(struct module_format *modfmt, struct pdb_reader **pdb, unsigned *fpoext_stream); extern void pdb_reader_dispose(struct pdb_reader *pdb); extern struct pdb_reader *pdb_hack_reader_init(struct module *module, HANDLE file, const IMAGE_SECTION_HEADER *sections, unsigned num_sections); +extern symref_t cv_hack_ptr_to_symref(struct pdb_reader *pdb, unsigned typeno, struct symt *symt); diff --git a/dlls/dbghelp/dwarf.c b/dlls/dbghelp/dwarf.c index 068ad53fb4af..33c3c773b727 100644 --- a/dlls/dbghelp/dwarf.c +++ b/dlls/dbghelp/dwarf.c @@ -4297,6 +4297,7 @@ static BOOL dwarf2_unload_CU_module(dwarf2_parse_module_context_t* module_ctx) static const struct module_format_vtable dwarf2_module_format_vtable = { dwarf2_module_remove, + NULL, dwarf2_location_compute, }; diff --git a/dlls/dbghelp/module.c b/dlls/dbghelp/module.c index 8db470a35fe5..af503a21a298 100644 --- a/dlls/dbghelp/module.c +++ b/dlls/dbghelp/module.c @@ -402,6 +402,14 @@ BOOL module_load_debug(struct module* module) } else ret = module->process->loader->load_debug_info(module->process, module); + /* Hack for fast symdef deref... + * Note: if ever we need another backend with dedicated symref_t support, + * we could always use the 3 non-zero lower bits of symref_t to match a + * debug backend. + */ + if (module->format_info[DFI_PDB] && module->format_info[DFI_PDB]->vtable) + module->ops_symref_modfmt = module->format_info[DFI_PDB]; + if (!ret) module->module.SymType = SymNone; assert(module->module.SymType != SymDeferred); module->module.NumSyms = module->ht_symbols.num_elts; diff --git a/dlls/dbghelp/msc.c b/dlls/dbghelp/msc.c index 5163532a41d6..30160da1fafd 100644 --- a/dlls/dbghelp/msc.c +++ b/dlls/dbghelp/msc.c @@ -148,7 +148,8 @@ struct cv_defined_module BOOL allowed; unsigned int first_type_index; unsigned int last_type_index; - struct symt** defined_types; + struct symt** defined_types; /* when old reader */ + struct pdb_reader *pdb; /* new reader: hack */ }; /* FIXME: don't make it static */ #define CV_MAX_MODULES 32 @@ -661,6 +662,19 @@ static struct symt* codeview_fetch_type(struct codeview_type_parse* ctp, return symt; } +static symref_t codeview_fetch_symref(struct codeview_type_parse* ctp, unsigned typeno) +{ + struct symt *symt = codeview_fetch_type(ctp, typeno); + return cv_hack_ptr_to_symref(cv_current_module->pdb, typeno, symt); +} + +static symref_t codeview_get_symref(struct module *module, unsigned typeno, struct symt *symt) +{ + if (!symt) + symt = codeview_get_type(typeno, FALSE); + return cv_hack_ptr_to_symref(module->format_info[DFI_PDB]->u.pdb_info->pdb_files[0].pdb_reader, typeno, symt); +} + static UINT32 codeview_compute_hash(const char* ptr, unsigned len) { const char* last = ptr + len; @@ -964,13 +978,13 @@ static void codeview_add_udt_element(struct codeview_type_parse* ctp, { case LF_BITFIELD_V1: symt_add_udt_element(ctp->module, symt, name, - symt_ptr_to_symref(codeview_fetch_type(ctp, cv_type->bitfield_v1.type)), + codeview_fetch_symref(ctp,cv_type->bitfield_v1.type), value, cv_type->bitfield_v1.bitoff, cv_type->bitfield_v1.nbits); return; case LF_BITFIELD_V2: symt_add_udt_element(ctp->module, symt, name, - symt_ptr_to_symref(codeview_fetch_type(ctp, cv_type->bitfield_v2.type)), + codeview_fetch_symref(ctp, cv_type->bitfield_v2.type), value, cv_type->bitfield_v2.bitoff, cv_type->bitfield_v2.nbits); return; @@ -982,7 +996,7 @@ static void codeview_add_udt_element(struct codeview_type_parse* ctp, { DWORD64 elem_size = 0; symt_get_info(ctp->module, subtype, TI_GET_LENGTH, &elem_size); - symt_add_udt_element(ctp->module, symt, name, symt_ptr_to_symref(subtype), + symt_add_udt_element(ctp->module, symt, name, codeview_get_symref(ctp->module, type, subtype), value, 0, 0); } } @@ -1521,6 +1535,7 @@ static BOOL codeview_parse_type_table(struct codeview_type_parse* ctp) unsigned int i, curr_type; const union codeview_type* type; + cv_current_module->pdb = ctp->module->format_info[DFI_PDB]->u.pdb_info->pdb_files[0].pdb_reader; cv_current_module->first_type_index = ctp->header.first_index; cv_current_module->last_type_index = ctp->header.last_index; cv_current_module->defined_types = calloc(ctp->header.last_index - ctp->header.first_index, @@ -1816,7 +1831,7 @@ static inline void codeview_add_variable(const struct msc_debug_info* msc_dbg, { if (!is_local || in_tls) WARN("Unsupported construct\n"); symt_add_func_local(msc_dbg->module, func, DataIsStaticLocal, &loc, block, - symt_ptr_to_symref(codeview_get_type(symtype, FALSE)), name); + codeview_get_symref(msc_dbg->module, symtype, NULL), name); return; } if (!dontcheck && !in_tls) @@ -1849,7 +1864,7 @@ static inline void codeview_add_variable(const struct msc_debug_info* msc_dbg, } if (is_local ^ (compiland != NULL)) FIXME("Unsupported construct\n"); symt_new_global_variable(msc_dbg->module, compiland, name, is_local, loc, 0, - symt_ptr_to_symref(codeview_get_type(symtype, FALSE))); + codeview_get_symref(msc_dbg->module, symtype, NULL)); } } @@ -2184,14 +2199,14 @@ static struct symt_function* codeview_create_inline_site(const struct msc_debug_ case LF_FUNC_ID: inlined = symt_new_inlinesite(msc_dbg->module, top_func, container, cvt->func_id_v3.name, - symt_ptr_to_symref(codeview_get_type(cvt->func_id_v3.type, FALSE)), + codeview_get_symref(msc_dbg->module, cvt->func_id_v3.type, NULL), num_ranges); break; case LF_MFUNC_ID: /* FIXME we just declare a function, not a method */ inlined = symt_new_inlinesite(msc_dbg->module, top_func, container, cvt->mfunc_id_v3.name, - symt_ptr_to_symref(codeview_get_type(cvt->mfunc_id_v3.type, FALSE)), + codeview_get_symref(msc_dbg->module, cvt->mfunc_id_v3.type, NULL), num_ranges); break; default: @@ -2309,7 +2324,6 @@ static BOOL codeview_snarf(const struct msc_debug_info* msc_dbg, struct symt* func_signature; int i, length; struct symt_block* block = NULL; - struct symt* symt; struct symt_compiland* compiland = NULL; struct location loc; unsigned top_frame_size = -1; @@ -2414,7 +2428,7 @@ static BOOL codeview_snarf(const struct msc_debug_info* msc_dbg, terminate_string(&sym->proc_v1.p_name), codeview_get_address(msc_dbg, sym->proc_v1.segment, sym->proc_v1.offset), sym->proc_v1.proc_len, - symt_ptr_to_symref(func_signature)); + codeview_get_symref(msc_dbg->module, sym->proc_v1.proctype, func_signature)); curr_func = top_func; loc.kind = loc_absolute; loc.offset = sym->proc_v1.debug_start; @@ -2433,7 +2447,7 @@ static BOOL codeview_snarf(const struct msc_debug_info* msc_dbg, terminate_string(&sym->proc_v2.p_name), codeview_get_address(msc_dbg, sym->proc_v2.segment, sym->proc_v2.offset), sym->proc_v2.proc_len, - symt_ptr_to_symref(func_signature)); + codeview_get_symref(msc_dbg->module, sym->proc_v2.proctype, func_signature)); curr_func = top_func; loc.kind = loc_absolute; loc.offset = sym->proc_v2.debug_start; @@ -2452,7 +2466,7 @@ static BOOL codeview_snarf(const struct msc_debug_info* msc_dbg, sym->proc_v3.name, codeview_get_address(msc_dbg, sym->proc_v3.segment, sym->proc_v3.offset), sym->proc_v3.proc_len, - symt_ptr_to_symref(func_signature)); + codeview_get_symref(msc_dbg->module, sym->proc_v3.proctype, func_signature)); curr_func = top_func; loc.kind = loc_absolute; loc.offset = sym->proc_v3.debug_start; @@ -2472,7 +2486,7 @@ static BOOL codeview_snarf(const struct msc_debug_info* msc_dbg, symt_add_func_local(msc_dbg->module, curr_func, sym->stack_v1.offset > 0 ? DataIsParam : DataIsLocal, &loc, block, - symt_ptr_to_symref(codeview_get_type(sym->stack_v1.symtype, FALSE)), + codeview_get_symref(msc_dbg->module, sym->stack_v1.symtype, NULL), terminate_string(&sym->stack_v1.p_name)); break; case S_BPREL32_ST: @@ -2483,7 +2497,7 @@ static BOOL codeview_snarf(const struct msc_debug_info* msc_dbg, symt_add_func_local(msc_dbg->module, curr_func, sym->stack_v2.offset > 0 ? DataIsParam : DataIsLocal, &loc, block, - symt_ptr_to_symref(codeview_get_type(sym->stack_v2.symtype, FALSE)), + codeview_get_symref(msc_dbg->module, sym->stack_v2.symtype, NULL), terminate_string(&sym->stack_v2.p_name)); break; case S_BPREL32: @@ -2496,7 +2510,7 @@ static BOOL codeview_snarf(const struct msc_debug_info* msc_dbg, symt_add_func_local(msc_dbg->module, curr_func, sym->stack_v3.offset > 0 ? DataIsParam : DataIsLocal, &loc, block, - symt_ptr_to_symref(codeview_get_type(sym->stack_v3.symtype, FALSE)), + codeview_get_symref(msc_dbg->module, sym->stack_v3.symtype, NULL), sym->stack_v3.name); break; case S_REGREL32: @@ -2509,7 +2523,7 @@ static BOOL codeview_snarf(const struct msc_debug_info* msc_dbg, symt_add_func_local(msc_dbg->module, curr_func, sym->regrel_v3.offset >= top_frame_size ? DataIsParam : DataIsLocal, &loc, block, - symt_ptr_to_symref(codeview_get_type(sym->regrel_v3.symtype, FALSE)), + codeview_get_symref(msc_dbg->module, sym->regrel_v3.symtype, NULL), sym->regrel_v3.name); break; @@ -2519,7 +2533,7 @@ static BOOL codeview_snarf(const struct msc_debug_info* msc_dbg, loc.offset = 0; symt_add_func_local(msc_dbg->module, curr_func, DataIsLocal, &loc, block, - symt_ptr_to_symref(codeview_get_type(sym->register_v1.type, FALSE)), + codeview_get_symref(msc_dbg->module, sym->register_v1.type, NULL), terminate_string(&sym->register_v1.p_name)); break; case S_REGISTER_ST: @@ -2528,7 +2542,7 @@ static BOOL codeview_snarf(const struct msc_debug_info* msc_dbg, loc.offset = 0; symt_add_func_local(msc_dbg->module, curr_func, DataIsLocal, &loc, block, - symt_ptr_to_symref(codeview_get_type(sym->register_v2.type, FALSE)), + codeview_get_symref(msc_dbg->module, sym->register_v2.type, NULL), terminate_string(&sym->register_v2.p_name)); break; case S_REGISTER: @@ -2539,7 +2553,7 @@ static BOOL codeview_snarf(const struct msc_debug_info* msc_dbg, loc.offset = 0; symt_add_func_local(msc_dbg->module, curr_func, DataIsLocal, &loc, block, - symt_ptr_to_symref(codeview_get_type(sym->register_v3.type, FALSE)), + codeview_get_symref(msc_dbg->module, sym->register_v3.type, NULL), sym->register_v3.name); break; @@ -2630,81 +2644,65 @@ static BOOL codeview_snarf(const struct msc_debug_info* msc_dbg, { int vlen; const struct p_string* name; - struct symt* se; VARIANT v; vlen = leaf_as_variant(&v, sym->constant_v1.data); name = (const struct p_string*)&sym->constant_v1.data[vlen]; - se = codeview_get_type(sym->constant_v1.type, FALSE); TRACE("S-Constant-V1 %u %s %x\n", V_INT(&v), terminate_string(name), sym->constant_v1.type); symt_new_constant(msc_dbg->module, compiland, terminate_string(name), - symt_ptr_to_symref(se), &v); + codeview_get_symref(msc_dbg->module, sym->constant_v1.type, NULL), &v); } break; case S_CONSTANT_ST: { int vlen; const struct p_string* name; - struct symt* se; VARIANT v; vlen = leaf_as_variant(&v, sym->constant_v2.data); name = (const struct p_string*)&sym->constant_v2.data[vlen]; - se = codeview_get_type(sym->constant_v2.type, FALSE); TRACE("S-Constant-V2 %u %s %x\n", V_INT(&v), terminate_string(name), sym->constant_v2.type); symt_new_constant(msc_dbg->module, compiland, terminate_string(name), - symt_ptr_to_symref(se), &v); + codeview_get_symref(msc_dbg->module, sym->constant_v2.type, NULL), &v); } break; case S_CONSTANT: { int vlen; const char* name; - struct symt* se; VARIANT v; vlen = leaf_as_variant(&v, sym->constant_v3.data); name = (const char*)&sym->constant_v3.data[vlen]; - se = codeview_get_type(sym->constant_v3.type, FALSE); TRACE("S-Constant-V3 %u %s %x\n", V_INT(&v), debugstr_a(name), sym->constant_v3.type); /* FIXME: we should add this as a constant value */ - symt_new_constant(msc_dbg->module, compiland, name, symt_ptr_to_symref(se), &v); + symt_new_constant(msc_dbg->module, compiland, name, + codeview_get_symref(msc_dbg->module, sym->constant_v3.type, NULL), &v); } break; case S_UDT_16t: if (sym->udt_v1.type) { - if ((symt = codeview_get_type(sym->udt_v1.type, FALSE))) - symt_new_typedef(msc_dbg->module, symt_ptr_to_symref(symt), - terminate_string(&sym->udt_v1.p_name)); - else - FIXME("S-Udt %s: couldn't find type 0x%x\n", - terminate_string(&sym->udt_v1.p_name), sym->udt_v1.type); + symt_new_typedef(msc_dbg->module, codeview_get_symref(msc_dbg->module, sym->udt_v1.type, NULL), + terminate_string(&sym->udt_v1.p_name)); } break; case S_UDT_ST: if (sym->udt_v2.type) { - if ((symt = codeview_get_type(sym->udt_v2.type, FALSE))) - symt_new_typedef(msc_dbg->module, symt_ptr_to_symref(symt), + symt_new_typedef(msc_dbg->module, codeview_get_symref(msc_dbg->module, sym->udt_v2.type, NULL), terminate_string(&sym->udt_v2.p_name)); - else - FIXME("S-Udt %s: couldn't find type 0x%x\n", - terminate_string(&sym->udt_v2.p_name), sym->udt_v2.type); } break; case S_UDT: if (sym->udt_v3.type) { - if ((symt = codeview_get_type(sym->udt_v3.type, FALSE))) - symt_new_typedef(msc_dbg->module, symt_ptr_to_symref(symt), sym->udt_v3.name); - else - FIXME("S-Udt %s: couldn't find type 0x%x\n", - debugstr_a(sym->udt_v3.name), sym->udt_v3.type); + symt_new_typedef(msc_dbg->module, codeview_get_symref(msc_dbg->module, sym->udt_v3.type, NULL), + sym->udt_v3.name); } break; case S_LOCAL: @@ -2726,7 +2724,7 @@ static BOOL codeview_snarf(const struct msc_debug_info* msc_dbg, symt_add_func_local(msc_dbg->module, curr_func, sym->local_v3.varflags.is_param ? DataIsParam : DataIsLocal, &loc, block, - symt_ptr_to_symref(codeview_get_type(sym->local_v3.symtype, FALSE)), + codeview_get_symref(msc_dbg->module, sym->local_v3.symtype, NULL), sym->local_v3.name); } else @@ -2981,39 +2979,27 @@ static BOOL codeview_snarf_sym_hashtable(const struct msc_debug_info* msc_dbg, c static BOOL pdb_global_feed_types(const struct msc_debug_info* msc_dbg, const union codeview_symbol* sym) { - struct symt* symt; switch (sym->generic.id) { case S_UDT_16t: if (sym->udt_v1.type) { - if ((symt = codeview_get_type(sym->udt_v1.type, FALSE))) - symt_new_typedef(msc_dbg->module, symt_ptr_to_symref(symt), + symt_new_typedef(msc_dbg->module, codeview_get_symref(msc_dbg->module, sym->udt_v1.type, NULL), terminate_string(&sym->udt_v1.p_name)); - else - FIXME("S-Udt %s: couldn't find type 0x%x\n", - terminate_string(&sym->udt_v1.p_name), sym->udt_v1.type); } break; case S_UDT_ST: if (sym->udt_v2.type) { - if ((symt = codeview_get_type(sym->udt_v2.type, FALSE))) - symt_new_typedef(msc_dbg->module, symt_ptr_to_symref(symt), - terminate_string(&sym->udt_v2.p_name)); - else - FIXME("S-Udt %s: couldn't find type 0x%x\n", - terminate_string(&sym->udt_v2.p_name), sym->udt_v2.type); + symt_new_typedef(msc_dbg->module, codeview_get_symref(msc_dbg->module, sym->udt_v2.type, NULL), + terminate_string(&sym->udt_v2.p_name)); } break; case S_UDT: if (sym->udt_v3.type) { - if ((symt = codeview_get_type(sym->udt_v3.type, FALSE))) - symt_new_typedef(msc_dbg->module, symt_ptr_to_symref(symt), sym->udt_v3.name); - else - FIXME("S-Udt %s: couldn't find type 0x%x\n", - debugstr_a(sym->udt_v3.name), sym->udt_v3.type); + symt_new_typedef(msc_dbg->module, codeview_get_symref(msc_dbg->module, sym->udt_v3.type, NULL), + sym->udt_v3.name); } break; default: return FALSE; @@ -3998,6 +3984,7 @@ static BOOL pdb_process_internal(const struct process *pcs, static const struct module_format_vtable old_pdb_module_format_vtable = { pdb_module_remove, + NULL, pdb_location_compute, }; diff --git a/dlls/dbghelp/pdb.c b/dlls/dbghelp/pdb.c index 6276ee41ee3b..203491e3cd85 100644 --- a/dlls/dbghelp/pdb.c +++ b/dlls/dbghelp/pdb.c @@ -1149,9 +1149,156 @@ static void pdb_method_location_compute(const struct module_format *modfmt, loc->reg = loc_err_out_of_scope; } +static symref_t encode_symref(unsigned v) +{ + return (v << 2) | 1; +} + +static inline unsigned decode_symref(symref_t ref) +{ + return ref >> 2; +} + +static struct {enum BasicType bt; unsigned char size;} supported_basic[T_MAXBASICTYPE] = +{ + /* all others are defined as 0 = btNoType */ + [T_VOID] = {btVoid, 0}, + [T_CURRENCY] = {btCurrency, 8}, + [T_CHAR] = {btInt, 1}, + [T_SHORT] = {btInt, 2}, + [T_LONG] = {btLong, 4}, + [T_QUAD] = {btInt, 8}, + [T_OCT] = {btInt, 16}, + [T_UCHAR] = {btUInt, 1}, + [T_USHORT] = {btUInt, 2}, + [T_ULONG] = {btULong, 4}, + [T_UQUAD] = {btUInt, 8}, + [T_UOCT] = {btUInt, 16}, + [T_BOOL08] = {btBool, 1}, + [T_BOOL16] = {btBool, 2}, + [T_BOOL32] = {btBool, 4}, + [T_BOOL64] = {btBool, 8}, + [T_REAL16] = {btFloat, 2}, + [T_REAL32] = {btFloat, 4}, + [T_REAL64] = {btFloat, 8}, + [T_REAL80] = {btFloat, 10}, + [T_REAL128] = {btFloat, 16}, + [T_RCHAR] = {btChar, 1}, + [T_WCHAR] = {btWChar, 2}, + [T_CHAR16] = {btChar16, 2}, + [T_CHAR32] = {btChar32, 4}, + [T_CHAR8] = {btChar8, 1}, + [T_INT2] = {btInt, 2}, + [T_UINT2] = {btUInt, 2}, + [T_INT4] = {btInt, 4}, + [T_UINT4] = {btUInt, 4}, + [T_INT8] = {btInt, 8}, + [T_UINT8] = {btUInt, 8}, + [T_HRESULT] = {btUInt, 4}, + [T_CPLX32] = {btComplex, 8}, + [T_CPLX64] = {btComplex, 16}, + [T_CPLX128] = {btComplex, 32}, +}; + +static inline BOOL is_basic_supported(unsigned basic) +{ + return basic <= T_MAXBASICTYPE && supported_basic[basic].bt != btNoType; +} + +static enum method_result pdb_reader_default_request(struct pdb_reader *pdb, IMAGEHLP_SYMBOL_TYPE_INFO req, void *data) +{ + switch (req) + { + case TI_FINDCHILDREN: + return ((TI_FINDCHILDREN_PARAMS*)data)->Count == 0 ? MR_SUCCESS : MR_FAILURE; + case TI_GET_CHILDRENCOUNT: + *((DWORD*)data) = 0; + return MR_SUCCESS; + case TI_GET_LEXICALPARENT: + *((DWORD*)data) = symt_ptr_to_index(pdb->module, &pdb->module->top->symt); + return MR_SUCCESS; + default: + FIXME("Unexpected request %x\n", req); + return MR_FAILURE; + } +} + +static enum method_result pdb_reader_basic_request(struct pdb_reader *pdb, unsigned basic, IMAGEHLP_SYMBOL_TYPE_INFO req, void *data) +{ + if (!is_basic_supported(basic & T_BASICTYPE_MASK)) + { + FIXME("Unsupported basic type %x\n", basic); + return MR_FAILURE; + } + + switch (req) + { + case TI_GET_BASETYPE: + if (basic >= T_MAXBASICTYPE) return MR_FAILURE; + *((DWORD*)data) = supported_basic[basic].bt; + break; + case TI_GET_LENGTH: + switch (basic & T_MODE_MASK) + { + case 0: *((DWORD64*)data) = supported_basic[basic].size; break; + /* pointer type */ + case T_NEARPTR_BITS: *((DWORD64*)data) = pdb->module->cpu->word_size; break; + case T_NEAR32PTR_BITS: *((DWORD64*)data) = 4; break; + case T_NEAR64PTR_BITS: *((DWORD64*)data) = 8; break; + default: return MR_FAILURE; + } + break; + case TI_GET_SYMTAG: + *((DWORD*)data) = (basic < T_MAXBASICTYPE) ? SymTagBaseType : SymTagPointerType; + break; + case TI_GET_TYPE: + case TI_GET_TYPEID: + if (basic < T_MAXBASICTYPE) return MR_FAILURE; + *((DWORD*)data) = encode_symref(basic & T_BASICTYPE_MASK); + break; + case TI_FINDCHILDREN: + case TI_GET_CHILDRENCOUNT: + case TI_GET_LEXICALPARENT: + return pdb_reader_default_request(pdb, req, data); + default: return MR_FAILURE; + } + return MR_SUCCESS; +} + +static enum method_result pdb_method_request_symref_t(struct module_format *modfmt, symref_t ref, IMAGEHLP_SYMBOL_TYPE_INFO req, void *data) +{ + struct pdb_reader *pdb; + cv_typ_t cv_typeid; + + if (!pdb_hack_get_main_info(modfmt, &pdb, NULL)) return MR_FAILURE; + if (req == TI_GET_SYMINDEX) + { + *((DWORD*)data) = ref; + return MR_SUCCESS; + } + cv_typeid = decode_symref(ref); + if (cv_typeid < T_MAXPREDEFINEDTYPE) + return pdb_reader_basic_request(pdb, cv_typeid, req, data); + return MR_NOT_FOUND; +} + +symref_t cv_hack_ptr_to_symref(struct pdb_reader *pdb, cv_typ_t cv_typeid, struct symt *symt) +{ + if (pdb) + { + if (symt_check_tag(symt, SymTagBaseType)) + { + if (cv_typeid < T_MAXPREDEFINEDTYPE) + return encode_symref(cv_typeid); + } + } + return symt_ptr_to_symref(symt); +} + static struct module_format_vtable pdb_module_format_vtable = { NULL,/*pdb_module_remove*/ + pdb_method_request_symref_t, pdb_method_location_compute, pdb_method_get_line_from_address, pdb_method_advance_line_info, diff --git a/dlls/dbghelp/type.c b/dlls/dbghelp/type.c index 6d3312fa1084..8d542fe8a7af 100644 --- a/dlls/dbghelp/type.c +++ b/dlls/dbghelp/type.c @@ -1120,7 +1120,14 @@ BOOL symt_get_info_from_index(struct module* module, DWORD index, BOOL symt_get_info_from_symref(struct module* module, symref_t ref, IMAGEHLP_SYMBOL_TYPE_INFO req, void* pInfo) { - return symt_get_info(module, (struct symt*)ref, req, pInfo); + if (symt_is_symref_ptr(ref)) + return symt_get_info(module, (struct symt *)ref, req, pInfo); + if (module->ops_symref_modfmt) + { + enum method_result result = module->ops_symref_modfmt->vtable->request_symref_t(module->ops_symref_modfmt, ref, req, pInfo); + if (result == MR_SUCCESS) return TRUE; + } + return FALSE; } /****************************************************************** From 5301f98f942c1635174b3be37cbf6c03b62c45c0 Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Fri, 13 Dec 2024 09:46:51 +0100 Subject: [PATCH 255/454] dbghelp: Advertize old PDB reader types into new reader. Signed-off-by: Eric Pouech (cherry picked from commit a9287cdbb756d597d23b0c6fb406c899aebb0b30) --- dlls/dbghelp/dbghelp_private.h | 1 + dlls/dbghelp/msc.c | 14 ++- dlls/dbghelp/pdb.c | 206 +++++++++++++++++++++++++++++++-- 3 files changed, 208 insertions(+), 13 deletions(-) diff --git a/dlls/dbghelp/dbghelp_private.h b/dlls/dbghelp/dbghelp_private.h index 5ba817f62b48..094b6424e2af 100644 --- a/dlls/dbghelp/dbghelp_private.h +++ b/dlls/dbghelp/dbghelp_private.h @@ -1072,3 +1072,4 @@ extern BOOL pdb_hack_get_main_info(struct module_format *modfmt, struct pdb_read extern void pdb_reader_dispose(struct pdb_reader *pdb); extern struct pdb_reader *pdb_hack_reader_init(struct module *module, HANDLE file, const IMAGE_SECTION_HEADER *sections, unsigned num_sections); extern symref_t cv_hack_ptr_to_symref(struct pdb_reader *pdb, unsigned typeno, struct symt *symt); +extern struct symt *pdb_hack_advertize_codeview_type(struct pdb_reader *pdb, unsigned typeid, struct symt *symt, unsigned offset); diff --git a/dlls/dbghelp/msc.c b/dlls/dbghelp/msc.c index 30160da1fafd..c662fcf8cbf1 100644 --- a/dlls/dbghelp/msc.c +++ b/dlls/dbghelp/msc.c @@ -600,9 +600,10 @@ static inline const void* codeview_jump_to_type(const struct codeview_type_parse ctp->table + ctp->offset[idx - ctp->header.first_index] : NULL; } -static int codeview_add_type(unsigned int typeno, struct symt* dt) +static int codeview_add_type(unsigned int typeno, struct symt* dt, unsigned offset) { unsigned idx; + if (!cv_current_module) { FIXME("Adding %x to non allowed module\n", typeno); @@ -618,6 +619,9 @@ static int codeview_add_type(unsigned int typeno, struct symt* dt) return FALSE; } idx = typeno - cv_current_module->first_type_index; + if (cv_current_module->pdb) + pdb_hack_advertize_codeview_type(cv_current_module->pdb, typeno, dt, offset); + if (cv_current_module->defined_types[idx]) { if (cv_current_module->defined_types[idx] != dt) @@ -1442,7 +1446,8 @@ static struct symt* codeview_parse_one_type(struct codeview_type_parse* ctp, dump(type, 2 + type->generic.len); return NULL; } - return symt && codeview_add_type(curr_type, symt) ? symt : NULL; + return symt && codeview_add_type(curr_type, symt, + (const BYTE*)type - ctp->table + ctp->header.type_offset) ? symt : NULL; } static struct symt* codeview_load_forwardable_type(struct codeview_type_parse* ctp, @@ -1573,7 +1578,8 @@ static BOOL codeview_parse_type_table(struct codeview_type_parse* ctp) { /* no load it */ if ((symt = codeview_load_forwardable_type(ctp, codeview_jump_to_type(ctp, impl_type)))) - codeview_add_type(impl_type, symt); + codeview_add_type(impl_type, symt, + (const BYTE*)codeview_jump_to_type(ctp, impl_type) - ctp->table + ctp->header.type_offset); else FIXME("forward def of %x => %x, unable to load impl\n", hl->id, impl_type); } @@ -1588,7 +1594,7 @@ static BOOL codeview_parse_type_table(struct codeview_type_parse* ctp) if (!(symt = codeview_get_type(hl->id, TRUE))) symt = codeview_load_forwardable_type(ctp, type); } - codeview_add_type(hl->id, symt); + codeview_add_type(hl->id, symt, (const BYTE*)type - ctp->table + ctp->header.type_offset); } } /* pass II: + non forwardable types: load them, but since they can be indirectly diff --git a/dlls/dbghelp/pdb.c b/dlls/dbghelp/pdb.c index 203491e3cd85..b42b68ab35f2 100644 --- a/dlls/dbghelp/pdb.c +++ b/dlls/dbghelp/pdb.c @@ -65,6 +65,25 @@ typedef uint32_t pdbsize_t; /* size inside a stream (including offset from beg o struct pdb_reader; typedef enum pdb_result (*pdb_reader_fetch_t)(struct pdb_reader *pdb, void *buffer, pdboff_t offset, pdbsize_t size); +struct pdb_reader_walker +{ + unsigned stream_id; + pdbsize_t offset; + pdbsize_t last; +}; + +struct pdb_type_details +{ + pdboff_t stream_offset; /* inside TPI stream */ + struct symt *symt; /* HACK to help during migration */ +}; + +struct pdb_type_hash_entry +{ + cv_typ_t cv_typeid; + struct pdb_type_hash_entry *next; +}; + struct pdb_reader { struct module *module; @@ -84,7 +103,14 @@ struct pdb_reader } *streams; char *stream_names; - unsigned source_listed : 1; + unsigned source_listed : 1, + TPI_types_invalid; + + /* types management */ + PDB_TYPES tpi_header; + struct pdb_reader_walker tpi_types_walker; + struct pdb_type_details *tpi_typemap; /* from first to last */ + struct pdb_type_hash_entry *tpi_types_hash; /* cache PE module sections for mapping... * we should rather use pe_module information @@ -107,6 +133,9 @@ enum pdb_result R_PDB_BUFFER_TOO_SMALL, }; +static const unsigned short PDB_STREAM_TPI = 2; +static const unsigned short PDB_STREAM_DBI = 3; + static enum pdb_result pdb_reader_fetch_file(struct pdb_reader *pdb, void *buffer, pdboff_t offset, pdbsize_t size) { OVERLAPPED ov = {.Offset = offset, .OffsetHigh = offset >> 32, .hEvent = (HANDLE)(DWORD_PTR)1}; @@ -179,13 +208,6 @@ static enum pdb_result pdb_reader_internal_read_from_blocks(struct pdb_reader *p return size != initial_size ? R_PDB_SUCCESS : R_PDB_INVALID_ARGUMENT; } -struct pdb_reader_walker -{ - unsigned stream_id; - pdbsize_t offset; - pdbsize_t last; -}; - static inline enum pdb_result pdb_reader_walker_init(struct pdb_reader *pdb, unsigned stream_id, struct pdb_reader_walker *walker) { walker->stream_id = stream_id; @@ -475,7 +497,7 @@ static enum pdb_result pdb_reader_read_DBI_header(struct pdb_reader* pdb, PDB_SY enum pdb_result result; /* assuming we always have that size (even for old format) in stream */ - if ((result = pdb_reader_walker_init(pdb, 3, walker)) || + if ((result = pdb_reader_walker_init(pdb, PDB_STREAM_DBI, walker)) || (result = pdb_reader_READ(pdb, walker, dbi_header))) return result; if (dbi_header->signature != 0xffffffff) { @@ -1282,6 +1304,160 @@ static enum method_result pdb_method_request_symref_t(struct module_format *modf return MR_NOT_FOUND; } +static enum pdb_result pdb_reader_read_cv_typeid_hash(struct pdb_reader *pdb, cv_typ_t cv_typeid, unsigned *hash) +{ + enum pdb_result result; + struct pdb_reader_walker walker; + unsigned value = 0; + + if ((result = pdb_reader_walker_init(pdb, pdb->tpi_header.hash_stream, &walker))) return result; + walker.offset += pdb->tpi_header.hash_offset + (cv_typeid - pdb->tpi_header.first_index) * pdb->tpi_header.hash_value_size; + + /* little endian reading */ + if ((result = pdb_reader_internal_read_advance(pdb, &walker, &value, pdb->tpi_header.hash_value_size))) return result; + if (value >= pdb->tpi_header.hash_num_buckets) + { + WARN("hash value %x isn't within hash table boundaries (0, %x(\n", value, pdb->tpi_header.hash_num_buckets); + return R_PDB_INVALID_PDB_FILE; + } + *hash = value; + return R_PDB_SUCCESS; +} + +static enum pdb_result pdb_reader_init_TPI(struct pdb_reader *pdb) +{ + enum pdb_result result; + unsigned i; + + if (pdb->TPI_types_invalid) return R_PDB_INVALID_PDB_FILE; + if (!pdb->tpi_typemap) /* load basic types information and hash table */ + { + if ((result = pdb_reader_walker_init(pdb, PDB_STREAM_TPI, &pdb->tpi_types_walker))) goto invalid_file; + /* assuming stream is always big enough go hold a full PDB_TYPES */ + if ((result = pdb_reader_READ(pdb, &pdb->tpi_types_walker, &pdb->tpi_header))) goto invalid_file; + result = R_PDB_INVALID_PDB_FILE; + if (pdb->tpi_header.version < 19960000 || pdb->tpi_header.type_offset < sizeof(PDB_TYPES)) + { + /* not supported yet... */ + FIXME("Old PDB_TYPES header, skipping\n"); + goto invalid_file; + } + /* validate some bits */ + if (pdb->tpi_header.hash_size != (pdb->tpi_header.last_index - pdb->tpi_header.first_index) * pdb->tpi_header.hash_value_size || + pdb->tpi_header.search_size % sizeof(uint32_t[2])) + goto invalid_file; + if (pdb->tpi_header.hash_value_size > sizeof(unsigned)) + { + FIXME("Unexpected hash value size %u\n", pdb->tpi_header.hash_value_size); + goto invalid_file; + } + pdb->tpi_types_walker.offset = pdb->tpi_header.type_offset; + + if ((result = pdb_reader_alloc(pdb, (pdb->tpi_header.last_index - pdb->tpi_header.first_index) * sizeof(pdb->tpi_typemap[0]), + (void **)&pdb->tpi_typemap))) + goto invalid_file; + memset(pdb->tpi_typemap, 0, (pdb->tpi_header.last_index - pdb->tpi_header.first_index) * sizeof(pdb->tpi_typemap[0])); + if ((result = pdb_reader_alloc(pdb, pdb->tpi_header.hash_num_buckets * sizeof(struct pdb_type_hash_entry), (void **)&pdb->tpi_types_hash))) + goto invalid_file; + /* create hash table: mark empty slots */ + for (i = 0; i < pdb->tpi_header.hash_num_buckets; i++) + pdb->tpi_types_hash[i].next = &pdb->tpi_types_hash[i]; + + for (i = pdb->tpi_header.first_index; i < pdb->tpi_header.last_index; i++) + { + unsigned hash; + + if ((result = pdb_reader_read_cv_typeid_hash(pdb, i, &hash))) goto invalid_file; + if (pdb->tpi_types_hash[hash].next == &pdb->tpi_types_hash[hash]) + { + pdb->tpi_types_hash[hash].next = NULL; + } + else + { + struct pdb_type_hash_entry *hash_entry; + if ((result = pdb_reader_alloc(pdb, sizeof(*hash_entry), (void**)&hash_entry))) goto invalid_file; + *hash_entry = pdb->tpi_types_hash[hash]; + pdb->tpi_types_hash[hash].next = hash_entry; + } + pdb->tpi_types_hash[hash].cv_typeid = i; + } + if (pdb->tpi_header.type_remap_size) + { + struct pdb_reader_walker remap_walker, remap_bitset_walker; + struct {uint32_t count, capacity, count_present;} head; + uint32_t deleted_bitset_count, i, mask; + unsigned hash; + + TRACE("Loading TPI remap information\n"); + if ((result = pdb_reader_walker_init(pdb, pdb->tpi_header.hash_stream, &remap_walker))) goto invalid_file; + remap_walker.offset = pdb->tpi_header.type_remap_offset; + if ((result = pdb_reader_READ(pdb, &remap_walker, &head))) goto invalid_file; + remap_bitset_walker = remap_walker; + remap_walker.offset += head.count_present * sizeof(uint32_t); /* skip bitset */ + if ((result = pdb_reader_READ(pdb, &remap_walker, &deleted_bitset_count))) goto invalid_file; + remap_walker.offset += deleted_bitset_count * sizeof(uint32_t); /* skip deleted bitset */ + for (i = 0; i < head.capacity; ++i) + { + if ((i % (8 * sizeof(uint32_t))) == 0 && + (result = pdb_reader_READ(pdb, &remap_bitset_walker, &mask))) goto invalid_file; + if (mask & (1u << (i % (8 * sizeof(uint32_t))))) + { + /* remap[0] is an offset for a string in /string stream, followed by type_id to force */ + uint32_t target_cv_typeid; + struct pdb_type_hash_entry *hash_entry, *prev_hash_entry = NULL; + + remap_walker.offset += sizeof(uint32_t); /* skip offset in /string stream */ + if ((result = pdb_reader_READ(pdb, &remap_walker, &target_cv_typeid))) goto invalid_file; + if ((result = pdb_reader_read_cv_typeid_hash(pdb, target_cv_typeid, &hash))) goto invalid_file; + if (pdb->tpi_types_hash[hash].next == &pdb->tpi_types_hash[hash]) /* empty list */ + goto invalid_file; + for (hash_entry = &pdb->tpi_types_hash[hash]; hash_entry; hash_entry = hash_entry->next) + { + if (hash_entry->cv_typeid == target_cv_typeid) + { + struct pdb_type_hash_entry swap; + + TRACE("Remap: using cv_typeid %x instead of %x\n", hash_entry->cv_typeid, pdb->tpi_types_hash[hash].cv_typeid); + /* put hash_entry content at head in list */ + if (prev_hash_entry) + { + prev_hash_entry->next = hash_entry->next; + swap = pdb->tpi_types_hash[hash]; + pdb->tpi_types_hash[hash] = *hash_entry; + pdb->tpi_types_hash[hash].next = hash_entry; + *hash_entry = swap; + } + break; + } + } + } + } + } + } + return R_PDB_SUCCESS; +invalid_file: + WARN("Invalid TPI hash table\n"); + /* free hash table upon error!! */ + if (pdb->tpi_types_hash) + { + struct pdb_type_hash_entry *hash_entry, *hash_entry_next; + for (i = 0; i < pdb->tpi_header.hash_num_buckets; i++) + if (pdb->tpi_types_hash[i].next != &pdb->tpi_types_hash[i]) + { + for (hash_entry = pdb->tpi_types_hash[i].next; hash_entry; hash_entry = hash_entry_next) + { + hash_entry_next = hash_entry->next; + pdb_reader_free(pdb, hash_entry); + } + } + pdb_reader_free(pdb, pdb->tpi_types_hash); + pdb->tpi_types_hash = NULL; + } + pdb_reader_free(pdb, pdb->tpi_typemap); + pdb->TPI_types_invalid = 1; + return result; +} + symref_t cv_hack_ptr_to_symref(struct pdb_reader *pdb, cv_typ_t cv_typeid, struct symt *symt) { if (pdb) @@ -1295,6 +1471,18 @@ symref_t cv_hack_ptr_to_symref(struct pdb_reader *pdb, cv_typ_t cv_typeid, struc return symt_ptr_to_symref(symt); } +struct symt *pdb_hack_advertize_codeview_type(struct pdb_reader *pdb, cv_typ_t cv_typeid, struct symt *symt, unsigned offset) +{ + if (pdb_reader_init_TPI(pdb)) return symt; + if (!pdb->tpi_typemap[cv_typeid - pdb->tpi_header.first_index].symt) + { + pdb->tpi_typemap[cv_typeid - pdb->tpi_header.first_index].symt = symt; + pdb->tpi_typemap[cv_typeid - pdb->tpi_header.first_index].stream_offset = offset; + } + + return symt; +} + static struct module_format_vtable pdb_module_format_vtable = { NULL,/*pdb_module_remove*/ From f6946069236d366a764292570493a75d4eede9c6 Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Tue, 17 Dec 2024 09:47:22 +0100 Subject: [PATCH 256/454] dbghelp: Add method for search type by name. Implement this method for PDB using PDB TPI hash table. Signed-off-by: Eric Pouech (cherry picked from commit 7513406def273f4ef2da7690acec58bce500af96) --- dlls/dbghelp/dbghelp_private.h | 3 + dlls/dbghelp/dwarf.c | 1 + dlls/dbghelp/msc.c | 1 + dlls/dbghelp/pdb.c | 298 +++++++++++++++++++++++++++++++++ dlls/dbghelp/type.c | 26 +++ 5 files changed, 329 insertions(+) diff --git a/dlls/dbghelp/dbghelp_private.h b/dlls/dbghelp/dbghelp_private.h index 094b6424e2af..d2952f8b55c0 100644 --- a/dlls/dbghelp/dbghelp_private.h +++ b/dlls/dbghelp/dbghelp_private.h @@ -440,6 +440,9 @@ struct module_format_vtable /* index management */ enum method_result (*request_symref_t)(struct module_format *modfmt, symref_t ref, IMAGEHLP_SYMBOL_TYPE_INFO req, void *data); + /* types management */ + enum method_result (*find_type)(struct module_format *modfmt, const char *name, symref_t *ref); + /* stack walk */ void (*loc_compute)(const struct module_format* modfmt, const struct symt_function* func, diff --git a/dlls/dbghelp/dwarf.c b/dlls/dbghelp/dwarf.c index 33c3c773b727..9e6872205337 100644 --- a/dlls/dbghelp/dwarf.c +++ b/dlls/dbghelp/dwarf.c @@ -4298,6 +4298,7 @@ static const struct module_format_vtable dwarf2_module_format_vtable = { dwarf2_module_remove, NULL, + NULL, dwarf2_location_compute, }; diff --git a/dlls/dbghelp/msc.c b/dlls/dbghelp/msc.c index c662fcf8cbf1..e8fc090d2e27 100644 --- a/dlls/dbghelp/msc.c +++ b/dlls/dbghelp/msc.c @@ -3991,6 +3991,7 @@ static const struct module_format_vtable old_pdb_module_format_vtable = { pdb_module_remove, NULL, + NULL, pdb_location_compute, }; diff --git a/dlls/dbghelp/pdb.c b/dlls/dbghelp/pdb.c index b42b68ab35f2..df20829bcf6a 100644 --- a/dlls/dbghelp/pdb.c +++ b/dlls/dbghelp/pdb.c @@ -215,6 +215,14 @@ static inline enum pdb_result pdb_reader_walker_init(struct pdb_reader *pdb, uns return pdb_reader_get_stream_size(pdb, stream_id, &walker->last); } +static inline enum pdb_result pdb_reader_walker_narrow(struct pdb_reader_walker *walker, pdbsize_t offset, pdbsize_t len) +{ + if (offset < walker->offset || offset + len > walker->last) return R_PDB_INVALID_ARGUMENT; + walker->offset = offset; + walker->last = offset + len; + return R_PDB_SUCCESS; +} + static enum pdb_result pdb_reader_read_from_stream(struct pdb_reader *pdb, const struct pdb_reader_walker *walker, void *buffer, pdbsize_t size, pdbsize_t *num_read) { @@ -1001,6 +1009,63 @@ static enum method_result pdb_method_enumerate_sources(struct module_format *mod return MR_NOT_FOUND; } +/* read a codeview numeric leaf */ +static enum pdb_result pdb_reader_read_leaf_as_variant(struct pdb_reader *pdb, struct pdb_reader_walker *walker, VARIANT *v) +{ + enum pdb_result result; + unsigned short int type; + + if ((result = pdb_reader_READ(pdb, walker, &type))) return result; + + if (type < LF_NUMERIC) + { + V_VT(v) = VT_I2; + V_I2(v) = type; + } + else + { + int len = 0; + + switch (type) + { + case LF_CHAR: V_VT(v) = VT_I1; return pdb_reader_READ(pdb, walker, &V_I1(v)); + case LF_SHORT: V_VT(v) = VT_I2; return pdb_reader_READ(pdb, walker, &V_I2(v)); + case LF_USHORT: V_VT(v) = VT_UI2; return pdb_reader_READ(pdb, walker, &V_UI2(v)); + case LF_LONG: V_VT(v) = VT_I4; return pdb_reader_READ(pdb, walker, &V_I4(v)); + case LF_ULONG: V_VT(v) = VT_UI4; return pdb_reader_READ(pdb, walker, &V_UI4(v)); + case LF_QUADWORD: V_VT(v) = VT_I8; return pdb_reader_READ(pdb, walker, &V_I8(v)); + case LF_UQUADWORD: V_VT(v) = VT_UI8; return pdb_reader_READ(pdb, walker, &V_UI8(v)); + case LF_REAL32: V_VT(v) = VT_R4; return pdb_reader_READ(pdb, walker, &V_R4(v)); + case LF_REAL64: V_VT(v) = VT_R8; return pdb_reader_READ(pdb, walker, &V_R8(v)); + + /* types that don't simply fit inside VARIANT (would need conversion) */ + + case LF_OCTWORD: len = 16; break; + case LF_UOCTWORD: len = 16; break; + case LF_REAL48: len = 6; break; + case LF_REAL80: len = 10; break; + case LF_REAL128: len = 16; break; + + case LF_COMPLEX32: len = 8; break; + case LF_COMPLEX64: len = 16; break; + case LF_COMPLEX80: len = 20; break; + case LF_COMPLEX128:len = 32; break; + + case LF_VARSTRING: + if ((result = pdb_reader_READ(pdb, walker, &len))) return result; + break; + + /* as we don't know the length... will lead to later issues */ + default: len = 0; break; + } + FIXME("Unknown numeric leaf type %04x\n", type); + walker->offset += len; + V_VT(v) = VT_EMPTY; + } + + return R_PDB_SUCCESS; +} + #define loc_cv_local_range (loc_user + 0) /* loc.offset contain the copy of all defrange* Codeview records following S_LOCAL */ #define loc_cv_defrange (loc_user + 1) /* loc.register+offset contain the stream_id+stream_offset of S_LOCAL Codeview record to search into */ @@ -1458,6 +1523,238 @@ static enum pdb_result pdb_reader_init_TPI(struct pdb_reader *pdb) return result; } +static enum pdb_result pdb_reader_get_type_details(struct pdb_reader *pdb, cv_typ_t cv_typeid, struct pdb_type_details **type_details) +{ + enum pdb_result result; + + if ((result = pdb_reader_init_TPI(pdb))) return result; + if (cv_typeid < pdb->tpi_header.first_index || cv_typeid >= pdb->tpi_header.last_index) return R_PDB_INVALID_ARGUMENT; + *type_details = &pdb->tpi_typemap[cv_typeid - pdb->tpi_header.first_index]; + return R_PDB_SUCCESS; +} + +/* caller must ensure that num_elt are not zero */ +static enum pdb_result pdb_reader_internal_binary_search(size_t num_elt, + enum pdb_result (*cmp)(unsigned idx, int *cmp_ressult, void *user), + size_t *found, void *user) +{ + enum pdb_result result; + size_t low = 0, high = num_elt, mid; + int res; + + *found = num_elt; + while (high > low + 1) + { + mid = (high + low) / 2; + if ((result = (*cmp)(mid, &res, user))) return result; + if (!res) + { + *found = low; + return R_PDB_SUCCESS; + } + if (res < 0) + low = mid; + else + high = mid; + } + + /* call again cmd so user can be filled with reported index */ + (*cmp)(low, &res, user); + *found = low; + return R_PDB_NOT_FOUND; +} + +struct type_offset +{ + struct pdb_reader *pdb; + struct pdb_reader_walker walker; + cv_typ_t to_search; + uint32_t values[2]; +}; + +static enum pdb_result pdb_reader_type_offset_cmp(unsigned idx, int *cmp, void *user) +{ + enum pdb_result result; + struct type_offset *type_offset = user; + struct pdb_reader_walker walker = type_offset->walker; + + walker.offset += idx * sizeof(uint32_t[2]); + if ((result = pdb_reader_READ(type_offset->pdb, &walker, &type_offset->values))) return result; + *cmp = type_offset->values[0] - type_offset->to_search; + return R_PDB_SUCCESS; +} + +static enum pdb_result pdb_reader_TPI_offset_from_cv_typeid(struct pdb_reader *pdb, cv_typ_t cv_typeid, pdbsize_t *found_type_offset) +{ + enum pdb_result result; + struct pdb_reader_walker walker; + struct type_offset type_offset; + size_t found; + unsigned short int cv_type_len; + + if ((result = pdb_reader_init_TPI(pdb))) return result; + walker = pdb->tpi_types_walker; + + type_offset.pdb = pdb; + if ((result = pdb_reader_walker_init(pdb, pdb->tpi_header.hash_stream, &type_offset.walker))) return result; + type_offset.to_search = cv_typeid; + if ((result = pdb_reader_walker_narrow(&type_offset.walker, pdb->tpi_header.search_offset, pdb->tpi_header.search_size))) return result; + result = pdb_reader_internal_binary_search(pdb->tpi_header.search_size / sizeof(uint32_t[2]), + pdb_reader_type_offset_cmp, &found, &type_offset); + + if (result) + { + if (result != R_PDB_NOT_FOUND) return result; + + if (type_offset.values[0] > cv_typeid) return R_PDB_INVALID_PDB_FILE; + walker.offset += type_offset.values[1]; + + for ( ; type_offset.values[0] < cv_typeid; type_offset.values[0]++) + { + if ((result = pdb_reader_READ(pdb, &walker, &cv_type_len))) return result; + walker.offset += cv_type_len; + } + } + else walker.offset += type_offset.values[1]; + *found_type_offset = walker.offset; + + return R_PDB_SUCCESS; +} + +static enum pdb_result pdb_reader_read_partial_codeview_type(struct pdb_reader *pdb, struct pdb_reader_walker *walker, + union codeview_type *cv_type) +{ + return pdb_reader_read_partial_blob(pdb, walker, cv_type, sizeof(*cv_type)); +} + +/* deserialize the variable part of a codeview_type... + * as output, string and/or decorated are optional + */ +static enum pdb_result pdb_reader_alloc_and_read_codeview_type_variablepart(struct pdb_reader *pdb, struct pdb_reader_walker walker, + const union codeview_type *cv_type, VARIANT *variant, + char **string, char **decorated) +{ + enum pdb_result result; + size_t var_offset; + BOOL has_leaf = TRUE, has_decorated = FALSE; + unsigned leaf_len; + + switch (cv_type->generic.id) + { + case LF_CLASS_V3: + case LF_STRUCTURE_V3: + var_offset = offsetof(union codeview_type, struct_v3.data); + has_decorated = cv_type->struct_v3.property.has_decorated_name; + break; + case LF_UNION_V3: + var_offset = offsetof(union codeview_type, union_v3.data); + has_decorated = cv_type->union_v3.property.has_decorated_name; + break; + case LF_ENUM_V3: + var_offset = offsetof(union codeview_type, enumeration_v3.name); + has_decorated = cv_type->enumeration_v3.property.has_decorated_name; + has_leaf = FALSE; + break; + default: + return R_PDB_NOT_FOUND; + } + walker.offset += var_offset - sizeof(cv_type->generic.len); + leaf_len = walker.offset; + if (!has_leaf) + V_VT(variant) = VT_EMPTY; + else if ((result = pdb_reader_read_leaf_as_variant(pdb, &walker, variant))) return result; + leaf_len = walker.offset - leaf_len; + + if (string) + result = pdb_reader_alloc_and_fetch_string(pdb, &walker, string); + else if (decorated) + result = pdb_reader_skip_string(pdb, &walker); + if (result == R_PDB_SUCCESS && decorated) + { + if (!has_decorated) + *decorated = NULL; + else if ((result = pdb_reader_alloc_and_fetch_string(pdb, &walker, decorated))) return result; + } + return result; +} + +static UINT32 codeview_compute_hash(const char* ptr, unsigned len) +{ + const char* last = ptr + len; + UINT32 ret = 0; + + while (ptr + sizeof(UINT32) <= last) + { + ret ^= *(UINT32*)ptr; + ptr += sizeof(UINT32); + } + if (ptr + sizeof(UINT16) <= last) + { + ret ^= *(UINT16*)ptr; + ptr += sizeof(UINT16); + } + if (ptr + sizeof(BYTE) <= last) + ret ^= *(BYTE*)ptr; + ret |= 0x20202020; + ret ^= (ret >> 11); + return ret ^ (ret >> 16); +} + +static enum pdb_result pdb_reader_read_codeview_type_by_name(struct pdb_reader *pdb, const char *name, struct pdb_reader_walker *walker, + union codeview_type *cv_type, cv_typ_t *cv_typeid) +{ + enum pdb_result result; + VARIANT v; + UINT32 hash; /* for now we only support 1, 2 or 4 as hash size */ + struct pdb_type_hash_entry *entry; + pdbsize_t tpi_offset; + char *other_name; + + if ((result = pdb_reader_init_TPI(pdb))) return result; + *walker = pdb->tpi_types_walker; + + hash = codeview_compute_hash(name, strlen(name)) % pdb->tpi_header.hash_num_buckets; + entry = &pdb->tpi_types_hash[hash]; + if (entry->next != entry) /* not empty */ + { + for (; entry; entry = entry->next) + { + int cmp; + if ((result = pdb_reader_TPI_offset_from_cv_typeid(pdb, entry->cv_typeid, &tpi_offset))) return result; + walker->offset = tpi_offset; + if ((result = pdb_reader_read_partial_codeview_type(pdb, walker, cv_type))) return result; + result = pdb_reader_alloc_and_read_codeview_type_variablepart(pdb, *walker, cv_type, &v, &other_name, NULL); + if (result == R_PDB_NOT_FOUND) continue; + if (result) return result; + cmp = strcmp(name, other_name); + pdb_reader_free(pdb, other_name); + if (!cmp) + { + *cv_typeid = entry->cv_typeid; + return R_PDB_SUCCESS; + } + } + } + return R_PDB_NOT_FOUND; +} + +static enum method_result pdb_method_find_type(struct module_format *modfmt, const char *name, symref_t *ref) +{ + struct pdb_reader *pdb; + enum pdb_result result; + struct pdb_reader_walker walker; + cv_typ_t cv_typeid; + union codeview_type cv_type; + struct pdb_type_details *type_details; + + if (!pdb_hack_get_main_info(modfmt, &pdb, NULL)) return MR_FAILURE; + if ((result = pdb_reader_init_TPI(pdb))) return pdb_method_result(result); + if ((result = pdb_reader_read_codeview_type_by_name(pdb, name, &walker, &cv_type, &cv_typeid))) return pdb_method_result(result); + if ((result = pdb_reader_get_type_details(pdb, cv_typeid, &type_details))) return pdb_method_result(result); + *ref = cv_hack_ptr_to_symref(pdb, cv_typeid, type_details->symt); + return MR_SUCCESS; +} + symref_t cv_hack_ptr_to_symref(struct pdb_reader *pdb, cv_typ_t cv_typeid, struct symt *symt) { if (pdb) @@ -1487,6 +1784,7 @@ static struct module_format_vtable pdb_module_format_vtable = { NULL,/*pdb_module_remove*/ pdb_method_request_symref_t, + pdb_method_find_type, pdb_method_location_compute, pdb_method_get_line_from_address, pdb_method_advance_line_info, diff --git a/dlls/dbghelp/type.c b/dlls/dbghelp/type.c index 8d542fe8a7af..5e61137d0f28 100644 --- a/dlls/dbghelp/type.c +++ b/dlls/dbghelp/type.c @@ -1154,10 +1154,36 @@ BOOL WINAPI SymGetTypeFromName(HANDLE hProcess, ULONG64 BaseOfDll, struct module_pair pair; struct symt* type; DWORD64 size; + BOOL hack_only_typedef = FALSE; + struct module_format_vtable_iterator iter = {}; if (!module_init_pair(&pair, hProcess, BaseOfDll)) return FALSE; + + while ((module_format_vtable_iterator_next(pair.effective, &iter, + MODULE_FORMAT_VTABLE_INDEX(find_type)))) + { + symref_t symref; + enum method_result result = iter.modfmt->vtable->find_type(iter.modfmt, Name, &symref); + if (result == MR_SUCCESS) + { + WCHAR *name; + Symbol->Index = Symbol->TypeIndex = symt_symref_to_index(pair.effective, symref); + symt_get_info_from_symref(pair.effective, symref, TI_GET_SYMNAME, &name); + Symbol->NameLen = WideCharToMultiByte(CP_ACP, 0, name, -1, Symbol->Name, Symbol->MaxNameLen, NULL, NULL); + if (Symbol->NameLen) Symbol->NameLen--; + HeapFree(GetProcessHeap(), 0, name); + symt_get_info_from_symref(pair.effective, symref, TI_GET_LENGTH, &size); + Symbol->Size = size; + Symbol->ModBase = pair.requested->module.BaseOfImage; + symt_get_info_from_symref(pair.effective, symref, TI_GET_SYMTAG, &Symbol->Tag); + return TRUE; + } + hack_only_typedef = TRUE; + } + type = symt_find_type_by_name(pair.effective, SymTagNull, Name); if (!type) return FALSE; + if (hack_only_typedef && !symt_check_tag(type, SymTagTypedef)) return FALSE; Symbol->Index = Symbol->TypeIndex = symt_ptr_to_index(pair.effective, type); symbol_setname(Symbol, symt_get_name(type)); symt_get_info(pair.effective, type, TI_GET_LENGTH, &size); From cfcd8b9d14292bd0a6c304496014252154cc2d57 Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Tue, 17 Dec 2024 16:09:48 +0100 Subject: [PATCH 257/454] dbghelp: Add enum_types debug-info method. Implement it for PDB debug info. - only the types in TPI stream will be enumerated; - the typedefs, which are in DBI stream, will be migrated later on. Signed-off-by: Eric Pouech (cherry picked from commit 8ce6e9bb3f0d15f134b8c78c3161716880c618fd) --- dlls/dbghelp/dbghelp_private.h | 1 + dlls/dbghelp/dwarf.c | 1 + dlls/dbghelp/msc.c | 1 + dlls/dbghelp/pdb.c | 43 +++++++++++++++++++++++++++ dlls/dbghelp/type.c | 53 ++++++++++++++++++++++++++++++++++ 5 files changed, 99 insertions(+) diff --git a/dlls/dbghelp/dbghelp_private.h b/dlls/dbghelp/dbghelp_private.h index d2952f8b55c0..d8f82877180f 100644 --- a/dlls/dbghelp/dbghelp_private.h +++ b/dlls/dbghelp/dbghelp_private.h @@ -442,6 +442,7 @@ struct module_format_vtable /* types management */ enum method_result (*find_type)(struct module_format *modfmt, const char *name, symref_t *ref); + enum method_result (*enumerate_types)(struct module_format *modfmt, BOOL (*cb)(symref_t, const char *, void*), void *user); /* stack walk */ void (*loc_compute)(const struct module_format* modfmt, diff --git a/dlls/dbghelp/dwarf.c b/dlls/dbghelp/dwarf.c index 9e6872205337..cc4e4b360e78 100644 --- a/dlls/dbghelp/dwarf.c +++ b/dlls/dbghelp/dwarf.c @@ -4299,6 +4299,7 @@ static const struct module_format_vtable dwarf2_module_format_vtable = dwarf2_module_remove, NULL, NULL, + NULL, dwarf2_location_compute, }; diff --git a/dlls/dbghelp/msc.c b/dlls/dbghelp/msc.c index e8fc090d2e27..729c860342c1 100644 --- a/dlls/dbghelp/msc.c +++ b/dlls/dbghelp/msc.c @@ -3992,6 +3992,7 @@ static const struct module_format_vtable old_pdb_module_format_vtable = pdb_module_remove, NULL, NULL, + NULL, pdb_location_compute, }; diff --git a/dlls/dbghelp/pdb.c b/dlls/dbghelp/pdb.c index df20829bcf6a..a38372503b49 100644 --- a/dlls/dbghelp/pdb.c +++ b/dlls/dbghelp/pdb.c @@ -1755,6 +1755,48 @@ static enum method_result pdb_method_find_type(struct module_format *modfmt, con return MR_SUCCESS; } +static enum method_result pdb_method_enumerate_types(struct module_format *modfmt, BOOL (*cb)(symref_t, const char *, void*), void *user) +{ + struct pdb_reader *pdb; + enum pdb_result result; + unsigned i; + struct pdb_reader_walker walker; + struct pdb_type_details *type_details; + struct pdb_type_hash_entry *hash_entry; + union codeview_type cv_type; + char *name; + VARIANT v; + BOOL ret; + + if (!pdb_hack_get_main_info(modfmt, &pdb, NULL)) return MR_FAILURE; + if ((result = pdb_reader_init_TPI(pdb))) return pdb_method_result(result); + walker = pdb->tpi_types_walker; + /* Note: walking the types through the hash table may not be the most efficient */ + for (i = 0; i < pdb->tpi_header.hash_num_buckets; i++) + { + if (&pdb->tpi_types_hash[i] == pdb->tpi_types_hash[i].next) continue; /* empty */ + for (hash_entry = &pdb->tpi_types_hash[i]; hash_entry; hash_entry = hash_entry->next) + { + if ((result = pdb_reader_get_type_details(pdb, hash_entry->cv_typeid, &type_details))) + continue; + + walker.offset = type_details->stream_offset; + if ((result = pdb_reader_read_partial_codeview_type(pdb, &walker, &cv_type))) return pdb_method_result(result); + result = pdb_reader_alloc_and_read_codeview_type_variablepart(pdb, walker, &cv_type, &v, &name, NULL); + if (!result) + { + if (*name) + ret = (*cb)(cv_hack_ptr_to_symref(pdb, hash_entry->cv_typeid, type_details->symt), name, user); + else + ret = TRUE; + pdb_reader_free(pdb, name); + if (!ret) return MR_SUCCESS; + } + } + } + return MR_NOT_FOUND; /* hack: as typedef are not migrated yet, ask to continue searching */ +} + symref_t cv_hack_ptr_to_symref(struct pdb_reader *pdb, cv_typ_t cv_typeid, struct symt *symt) { if (pdb) @@ -1785,6 +1827,7 @@ static struct module_format_vtable pdb_module_format_vtable = NULL,/*pdb_module_remove*/ pdb_method_request_symref_t, pdb_method_find_type, + pdb_method_enumerate_types, pdb_method_location_compute, pdb_method_get_line_from_address, pdb_method_advance_line_info, diff --git a/dlls/dbghelp/type.c b/dlls/dbghelp/type.c index 5e61137d0f28..23e4ace6eca2 100644 --- a/dlls/dbghelp/type.c +++ b/dlls/dbghelp/type.c @@ -445,24 +445,77 @@ struct symt_typedef* symt_new_typedef(struct module* module, symref_t ref, return sym; } +struct sym_modfmt_type_enum +{ + struct module *module; + SYMBOL_INFO *sym_info; + PSYM_ENUMERATESYMBOLS_CALLBACK cb; + void *user; + const char *type_name; +}; + +static BOOL sym_modfmt_type_enum_cb(symref_t symref, const char *name, void *user) +{ + struct sym_modfmt_type_enum *info = user; + DWORD64 size; + + if (info->type_name && !SymMatchStringA(name, info->type_name, TRUE)) return TRUE; + info->sym_info->TypeIndex = symt_symref_to_index(info->module, symref); + info->sym_info->Index = 0; + symt_get_info_from_symref(info->module, symref, TI_GET_LENGTH, &size); + info->sym_info->Size = size; + info->sym_info->ModBase = info->module->module.BaseOfImage; + info->sym_info->Flags = 0; /* FIXME */ + info->sym_info->Value = 0; /* FIXME */ + info->sym_info->Address = 0; /* FIXME */ + info->sym_info->Register = 0; /* FIXME */ + info->sym_info->Scope = 0; /* FIXME */ + symt_get_info_from_symref(info->module, symref, TI_GET_SYMTAG, &info->sym_info->Tag); + symbol_setname(info->sym_info, name); + + return (*info->cb)(info->sym_info, info->sym_info->Size, info->user); +} + static BOOL sym_enum_types(struct module_pair *pair, const char *type_name, PSYM_ENUMERATESYMBOLS_CALLBACK cb, void *user) { + struct module_format_vtable_iterator iter = {}; char buffer[sizeof(SYMBOL_INFO) + 256]; SYMBOL_INFO *sym_info = (SYMBOL_INFO*)buffer; struct hash_table_iter hti; void* ptr; struct symt_ht *type; DWORD64 size; + BOOL hack_only_typedef = FALSE; sym_info->SizeOfStruct = sizeof(SYMBOL_INFO); sym_info->MaxNameLen = sizeof(buffer) - sizeof(SYMBOL_INFO); + /* FIXME could optim if type_name doesn't contain wild cards */ + while ((module_format_vtable_iterator_next(pair->effective, &iter, + MODULE_FORMAT_VTABLE_INDEX(enumerate_types)))) + { + struct sym_modfmt_type_enum info = {pair->effective, sym_info, cb, user, type_name}; + enum method_result result = iter.modfmt->vtable->enumerate_types(iter.modfmt, sym_modfmt_type_enum_cb, &info); + + switch (result) + { + case MR_FAILURE: + return FALSE; + case MR_SUCCESS: + return TRUE; + case MR_NOT_FOUND: + hack_only_typedef = TRUE; + break; + } + } + hash_table_iter_init(&pair->effective->ht_types, &hti, type_name); while ((ptr = hash_table_iter_up(&hti))) { type = CONTAINING_RECORD(ptr, struct symt_ht, hash_elt); if (type_name && !SymMatchStringA(type->hash_elt.name, type_name, TRUE)) continue; + if (hack_only_typedef && !symt_check_tag(&type->symt, SymTagTypedef)) continue; sym_info->TypeIndex = symt_ptr_to_index(pair->effective, &type->symt); sym_info->Index = 0; /* FIXME */ From 73bf840db040fd330d2df89ae064d8daf58fbc03 Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Fri, 13 Dec 2024 10:28:58 +0100 Subject: [PATCH 258/454] dbghelp: Move pointer type handle to PDB backend. Signed-off-by: Eric Pouech (cherry picked from commit 4459f63f6948baac5ade6d53f4f0c4f1d9409d1d) --- dlls/dbghelp/pdb.c | 265 ++++++++++++++++++++++++++++++++++++++------ dlls/dbghelp/type.c | 2 +- 2 files changed, 231 insertions(+), 36 deletions(-) diff --git a/dlls/dbghelp/pdb.c b/dlls/dbghelp/pdb.c index a38372503b49..4698495a615a 100644 --- a/dlls/dbghelp/pdb.c +++ b/dlls/dbghelp/pdb.c @@ -74,8 +74,9 @@ struct pdb_reader_walker struct pdb_type_details { - pdboff_t stream_offset; /* inside TPI stream */ - struct symt *symt; /* HACK to help during migration */ + pdbsize_t stream_offset; /* inside TPI stream */ + cv_typ_t resolved_cv_typeid; + struct symt *symt; }; struct pdb_type_hash_entry @@ -250,6 +251,49 @@ static enum pdb_result pdb_reader_read_from_stream(struct pdb_reader *pdb, const return R_PDB_SUCCESS; } +struct symref_code +{ + enum {symref_code_cv_typeid} kind; + cv_typ_t cv_typeid; +}; + +static inline struct symref_code *symref_code_init_from_cv_typeid(struct symref_code *code, cv_typ_t cv_typeid) +{ + code->kind = symref_code_cv_typeid; + code->cv_typeid = cv_typeid; + return code; +} + +static enum pdb_result pdb_reader_encode_symref(struct pdb_reader *pdb, const struct symref_code *code, symref_t *symref) +{ + unsigned v; + if (code->kind == symref_code_cv_typeid) + { + if (code->cv_typeid < T_MAXPREDEFINEDTYPE) + v = code->cv_typeid; + else if (code->cv_typeid >= pdb->tpi_header.first_index && code->cv_typeid < pdb->tpi_header.last_index) + v = T_MAXPREDEFINEDTYPE + (code->cv_typeid - pdb->tpi_header.first_index); + else + return R_PDB_INVALID_ARGUMENT; + *symref = (v << 2) | 1; + } + else return R_PDB_INVALID_ARGUMENT; + return R_PDB_SUCCESS; +} + +static enum pdb_result pdb_reader_decode_symref(struct pdb_reader *pdb, symref_t ref, struct symref_code *code) +{ + if ((ref & 3) != 1) return R_PDB_INVALID_ARGUMENT; + ref >>= 2; + if (ref < T_MAXPREDEFINEDTYPE) + symref_code_init_from_cv_typeid(code, ref); + else if (ref < T_MAXPREDEFINEDTYPE + pdb->tpi_header.last_index - pdb->tpi_header.first_index) + symref_code_init_from_cv_typeid(code, pdb->tpi_header.first_index + (ref - T_MAXPREDEFINEDTYPE)); + else + return R_PDB_INVALID_ARGUMENT; + return R_PDB_SUCCESS; +} + static enum pdb_result pdb_reader_init(struct pdb_reader *pdb, struct module *module, HANDLE file) { enum pdb_result result; @@ -993,7 +1037,7 @@ static enum method_result pdb_method_enumerate_sources(struct module_format *mod { struct pdb_reader *pdb; - if (!pdb_hack_get_main_info(modfmt, &pdb, NULL)) return -1; + if (!pdb_hack_get_main_info(modfmt, &pdb, NULL)) return MR_FAILURE; /* Note: in PDB, each compiland lists its used files, which are all in global string table, * but there's no global source files table AFAICT. @@ -1236,16 +1280,6 @@ static void pdb_method_location_compute(const struct module_format *modfmt, loc->reg = loc_err_out_of_scope; } -static symref_t encode_symref(unsigned v) -{ - return (v << 2) | 1; -} - -static inline unsigned decode_symref(symref_t ref) -{ - return ref >> 2; -} - static struct {enum BasicType bt; unsigned char size;} supported_basic[T_MAXBASICTYPE] = { /* all others are defined as 0 = btNoType */ @@ -1312,6 +1346,9 @@ static enum method_result pdb_reader_default_request(struct pdb_reader *pdb, IMA static enum method_result pdb_reader_basic_request(struct pdb_reader *pdb, unsigned basic, IMAGEHLP_SYMBOL_TYPE_INFO req, void *data) { + struct symref_code code; + symref_t symref; + if (!is_basic_supported(basic & T_BASICTYPE_MASK)) { FIXME("Unsupported basic type %x\n", basic); @@ -1341,34 +1378,19 @@ static enum method_result pdb_reader_basic_request(struct pdb_reader *pdb, unsig case TI_GET_TYPE: case TI_GET_TYPEID: if (basic < T_MAXBASICTYPE) return MR_FAILURE; - *((DWORD*)data) = encode_symref(basic & T_BASICTYPE_MASK); + if (pdb_reader_encode_symref(pdb, symref_code_init_from_cv_typeid(&code, basic & T_BASICTYPE_MASK), &symref)) return MR_FAILURE; + *((DWORD*)data) = symt_symref_to_index(pdb->module, symref); break; case TI_FINDCHILDREN: case TI_GET_CHILDRENCOUNT: case TI_GET_LEXICALPARENT: return pdb_reader_default_request(pdb, req, data); - default: return MR_FAILURE; + default: + return MR_FAILURE; } return MR_SUCCESS; } -static enum method_result pdb_method_request_symref_t(struct module_format *modfmt, symref_t ref, IMAGEHLP_SYMBOL_TYPE_INFO req, void *data) -{ - struct pdb_reader *pdb; - cv_typ_t cv_typeid; - - if (!pdb_hack_get_main_info(modfmt, &pdb, NULL)) return MR_FAILURE; - if (req == TI_GET_SYMINDEX) - { - *((DWORD*)data) = ref; - return MR_SUCCESS; - } - cv_typeid = decode_symref(ref); - if (cv_typeid < T_MAXPREDEFINEDTYPE) - return pdb_reader_basic_request(pdb, cv_typeid, req, data); - return MR_NOT_FOUND; -} - static enum pdb_result pdb_reader_read_cv_typeid_hash(struct pdb_reader *pdb, cv_typ_t cv_typeid, unsigned *hash) { enum pdb_result result; @@ -1755,6 +1777,78 @@ static enum method_result pdb_method_find_type(struct module_format *modfmt, con return MR_SUCCESS; } +static BOOL codeview_type_is_forward(const union codeview_type* cvtype) +{ + cv_property_t property; + + switch (cvtype->generic.id) + { + case LF_STRUCTURE_V3: + case LF_CLASS_V3: property = cvtype->struct_v3.property; break; + case LF_UNION_V3: property = cvtype->union_v3.property; break; + case LF_ENUM_V3: property = cvtype->enumeration_v3.property; break; + default: return FALSE; + } + return property.is_forward_defn; +} + +/* resolves forward declaration to the actual implementation + * resolves incremental linker remap bits + */ +static enum pdb_result pdb_reader_resolve_cv_typeid(struct pdb_reader *pdb, cv_typ_t raw_cv_typeid, cv_typ_t *cv_typeid) +{ + enum pdb_result result; + pdbsize_t tpi_offset; + union codeview_type cv_type; + struct pdb_type_details *type_details; + struct pdb_reader_walker type_walker; + + if (raw_cv_typeid < T_FIRSTDEFINABLETYPE) + { + *cv_typeid = raw_cv_typeid; + return R_PDB_SUCCESS; + } + if ((result = pdb_reader_get_type_details(pdb, raw_cv_typeid, &type_details))) return result; + if (type_details->resolved_cv_typeid) + { + *cv_typeid = type_details->resolved_cv_typeid; + return R_PDB_SUCCESS; + } + if ((result = pdb_reader_TPI_offset_from_cv_typeid(pdb, raw_cv_typeid, &tpi_offset))) return result; + + type_walker = pdb->tpi_types_walker; + type_walker.offset = tpi_offset; + if ((result = pdb_reader_read_partial_codeview_type(pdb, &type_walker, &cv_type))) return result; + + if (codeview_type_is_forward(&cv_type)) + { + VARIANT v; + char *udt_name; + struct pdb_reader_walker other_walker = pdb->tpi_types_walker; + union codeview_type other_cv_type; + cv_typ_t other_cv_typeid; + + if ((result = pdb_reader_alloc_and_read_codeview_type_variablepart(pdb, type_walker, &cv_type, &v, &udt_name, NULL))) return result; + result = pdb_reader_read_codeview_type_by_name(pdb, udt_name, &other_walker, &other_cv_type, &other_cv_typeid); + pdb_reader_free(pdb, udt_name); + switch (result) + { + case R_PDB_SUCCESS: + type_details->resolved_cv_typeid = other_cv_typeid; + break; + case R_PDB_NOT_FOUND: /* we can have a forward decl without a real implementation */ + type_details->resolved_cv_typeid = raw_cv_typeid; + break; + default: + return result; + } + } + else type_details->resolved_cv_typeid = raw_cv_typeid; + + *cv_typeid = type_details->resolved_cv_typeid; + return R_PDB_SUCCESS; +} + static enum method_result pdb_method_enumerate_types(struct module_format *modfmt, BOOL (*cb)(symref_t, const char *, void*), void *user) { struct pdb_reader *pdb; @@ -1777,8 +1871,14 @@ static enum method_result pdb_method_enumerate_types(struct module_format *modfm if (&pdb->tpi_types_hash[i] == pdb->tpi_types_hash[i].next) continue; /* empty */ for (hash_entry = &pdb->tpi_types_hash[i]; hash_entry; hash_entry = hash_entry->next) { - if ((result = pdb_reader_get_type_details(pdb, hash_entry->cv_typeid, &type_details))) - continue; + cv_typ_t cv_typeid; + + /* We don't advertize a forward declaration unless a real declaration exists. + * So advertize only TPI entries that are resolved to themselves. + */ + if ((result = pdb_reader_resolve_cv_typeid(pdb, hash_entry->cv_typeid, &cv_typeid))) continue; + if (hash_entry->cv_typeid != cv_typeid) continue; + if ((result = pdb_reader_get_type_details(pdb, cv_typeid, &type_details))) continue; walker.offset = type_details->stream_offset; if ((result = pdb_reader_read_partial_codeview_type(pdb, &walker, &cv_type))) return pdb_method_result(result); @@ -1797,14 +1897,109 @@ static enum method_result pdb_method_enumerate_types(struct module_format *modfm return MR_NOT_FOUND; /* hack: as typedef are not migrated yet, ask to continue searching */ } +static enum pdb_result pdb_reader_index_from_cv_typeid(struct pdb_reader *pdb, cv_typ_t cv_typeid, DWORD *index) +{ + enum pdb_result result; + struct symref_code code; + symref_t symref; + + if (cv_typeid > T_MAXPREDEFINEDTYPE) + { + struct pdb_type_details *type_details; + + if ((result = pdb_reader_resolve_cv_typeid(pdb, cv_typeid, &cv_typeid))) return result; + if ((result = pdb_reader_get_type_details(pdb, cv_typeid, &type_details))) return result; + symref = cv_hack_ptr_to_symref(pdb, cv_typeid, type_details->symt); + } + else if ((result = pdb_reader_encode_symref(pdb, symref_code_init_from_cv_typeid(&code, cv_typeid), &symref))) return result; + *index = symt_symref_to_index(pdb->module, symref); + return R_PDB_SUCCESS; +} + +static enum method_result pdb_reader_TPI_pointer_request(struct pdb_reader *pdb, const union codeview_type *cv_type, IMAGEHLP_SYMBOL_TYPE_INFO req, void *data) +{ + switch (req) + { + case TI_GET_SYMTAG: + *((DWORD*)data) = SymTagPointerType; + return MR_SUCCESS; + case TI_GET_LENGTH: + *((DWORD64*)data) = pdb->module->cpu->word_size; + return MR_SUCCESS; + case TI_GET_TYPE: + case TI_GET_TYPEID: + return pdb_reader_index_from_cv_typeid(pdb, cv_type->pointer_v2.datatype, + (DWORD*)data) == R_PDB_SUCCESS ? MR_SUCCESS : MR_FAILURE; + case TI_FINDCHILDREN: + case TI_GET_CHILDRENCOUNT: + case TI_GET_LEXICALPARENT: + return pdb_reader_default_request(pdb, req, data); + + default: + return MR_FAILURE; + } +} + +static enum method_result pdb_reader_TPI_request(struct pdb_reader *pdb, struct pdb_type_details *type_details, IMAGEHLP_SYMBOL_TYPE_INFO req, void *data) +{ + enum pdb_result result; + struct pdb_reader_walker walker; + union codeview_type cv_type; + enum method_result ret; + + if ((result = pdb_reader_init_TPI(pdb))) return MR_FAILURE; + walker = pdb->tpi_types_walker; + walker.offset = type_details->stream_offset; + if ((result = pdb_reader_read_partial_codeview_type(pdb, &walker, &cv_type))) return MR_FAILURE; + + switch (cv_type.generic.id) + { + case LF_POINTER_V2: + ret = pdb_reader_TPI_pointer_request(pdb, &cv_type, req, data); + break; + default: + FIXME("Unexpected id %x\n", cv_type.generic.id); + ret = MR_FAILURE; + break; + } + + return ret; +} + +static enum method_result pdb_method_request_symref_t(struct module_format *modfmt, symref_t symref, IMAGEHLP_SYMBOL_TYPE_INFO req, void *data) +{ + struct pdb_reader *pdb; + struct pdb_type_details *type_details; + struct symref_code code; + + if (!pdb_hack_get_main_info(modfmt, &pdb, NULL)) return MR_FAILURE; + if (req == TI_GET_SYMINDEX) + { + *((DWORD*)data) = symt_symref_to_index(modfmt->module, symref); + return MR_SUCCESS; + } + if (pdb_reader_decode_symref(pdb, symref, &code)) return MR_FAILURE; + if (code.cv_typeid < T_MAXPREDEFINEDTYPE) + return pdb_reader_basic_request(pdb, code.cv_typeid, req, data); + if (pdb_reader_get_type_details(pdb, code.cv_typeid, &type_details)) return MR_FAILURE; + return pdb_reader_TPI_request(pdb, type_details, req, data); +} + symref_t cv_hack_ptr_to_symref(struct pdb_reader *pdb, cv_typ_t cv_typeid, struct symt *symt) { + struct symref_code code; + symref_t symref; + if (pdb) { if (symt_check_tag(symt, SymTagBaseType)) { if (cv_typeid < T_MAXPREDEFINEDTYPE) - return encode_symref(cv_typeid); + return pdb_reader_encode_symref(pdb, symref_code_init_from_cv_typeid(&code, cv_typeid), &symref) ? 0 : symref; + } + if (symt_check_tag(symt, SymTagPointerType)) + { + return pdb_reader_encode_symref(pdb, symref_code_init_from_cv_typeid(&code, cv_typeid), &symref) ? 0 : symref; } } return symt_ptr_to_symref(symt); diff --git a/dlls/dbghelp/type.c b/dlls/dbghelp/type.c index 23e4ace6eca2..2d7285419980 100644 --- a/dlls/dbghelp/type.c +++ b/dlls/dbghelp/type.c @@ -1178,7 +1178,7 @@ BOOL symt_get_info_from_symref(struct module* module, symref_t ref, if (module->ops_symref_modfmt) { enum method_result result = module->ops_symref_modfmt->vtable->request_symref_t(module->ops_symref_modfmt, ref, req, pInfo); - if (result == MR_SUCCESS) return TRUE; + return result == MR_SUCCESS; } return FALSE; } From e8416b37142072d7be879453a69b8d2dc4e0600a Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Wed, 18 Dec 2024 13:30:35 +0100 Subject: [PATCH 259/454] dbghelp: Move array type handling to PDB backend. Signed-off-by: Eric Pouech (cherry picked from commit eb31d4c8601c0dd1e3b62cef5e88991fa72a28a3) --- dlls/dbghelp/pdb.c | 137 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 136 insertions(+), 1 deletion(-) diff --git a/dlls/dbghelp/pdb.c b/dlls/dbghelp/pdb.c index 4698495a615a..94430f75b399 100644 --- a/dlls/dbghelp/pdb.c +++ b/dlls/dbghelp/pdb.c @@ -1053,6 +1053,93 @@ static enum method_result pdb_method_enumerate_sources(struct module_format *mod return MR_NOT_FOUND; } +static unsigned codeview_get_leaf_length(unsigned short type) +{ + static unsigned char codeview_leaf_length[] = + { + [LF_CHAR - LF_NUMERIC] = 1, + [LF_SHORT - LF_NUMERIC] = 2, + [LF_USHORT - LF_NUMERIC] = 2, + [LF_LONG - LF_NUMERIC] = 4, + [LF_ULONG - LF_NUMERIC] = 4, + [LF_REAL32 - LF_NUMERIC] = 4, + [LF_REAL64 - LF_NUMERIC] = 8, + [LF_REAL80 - LF_NUMERIC] = 10, + [LF_REAL128 - LF_NUMERIC] = 16, + [LF_QUADWORD - LF_NUMERIC] = 8, + [LF_UQUADWORD - LF_NUMERIC] = 8, + [LF_REAL48 - LF_NUMERIC] = 6, + [LF_COMPLEX32 - LF_NUMERIC] = 8, + [LF_COMPLEX64 - LF_NUMERIC] = 16, + [LF_COMPLEX80 - LF_NUMERIC] = 20, + [LF_COMPLEX128 - LF_NUMERIC] = 32, + [LF_VARSTRING - LF_NUMERIC] = 0, + [LF_OCTWORD - LF_NUMERIC] = 16, + [LF_UOCTWORD - LF_NUMERIC] = 16, + [LF_DECIMAL - LF_NUMERIC] = 0, + [LF_DATE - LF_NUMERIC] = 0, + [LF_UTF8STRING - LF_NUMERIC] = 0, + [LF_REAL16 - LF_NUMERIC] = 2, + }; + + if (type < LF_NUMERIC) return 0; + type -= LF_NUMERIC; + return type < ARRAY_SIZE(codeview_leaf_length) ? codeview_leaf_length[type] : 0; +} + +/* read a codeview numeric leaf from a partially loaded codeview type */ +static enum pdb_result codeview_fetch_leaf_as_int(const union codeview_type *cv_type, const unsigned char *data, int *value) +{ + unsigned data_len = min(sizeof(*cv_type), sizeof(cv_type->generic.len) + cv_type->generic.len) - (data - (const unsigned char*)cv_type); + unsigned short int type; + + if (data_len < sizeof(unsigned short)) return R_PDB_BUFFER_TOO_SMALL; + + type = *(unsigned short int*)data; + if (type < LF_NUMERIC) + { + *value = type; + } + else + { + if (data_len < codeview_get_leaf_length(type)) + return R_PDB_BUFFER_TOO_SMALL; + + switch (type) + { + case LF_CHAR: *value = *(char*)(data + 2); break; + case LF_SHORT: *value = (int)*(short*)(data + 2); break; + case LF_USHORT: *value = *(unsigned short*)(data + 2); break; + case LF_LONG: *value = *(int*)(data + 2); break; + case LF_ULONG: *value = *(unsigned*)(data + 2); break; + + /* the leaves we can't convert into an int */ + case LF_QUADWORD: + case LF_UQUADWORD: + case LF_OCTWORD: + case LF_UOCTWORD: + case LF_REAL32: + case LF_REAL48: + case LF_REAL64: + case LF_REAL80: + case LF_REAL128: + case LF_COMPLEX32: + case LF_COMPLEX64: + case LF_COMPLEX80: + case LF_COMPLEX128: + + case LF_VARSTRING: + /* as we don't know the length... will lead to later issues */ + + default: + FIXME("Unknown numeric leaf type %04x\n", type); + return R_PDB_INVALID_ARGUMENT; + } + } + + return R_PDB_SUCCESS; +} + /* read a codeview numeric leaf */ static enum pdb_result pdb_reader_read_leaf_as_variant(struct pdb_reader *pdb, struct pdb_reader_walker *walker, VARIANT *v) { @@ -1916,6 +2003,15 @@ static enum pdb_result pdb_reader_index_from_cv_typeid(struct pdb_reader *pdb, c return R_PDB_SUCCESS; } +/* FIXME: suboptimal implementation until we have moved all types */ +static BOOL pdb_reader_request_cv_typeid(struct pdb_reader *pdb, cv_typ_t cv_typeid, IMAGEHLP_SYMBOL_TYPE_INFO req, void *data) +{ + DWORD index; + + if (pdb_reader_index_from_cv_typeid(pdb, cv_typeid, &index)) return FALSE; + return symt_get_info_from_index(pdb->module, index, req, data); +} + static enum method_result pdb_reader_TPI_pointer_request(struct pdb_reader *pdb, const union codeview_type *cv_type, IMAGEHLP_SYMBOL_TYPE_INFO req, void *data) { switch (req) @@ -1940,6 +2036,41 @@ static enum method_result pdb_reader_TPI_pointer_request(struct pdb_reader *pdb, } } +static enum method_result pdb_reader_TPI_array_request(struct pdb_reader *pdb, const union codeview_type *cv_type, IMAGEHLP_SYMBOL_TYPE_INFO req, void *data) +{ + DWORD64 element_length; + int array_size; + + switch (req) + { + case TI_GET_SYMTAG: + *((DWORD*)data) = SymTagArrayType; + return MR_SUCCESS; + case TI_GET_COUNT: + if (!pdb_reader_request_cv_typeid(pdb, cv_type->array_v3.elemtype, TI_GET_LENGTH, &element_length)) return MR_FAILURE; + if (codeview_fetch_leaf_as_int(cv_type, cv_type->array_v3.data, &array_size)) return MR_FAILURE; + if (!element_length || (array_size % element_length)) + WARN("Incorrect array %u %I64u\n", array_size, element_length); + *((DWORD*)data) = array_size / element_length; + return MR_SUCCESS; + case TI_GET_LENGTH: + if (codeview_fetch_leaf_as_int(cv_type, cv_type->array_v3.data, &array_size)) return MR_FAILURE; + *((DWORD64*)data) = (unsigned)array_size; + return MR_SUCCESS; + case TI_GET_TYPE: + case TI_GET_TYPEID: + return pdb_reader_index_from_cv_typeid(pdb, cv_type->array_v3.elemtype, (DWORD*)data) == R_PDB_SUCCESS ? MR_SUCCESS : MR_FAILURE; + case TI_GET_ARRAYINDEXTYPEID: + return pdb_reader_index_from_cv_typeid(pdb, cv_type->array_v3.idxtype, (DWORD*)data) == R_PDB_SUCCESS ? MR_SUCCESS : MR_FAILURE; + case TI_FINDCHILDREN: + case TI_GET_CHILDRENCOUNT: + case TI_GET_LEXICALPARENT: + return pdb_reader_default_request(pdb, req, data); + default: + return MR_FAILURE; + } +} + static enum method_result pdb_reader_TPI_request(struct pdb_reader *pdb, struct pdb_type_details *type_details, IMAGEHLP_SYMBOL_TYPE_INFO req, void *data) { enum pdb_result result; @@ -1957,6 +2088,9 @@ static enum method_result pdb_reader_TPI_request(struct pdb_reader *pdb, struct case LF_POINTER_V2: ret = pdb_reader_TPI_pointer_request(pdb, &cv_type, req, data); break; + case LF_ARRAY_V3: + ret = pdb_reader_TPI_array_request(pdb, &cv_type, req, data); + break; default: FIXME("Unexpected id %x\n", cv_type.generic.id); ret = MR_FAILURE; @@ -1997,7 +2131,8 @@ symref_t cv_hack_ptr_to_symref(struct pdb_reader *pdb, cv_typ_t cv_typeid, struc if (cv_typeid < T_MAXPREDEFINEDTYPE) return pdb_reader_encode_symref(pdb, symref_code_init_from_cv_typeid(&code, cv_typeid), &symref) ? 0 : symref; } - if (symt_check_tag(symt, SymTagPointerType)) + if (symt_check_tag(symt, SymTagPointerType) || + symt_check_tag(symt, SymTagArrayType)) { return pdb_reader_encode_symref(pdb, symref_code_init_from_cv_typeid(&code, cv_typeid), &symref) ? 0 : symref; } From e97c9c32530f87b0155777613c9e7ead0966f0de Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Wed, 18 Dec 2024 15:43:07 +0100 Subject: [PATCH 260/454] dbghelp: Move function signature type handling to PDB backend. Signed-off-by: Eric Pouech (cherry picked from commit aa7f88772a68fc2a90b6149d5d8619d830d38a06) --- dlls/dbghelp/pdb.c | 257 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 237 insertions(+), 20 deletions(-) diff --git a/dlls/dbghelp/pdb.c b/dlls/dbghelp/pdb.c index 94430f75b399..1c44b8bcd700 100644 --- a/dlls/dbghelp/pdb.c +++ b/dlls/dbghelp/pdb.c @@ -79,6 +79,18 @@ struct pdb_type_details struct symt *symt; }; +enum pdb_action_type +{ + action_type_cv_typ_t, /* cv_typ_t or cv_typ16_t (depending on size) */ +}; + +struct pdb_action_entry +{ + enum pdb_action_type action_type : 2; + unsigned action_length : 14; + pdbsize_t stream_offset; +}; + struct pdb_type_hash_entry { cv_typ_t cv_typeid; @@ -113,6 +125,10 @@ struct pdb_reader struct pdb_type_details *tpi_typemap; /* from first to last */ struct pdb_type_hash_entry *tpi_types_hash; + /* symbol (and types) management */ + unsigned num_action_entries; + struct pdb_action_entry *action_store; + /* cache PE module sections for mapping... * we should rather use pe_module information */ @@ -253,8 +269,9 @@ static enum pdb_result pdb_reader_read_from_stream(struct pdb_reader *pdb, const struct symref_code { - enum {symref_code_cv_typeid} kind; + enum {symref_code_cv_typeid, symref_code_action} kind; cv_typ_t cv_typeid; + unsigned action; }; static inline struct symref_code *symref_code_init_from_cv_typeid(struct symref_code *code, cv_typ_t cv_typeid) @@ -264,20 +281,33 @@ static inline struct symref_code *symref_code_init_from_cv_typeid(struct symref_ return code; } +static inline struct symref_code *symref_code_init_from_action(struct symref_code *code, unsigned action) +{ + code->kind = symref_code_action; + code->action = action; + return code; +} + static enum pdb_result pdb_reader_encode_symref(struct pdb_reader *pdb, const struct symref_code *code, symref_t *symref) { unsigned v; - if (code->kind == symref_code_cv_typeid) + switch (code->kind) { + case symref_code_cv_typeid: if (code->cv_typeid < T_MAXPREDEFINEDTYPE) v = code->cv_typeid; else if (code->cv_typeid >= pdb->tpi_header.first_index && code->cv_typeid < pdb->tpi_header.last_index) v = T_MAXPREDEFINEDTYPE + (code->cv_typeid - pdb->tpi_header.first_index); else return R_PDB_INVALID_ARGUMENT; - *symref = (v << 2) | 1; + break; + case symref_code_action: + v = T_MAXPREDEFINEDTYPE + pdb->tpi_header.last_index - pdb->tpi_header.first_index + code->action; + break; + default: + return R_PDB_INVALID_ARGUMENT; } - else return R_PDB_INVALID_ARGUMENT; + *symref = (v << 2) | 1; return R_PDB_SUCCESS; } @@ -289,11 +319,34 @@ static enum pdb_result pdb_reader_decode_symref(struct pdb_reader *pdb, symref_t symref_code_init_from_cv_typeid(code, ref); else if (ref < T_MAXPREDEFINEDTYPE + pdb->tpi_header.last_index - pdb->tpi_header.first_index) symref_code_init_from_cv_typeid(code, pdb->tpi_header.first_index + (ref - T_MAXPREDEFINEDTYPE)); + else if (ref < T_MAXPREDEFINEDTYPE + pdb->tpi_header.last_index - pdb->tpi_header.first_index + pdb->num_action_entries) + symref_code_init_from_action(code, ref - (T_MAXPREDEFINEDTYPE + pdb->tpi_header.last_index - pdb->tpi_header.first_index)); else return R_PDB_INVALID_ARGUMENT; return R_PDB_SUCCESS; } +static enum pdb_result pdb_reader_push_action(struct pdb_reader *pdb, enum pdb_action_type action, pdbsize_t stream_offset, + unsigned id_size, symref_t *symref) +{ + enum pdb_result result; + struct symref_code code; + + if (!(pdb->num_action_entries & (pdb->num_action_entries - 1))) + { + unsigned n = pdb->num_action_entries; + n = n ? n * 2 : 256; + if ((result = pdb_reader_realloc(pdb, (void**)&pdb->action_store, n * sizeof(pdb->action_store[0])))) return result; + } + if ((result = pdb_reader_encode_symref(pdb, symref_code_init_from_action(&code, pdb->num_action_entries), symref))) return result; + pdb->action_store[pdb->num_action_entries].action_type = action; + pdb->action_store[pdb->num_action_entries].action_length = id_size; + pdb->action_store[pdb->num_action_entries].stream_offset = stream_offset; + pdb->num_action_entries++; + + return R_PDB_SUCCESS; +} + static enum pdb_result pdb_reader_init(struct pdb_reader *pdb, struct module *module, HANDLE file) { enum pdb_result result; @@ -1223,19 +1276,14 @@ static enum pdb_result pdb_reader_read_partial_blob(struct pdb_reader *pdb, stru static enum pdb_result pdb_reader_alloc_and_read_full_blob(struct pdb_reader *pdb, struct pdb_reader_walker *walker, void **blob) { enum pdb_result result; - unsigned short int len; + unsigned short len; - if ((result = pdb_reader_READ(pdb, walker, &len))) return result; - if ((result = pdb_reader_alloc(pdb, len + sizeof(len), blob))) - { - walker->offset -= sizeof(len); - return result; - } + if ((result = pdb_reader_internal_read_advance(pdb, walker, &len, sizeof(len)))) return result; + if ((result = pdb_reader_alloc(pdb, len + 2, blob))) return result; if ((result = pdb_reader_internal_read_advance(pdb, walker, (char*)*blob + sizeof(len), len))) { - pdb_reader_free(pdb, *blob); - walker->offset -= sizeof(len); + pdb_reader_free(pdb, blob); return result; } *(unsigned short int*)*blob = len; @@ -1787,6 +1835,20 @@ static enum pdb_result pdb_reader_alloc_and_read_codeview_type_variablepart(stru return result; } +static enum pdb_result pdb_reader_TPI_read_partial_reftype(struct pdb_reader *pdb, cv_typ_t cv_typeid, union codeview_reftype *cv_reftype, + pdbsize_t *tpi_offset) +{ + struct pdb_reader_walker walker; + enum pdb_result result; + + if ((result = pdb_reader_init_TPI(pdb))) return result; + if ((result = pdb_reader_TPI_offset_from_cv_typeid(pdb, cv_typeid, tpi_offset))) return result; + walker = pdb->tpi_types_walker; + walker.offset = *tpi_offset; + + return pdb_reader_read_partial_blob(pdb, &walker, (void*)cv_reftype, sizeof(*cv_reftype)); +} + static UINT32 codeview_compute_hash(const char* ptr, unsigned len) { const char* last = ptr + len; @@ -2047,7 +2109,7 @@ static enum method_result pdb_reader_TPI_array_request(struct pdb_reader *pdb, c *((DWORD*)data) = SymTagArrayType; return MR_SUCCESS; case TI_GET_COUNT: - if (!pdb_reader_request_cv_typeid(pdb, cv_type->array_v3.elemtype, TI_GET_LENGTH, &element_length)) return MR_FAILURE; + if (!pdb_reader_request_cv_typeid(pdb, cv_type->array_v3.elemtype, TI_GET_LENGTH, &element_length)) return MR_FAILURE; if (codeview_fetch_leaf_as_int(cv_type, cv_type->array_v3.data, &array_size)) return MR_FAILURE; if (!element_length || (array_size % element_length)) WARN("Incorrect array %u %I64u\n", array_size, element_length); @@ -2059,7 +2121,7 @@ static enum method_result pdb_reader_TPI_array_request(struct pdb_reader *pdb, c return MR_SUCCESS; case TI_GET_TYPE: case TI_GET_TYPEID: - return pdb_reader_index_from_cv_typeid(pdb, cv_type->array_v3.elemtype, (DWORD*)data) == R_PDB_SUCCESS ? MR_SUCCESS : MR_FAILURE; + return pdb_reader_index_from_cv_typeid(pdb, cv_type->array_v3.elemtype, (DWORD*)data) == R_PDB_SUCCESS ? MR_SUCCESS : MR_FAILURE; case TI_GET_ARRAYINDEXTYPEID: return pdb_reader_index_from_cv_typeid(pdb, cv_type->array_v3.idxtype, (DWORD*)data) == R_PDB_SUCCESS ? MR_SUCCESS : MR_FAILURE; case TI_FINDCHILDREN: @@ -2071,6 +2133,134 @@ static enum method_result pdb_reader_TPI_array_request(struct pdb_reader *pdb, c } } +static enum pdb_result pdb_reader_TPI_fillin_arglist(struct pdb_reader *pdb, unsigned num_ids, unsigned id_size, pdbsize_t tpi_offset, + DWORD *indexes) +{ + enum pdb_result result; + symref_t symref; + unsigned i; + + for (i = 0; i < num_ids; i++, tpi_offset += id_size) + { + if ((result = pdb_reader_push_action(pdb, action_type_cv_typ_t, tpi_offset, id_size, &symref))) return result; + indexes[i] = symt_symref_to_index(pdb->module, symref); + } + return R_PDB_SUCCESS; +} + +static enum method_result pdb_reader_TPI_procsignature_request(struct pdb_reader *pdb, const union codeview_type *cv_type, IMAGEHLP_SYMBOL_TYPE_INFO req, void *data) +{ + enum pdb_result result; + cv_typ_t return_cv_typeid, arglist_cv_typeid; + unsigned char call_conv; + unsigned num_args; + + switch (cv_type->generic.id) + { + case LF_PROCEDURE_V2: + return_cv_typeid = cv_type->procedure_v2.rvtype; + arglist_cv_typeid = cv_type->procedure_v2.arglist; + call_conv = cv_type->procedure_v2.callconv; + num_args = cv_type->procedure_v2.params; + break; + /* FIXME: for C++, this is plain wrong, but as we don't use arg types + * nor class information, this would just do for now + */ + case LF_MFUNCTION_V2: + return_cv_typeid = cv_type->mfunction_v2.rvtype; + arglist_cv_typeid = cv_type->mfunction_v2.arglist; + call_conv = cv_type->mfunction_v2.callconv; + num_args = cv_type->mfunction_v2.params; + break; + default: + return MR_FAILURE; + } + + switch (req) + { + case TI_GET_SYMTAG: + *((DWORD*)data) = SymTagFunctionType; + return MR_SUCCESS; + case TI_FINDCHILDREN: + { + TI_FINDCHILDREN_PARAMS *p = data; + union codeview_reftype cv_reftype; + pdbsize_t tpi_arglist_offset; + + /* sigh... the codeview format doesn't have an explicit storage for the arg list item + * so we have to fake one using the 'action' field in storage + */ + if (p->Start != 0) return MR_FAILURE; + if ((result = pdb_reader_TPI_read_partial_reftype(pdb, arglist_cv_typeid, &cv_reftype, &tpi_arglist_offset))) + return MR_FAILURE; + tpi_arglist_offset += offsetof(union codeview_reftype, arglist_v2.args[0]); + if (p->Count != cv_reftype.arglist_v2.num || num_args > cv_reftype.arglist_v2.num) + return MR_FAILURE; + result = pdb_reader_TPI_fillin_arglist(pdb, cv_reftype.arglist_v2.num, sizeof(cv_typ_t), tpi_arglist_offset, p->ChildId); + if (result) return MR_FAILURE; + } + return MR_SUCCESS; + case TI_GET_CHILDRENCOUNT: + case TI_GET_COUNT: /* should evolve when considering methods */ + { + enum pdb_result result; + union codeview_reftype cv_reftype; + pdbsize_t tpi_offset; + + if ((result = pdb_reader_TPI_read_partial_reftype(pdb, arglist_cv_typeid, &cv_reftype, &tpi_offset))) + return MR_FAILURE; + *((DWORD*)data) = cv_reftype.arglist_v2.num; + } + return MR_SUCCESS; + case TI_GET_TYPE: + case TI_GET_TYPEID: + return pdb_reader_index_from_cv_typeid(pdb, return_cv_typeid, (DWORD*)data) == R_PDB_SUCCESS ? MR_SUCCESS : MR_FAILURE; + case TI_GET_CALLING_CONVENTION: + *((DWORD*)data) = call_conv; + return MR_SUCCESS; + case TI_GET_LEXICALPARENT: + return pdb_reader_default_request(pdb, req, data); + + default: + return MR_FAILURE; + } +} + +static enum method_result pdb_reader_TPI_argtype_request(struct pdb_reader *pdb, struct pdb_action_entry *entry, IMAGEHLP_SYMBOL_TYPE_INFO req, void *data) +{ + enum pdb_result result; + + switch (req) + { + case TI_GET_SYMTAG: + *(DWORD*)data = SymTagFunctionArgType; + return MR_SUCCESS; + case TI_GET_TYPE: + case TI_GET_TYPEID: + { + cv_typ_t cv_typeid; + struct pdb_reader_walker walker = pdb->tpi_types_walker; + walker.offset = entry->stream_offset; + if (entry->action_length == sizeof(cv_typ16_t)) + { + cv_typ16_t cv_typeid16; + result = pdb_reader_READ(pdb, &walker, &cv_typeid16); + cv_typeid = cv_typeid16; + } + else + result = pdb_reader_READ(pdb, &walker, &cv_typeid); + if (result || !cv_typeid) return MR_FAILURE; + return pdb_reader_index_from_cv_typeid(pdb, cv_typeid, (DWORD*)data) == R_PDB_SUCCESS ? MR_SUCCESS : MR_FAILURE; + } + case TI_FINDCHILDREN: + case TI_GET_CHILDRENCOUNT: + case TI_GET_LEXICALPARENT: + return pdb_reader_default_request(pdb, req, data); + default: + return MR_FAILURE; + } +} + static enum method_result pdb_reader_TPI_request(struct pdb_reader *pdb, struct pdb_type_details *type_details, IMAGEHLP_SYMBOL_TYPE_INFO req, void *data) { enum pdb_result result; @@ -2091,6 +2281,11 @@ static enum method_result pdb_reader_TPI_request(struct pdb_reader *pdb, struct case LF_ARRAY_V3: ret = pdb_reader_TPI_array_request(pdb, &cv_type, req, data); break; + + case LF_PROCEDURE_V2: + case LF_MFUNCTION_V2: + return pdb_reader_TPI_procsignature_request(pdb, &cv_type, req, data); + default: FIXME("Unexpected id %x\n", cv_type.generic.id); ret = MR_FAILURE; @@ -2112,11 +2307,31 @@ static enum method_result pdb_method_request_symref_t(struct module_format *modf *((DWORD*)data) = symt_symref_to_index(modfmt->module, symref); return MR_SUCCESS; } + if (pdb_reader_decode_symref(pdb, symref, &code)) return MR_FAILURE; - if (code.cv_typeid < T_MAXPREDEFINEDTYPE) - return pdb_reader_basic_request(pdb, code.cv_typeid, req, data); - if (pdb_reader_get_type_details(pdb, code.cv_typeid, &type_details)) return MR_FAILURE; - return pdb_reader_TPI_request(pdb, type_details, req, data); + switch (code.kind) + { + case symref_code_cv_typeid: + if (code.cv_typeid < T_MAXPREDEFINEDTYPE) + return pdb_reader_basic_request(pdb, code.cv_typeid, req, data); + if (pdb_reader_get_type_details(pdb, code.cv_typeid, &type_details)) return MR_FAILURE; + return pdb_reader_TPI_request(pdb, type_details, req, data); + case symref_code_action: + { + struct pdb_action_entry *entry = &pdb->action_store[code.action]; + + switch (entry->action_type) + { + case action_type_cv_typ_t: + return pdb_reader_TPI_argtype_request(pdb, entry, req, data); + default: + return MR_FAILURE; + } + } + return MR_FAILURE; + default: + return MR_FAILURE; + } } symref_t cv_hack_ptr_to_symref(struct pdb_reader *pdb, cv_typ_t cv_typeid, struct symt *symt) @@ -2132,7 +2347,9 @@ symref_t cv_hack_ptr_to_symref(struct pdb_reader *pdb, cv_typ_t cv_typeid, struc return pdb_reader_encode_symref(pdb, symref_code_init_from_cv_typeid(&code, cv_typeid), &symref) ? 0 : symref; } if (symt_check_tag(symt, SymTagPointerType) || - symt_check_tag(symt, SymTagArrayType)) + symt_check_tag(symt, SymTagArrayType) || + symt_check_tag(symt, SymTagFunctionType) || + symt_check_tag(symt, SymTagFunctionArgType)) { return pdb_reader_encode_symref(pdb, symref_code_init_from_cv_typeid(&code, cv_typeid), &symref) ? 0 : symref; } From e457d7b77f813639445cab46196c4f085ec9e36b Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Thu, 19 Dec 2024 12:08:08 +0100 Subject: [PATCH 261/454] dbghelp: Move enumeration type to PDB backend. Signed-off-by: Eric Pouech (cherry picked from commit 6b8c3977421b71fbe7ab64b7c123e286847e3a98) --- dlls/dbghelp/pdb.c | 419 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 354 insertions(+), 65 deletions(-) diff --git a/dlls/dbghelp/pdb.c b/dlls/dbghelp/pdb.c index 1c44b8bcd700..15ae1454ef2c 100644 --- a/dlls/dbghelp/pdb.c +++ b/dlls/dbghelp/pdb.c @@ -82,6 +82,7 @@ struct pdb_type_details enum pdb_action_type { action_type_cv_typ_t, /* cv_typ_t or cv_typ16_t (depending on size) */ + action_type_field, /* union codeview_fieldtype */ }; struct pdb_action_entry @@ -89,6 +90,7 @@ struct pdb_action_entry enum pdb_action_type action_type : 2; unsigned action_length : 14; pdbsize_t stream_offset; + symref_t container_symref; }; struct pdb_type_hash_entry @@ -150,6 +152,13 @@ enum pdb_result R_PDB_BUFFER_TOO_SMALL, }; +#define PDB_REPORT_UNEXPECTED(k,i) pdb_reader_report_unexpected(k, __FUNCTION__, (i)) +static enum pdb_result pdb_reader_report_unexpected(const char *kind, const char *function, unsigned id) +{ + WARN("%s: unexpected %s %x\n", function, kind, id); + return R_PDB_INVALID_PDB_FILE; +} + static const unsigned short PDB_STREAM_TPI = 2; static const unsigned short PDB_STREAM_DBI = 3; @@ -327,7 +336,7 @@ static enum pdb_result pdb_reader_decode_symref(struct pdb_reader *pdb, symref_t } static enum pdb_result pdb_reader_push_action(struct pdb_reader *pdb, enum pdb_action_type action, pdbsize_t stream_offset, - unsigned id_size, symref_t *symref) + unsigned id_size, symref_t container_symref, symref_t *symref) { enum pdb_result result; struct symref_code code; @@ -342,6 +351,7 @@ static enum pdb_result pdb_reader_push_action(struct pdb_reader *pdb, enum pdb_a pdb->action_store[pdb->num_action_entries].action_type = action; pdb->action_store[pdb->num_action_entries].action_length = id_size; pdb->action_store[pdb->num_action_entries].stream_offset = stream_offset; + pdb->action_store[pdb->num_action_entries].container_symref = container_symref; pdb->num_action_entries++; return R_PDB_SUCCESS; @@ -1140,6 +1150,130 @@ static unsigned codeview_get_leaf_length(unsigned short type) return type < ARRAY_SIZE(codeview_leaf_length) ? codeview_leaf_length[type] : 0; } +static int codeview_leaf_as_variant(const unsigned char *leaf, VARIANT *v) +{ + unsigned short int type = *(const unsigned short *)leaf; + + if (type < LF_NUMERIC) + { + V_VT(v) = VT_I2; + V_I2(v) = type; + return sizeof(unsigned short); + } + leaf += sizeof(unsigned short); + switch (type) + { + case LF_CHAR: + V_VT(v) = VT_I1; + V_I1(v) = *(const char*)leaf; + break; + case LF_SHORT: + V_VT(v) = VT_I2; + V_I2(v) = *(const short*)leaf; + break; + case LF_USHORT: + V_VT(v) = VT_UI2; + V_UI2(v) = *(const unsigned short*)leaf; + break; + case LF_LONG: + V_VT(v) = VT_I4; + V_I4(v) = *(const int*)leaf; + break; + case LF_ULONG: + V_VT(v) = VT_UI4; + V_UI4(v) = *(const unsigned int*)leaf; + break; + case LF_QUADWORD: + V_VT(v) = VT_I8; + V_I8(v) = *(const long long int*)leaf; + break; + case LF_UQUADWORD: + V_VT(v) = VT_UI8; + V_UI8(v) = *(const long long unsigned int*)leaf; + break; + case LF_REAL32: + V_VT(v) = VT_R4; + V_R4(v) = *(const float*)leaf; + break; + case LF_REAL64: + V_VT(v) = VT_R8; + V_R8(v) = *(const double*)leaf; + break; + + case LF_VARSTRING: + PDB_REPORT_UNEXPECTED("numeric leaf", type); + V_VT(v) = VT_EMPTY; /* FIXME */ + return 2 + *leaf; + + case LF_REAL48: + case LF_REAL80: + case LF_REAL128: + case LF_COMPLEX32: + case LF_COMPLEX64: + case LF_COMPLEX80: + case LF_COMPLEX128: + /* we can't convert them into an int */ + default: + PDB_REPORT_UNEXPECTED("numeric leaf", type); + V_VT(v) = VT_EMPTY; /* FIXME */ + return 0; + } + return sizeof(unsigned short) + codeview_get_leaf_length(type); +} + +/* read a codeview numeric leaf */ +static enum pdb_result pdb_reader_read_leaf_as_variant(struct pdb_reader *pdb, struct pdb_reader_walker *walker, VARIANT *v) +{ + enum pdb_result result; + unsigned short int type; + + if ((result = pdb_reader_READ(pdb, walker, &type))) return result; + + if (type < LF_NUMERIC) + { + V_VT(v) = VT_I2; + V_I2(v) = type; + } + else + { + switch (type) + { + case LF_CHAR: V_VT(v) = VT_I1; return pdb_reader_READ(pdb, walker, &V_I1(v)); + case LF_SHORT: V_VT(v) = VT_I2; return pdb_reader_READ(pdb, walker, &V_I2(v)); + case LF_USHORT: V_VT(v) = VT_UI2; return pdb_reader_READ(pdb, walker, &V_UI2(v)); + case LF_LONG: V_VT(v) = VT_I4; return pdb_reader_READ(pdb, walker, &V_I4(v)); + case LF_ULONG: V_VT(v) = VT_UI4; return pdb_reader_READ(pdb, walker, &V_UI4(v)); + case LF_QUADWORD: V_VT(v) = VT_I8; return pdb_reader_READ(pdb, walker, &V_I8(v)); + case LF_UQUADWORD: V_VT(v) = VT_UI8; return pdb_reader_READ(pdb, walker, &V_UI8(v)); + case LF_REAL32: V_VT(v) = VT_R4; return pdb_reader_READ(pdb, walker, &V_R4(v)); + case LF_REAL64: V_VT(v) = VT_R8; return pdb_reader_READ(pdb, walker, &V_R8(v)); + + /* types that don't simply fit inside VARIANT (would need conversion) */ + + case LF_OCTWORD: + case LF_UOCTWORD: + case LF_REAL48: + case LF_REAL80: + case LF_REAL128: + case LF_COMPLEX32: + case LF_COMPLEX64: + case LF_COMPLEX80: + case LF_COMPLEX128: + + /* case LF_VARSTRING: */ + + /* as we don't know the length... will lead to later issues */ + default: + break; + } + PDB_REPORT_UNEXPECTED("numeric leaf", type); + walker->offset += codeview_get_leaf_length(type); + V_VT(v) = VT_EMPTY; + } + + return R_PDB_SUCCESS; +} + /* read a codeview numeric leaf from a partially loaded codeview type */ static enum pdb_result codeview_fetch_leaf_as_int(const union codeview_type *cv_type, const unsigned char *data, int *value) { @@ -1185,7 +1319,7 @@ static enum pdb_result codeview_fetch_leaf_as_int(const union codeview_type *cv_ /* as we don't know the length... will lead to later issues */ default: - FIXME("Unknown numeric leaf type %04x\n", type); + PDB_REPORT_UNEXPECTED("numeric leaf", type); return R_PDB_INVALID_ARGUMENT; } } @@ -1193,61 +1327,15 @@ static enum pdb_result codeview_fetch_leaf_as_int(const union codeview_type *cv_ return R_PDB_SUCCESS; } -/* read a codeview numeric leaf */ -static enum pdb_result pdb_reader_read_leaf_as_variant(struct pdb_reader *pdb, struct pdb_reader_walker *walker, VARIANT *v) +static WCHAR *heap_allocate_symname(const char *string) { - enum pdb_result result; - unsigned short int type; - - if ((result = pdb_reader_READ(pdb, walker, &type))) return result; - - if (type < LF_NUMERIC) - { - V_VT(v) = VT_I2; - V_I2(v) = type; - } - else - { - int len = 0; - - switch (type) - { - case LF_CHAR: V_VT(v) = VT_I1; return pdb_reader_READ(pdb, walker, &V_I1(v)); - case LF_SHORT: V_VT(v) = VT_I2; return pdb_reader_READ(pdb, walker, &V_I2(v)); - case LF_USHORT: V_VT(v) = VT_UI2; return pdb_reader_READ(pdb, walker, &V_UI2(v)); - case LF_LONG: V_VT(v) = VT_I4; return pdb_reader_READ(pdb, walker, &V_I4(v)); - case LF_ULONG: V_VT(v) = VT_UI4; return pdb_reader_READ(pdb, walker, &V_UI4(v)); - case LF_QUADWORD: V_VT(v) = VT_I8; return pdb_reader_READ(pdb, walker, &V_I8(v)); - case LF_UQUADWORD: V_VT(v) = VT_UI8; return pdb_reader_READ(pdb, walker, &V_UI8(v)); - case LF_REAL32: V_VT(v) = VT_R4; return pdb_reader_READ(pdb, walker, &V_R4(v)); - case LF_REAL64: V_VT(v) = VT_R8; return pdb_reader_READ(pdb, walker, &V_R8(v)); + unsigned sz = MultiByteToWideChar(CP_ACP, 0, string, -1, NULL, 0); + WCHAR *stringW; - /* types that don't simply fit inside VARIANT (would need conversion) */ - - case LF_OCTWORD: len = 16; break; - case LF_UOCTWORD: len = 16; break; - case LF_REAL48: len = 6; break; - case LF_REAL80: len = 10; break; - case LF_REAL128: len = 16; break; - - case LF_COMPLEX32: len = 8; break; - case LF_COMPLEX64: len = 16; break; - case LF_COMPLEX80: len = 20; break; - case LF_COMPLEX128:len = 32; break; - - case LF_VARSTRING: - if ((result = pdb_reader_READ(pdb, walker, &len))) return result; - break; - - /* as we don't know the length... will lead to later issues */ - default: len = 0; break; - } - FIXME("Unknown numeric leaf type %04x\n", type); - walker->offset += len; - V_VT(v) = VT_EMPTY; - } - - return R_PDB_SUCCESS; + stringW = HeapAlloc(GetProcessHeap(), 0, sz * sizeof(WCHAR)); + if (stringW) + MultiByteToWideChar(CP_ACP, 0, string, -1, stringW, sz); + return stringW; } #define loc_cv_local_range (loc_user + 0) /* loc.offset contain the copy of all defrange* Codeview records following S_LOCAL */ @@ -1486,7 +1574,7 @@ static enum method_result pdb_reader_basic_request(struct pdb_reader *pdb, unsig if (!is_basic_supported(basic & T_BASICTYPE_MASK)) { - FIXME("Unsupported basic type %x\n", basic); + PDB_REPORT_UNEXPECTED("basic type", basic); return MR_FAILURE; } @@ -1849,6 +1937,20 @@ static enum pdb_result pdb_reader_TPI_read_partial_reftype(struct pdb_reader *pd return pdb_reader_read_partial_blob(pdb, &walker, (void*)cv_reftype, sizeof(*cv_reftype)); } +static enum pdb_result pdb_reader_TPI_alloc_and_read_full_reftype(struct pdb_reader *pdb, cv_typ_t cv_typeid, union codeview_reftype **cv_reftype, + pdbsize_t *tpi_offset) +{ + struct pdb_reader_walker walker; + enum pdb_result result; + + if ((result = pdb_reader_init_TPI(pdb))) return result; + if ((result = pdb_reader_TPI_offset_from_cv_typeid(pdb, cv_typeid, tpi_offset))) return result; + walker = pdb->tpi_types_walker; + walker.offset = *tpi_offset; + + return pdb_reader_alloc_and_read_full_blob(pdb, &walker, (void**)cv_reftype); +} + static UINT32 codeview_compute_hash(const char* ptr, unsigned len) { const char* last = ptr + len; @@ -2142,7 +2244,7 @@ static enum pdb_result pdb_reader_TPI_fillin_arglist(struct pdb_reader *pdb, uns for (i = 0; i < num_ids; i++, tpi_offset += id_size) { - if ((result = pdb_reader_push_action(pdb, action_type_cv_typ_t, tpi_offset, id_size, &symref))) return result; + if ((result = pdb_reader_push_action(pdb, action_type_cv_typ_t, tpi_offset, id_size, 0, &symref))) return result; indexes[i] = symt_symref_to_index(pdb->module, symref); } return R_PDB_SUCCESS; @@ -2226,7 +2328,115 @@ static enum method_result pdb_reader_TPI_procsignature_request(struct pdb_reader } } +static enum pdb_result pdb_reader_TPI_fillin_enumlist(struct pdb_reader *pdb, symref_t container_symref, + cv_typ_t enumlist_cv_typeid, unsigned count, DWORD *index) + { + enum pdb_result result; + union codeview_reftype *cv_reftype; + pdbsize_t tpi_offset; + const unsigned char *start, *ptr, *last; + const union codeview_fieldtype *cv_field; + unsigned length; + VARIANT v; + symref_t symref; + + if ((result = pdb_reader_TPI_alloc_and_read_full_reftype(pdb, enumlist_cv_typeid, &cv_reftype, &tpi_offset))) return result; + if (cv_reftype->generic.id != LF_FIELDLIST_V2) return PDB_REPORT_UNEXPECTED("enum list", cv_reftype->generic.id); + start = ptr = (const unsigned char *)cv_reftype->fieldlist.list; + last = (const unsigned char *)cv_reftype + sizeof(cv_reftype->generic.id) + cv_reftype->generic.len; + + tpi_offset += offsetof(union codeview_reftype, fieldlist.list); + + while (ptr < last) + { + if (*ptr >= 0xf0) /* Padding */ + { + ptr += *ptr & 0x0f; + continue; + } + cv_field = (const union codeview_fieldtype *)ptr; + switch (cv_field->generic.id) + { + case LF_ENUMERATE_V3: + if (!count) return R_PDB_INVALID_ARGUMENT; + length = offsetof(union codeview_fieldtype, enumerate_v3.data); + length += codeview_leaf_as_variant(ptr + length, &v); + length += strlen((const char *)ptr + length) + 1; + if ((result = pdb_reader_push_action(pdb, action_type_field, tpi_offset + (ptr - start), length, container_symref, &symref))) + { + pdb_reader_free(pdb, cv_reftype); + return result; + } + *index = symt_symref_to_index(pdb->module, symref); + index++; + count--; + ptr += length; + break; + + case LF_INDEX_V2: + if ((result = pdb_reader_TPI_fillin_enumlist(pdb, container_symref, cv_field->index_v2.ref, count, index))) return result; + ptr += sizeof(cv_field->index_v2); + break; + + default: + pdb_reader_free(pdb, cv_reftype); + return PDB_REPORT_UNEXPECTED("enum field list", cv_field->generic.id); + } + } + pdb_reader_free(pdb, cv_reftype); + return R_PDB_SUCCESS; +} + +static enum method_result pdb_reader_TPI_enum_request(struct pdb_reader *pdb, symref_t symref, const union codeview_type *cv_type, + const struct pdb_reader_walker *walker, IMAGEHLP_SYMBOL_TYPE_INFO req, void *data) +{ + switch (req) + { + case TI_FINDCHILDREN: + { + TI_FINDCHILDREN_PARAMS *p = data; + if (p->Start != 0 || p->Count != cv_type->enumeration_v3.count) return MR_FAILURE; + if (pdb_reader_TPI_fillin_enumlist(pdb, symref, cv_type->enumeration_v3.fieldlist, cv_type->enumeration_v3.count, p->ChildId)) return MR_FAILURE; + } + return MR_SUCCESS; + case TI_GET_CHILDRENCOUNT: + *(DWORD*)data = cv_type->enumeration_v3.count; + return MR_SUCCESS; + case TI_GET_SYMNAME: + { + VARIANT v; + char *string; + + if (pdb_reader_alloc_and_read_codeview_type_variablepart(pdb, *walker, cv_type, &v, &string, NULL)) + return MR_FAILURE; + *((WCHAR**)data) = heap_allocate_symname(string); + pdb_reader_free(pdb, string); + } + return *((WCHAR**)data) != NULL ? MR_SUCCESS : MR_FAILURE; + case TI_GET_BASETYPE: + case TI_GET_LENGTH: /* forward to base type */ + if (cv_type->enumeration_v3.type >= T_MAXPREDEFINEDTYPE) return MR_FAILURE; + return pdb_reader_basic_request(pdb, cv_type->enumeration_v3.type, req, data); + case TI_GET_NESTED: + *(DWORD*)data = 0; + return MR_SUCCESS; + case TI_GET_SYMTAG: + *((DWORD*)data) = SymTagEnum; + return MR_SUCCESS; + case TI_GET_TYPE: + case TI_GET_TYPEID: + return pdb_reader_index_from_cv_typeid(pdb, cv_type->enumeration_v3.type, (DWORD*)data) == R_PDB_SUCCESS ? MR_SUCCESS : MR_FAILURE; + case TI_GET_LEXICALPARENT: + return pdb_reader_default_request(pdb, req, data); + default: + return MR_FAILURE; + } +} + +static enum method_result pdb_reader_request_symref_t(struct pdb_reader *pdb, symref_t symref, IMAGEHLP_SYMBOL_TYPE_INFO req, void *data); + static enum method_result pdb_reader_TPI_argtype_request(struct pdb_reader *pdb, struct pdb_action_entry *entry, IMAGEHLP_SYMBOL_TYPE_INFO req, void *data) + { enum pdb_result result; @@ -2261,7 +2471,73 @@ static enum method_result pdb_reader_TPI_argtype_request(struct pdb_reader *pdb, } } -static enum method_result pdb_reader_TPI_request(struct pdb_reader *pdb, struct pdb_type_details *type_details, IMAGEHLP_SYMBOL_TYPE_INFO req, void *data) +static enum method_result pdb_reader_TPI_enumerate_request(struct pdb_reader *pdb, struct pdb_action_entry *entry, const union codeview_fieldtype *cv_field, + IMAGEHLP_SYMBOL_TYPE_INFO req, void *data) +{ + pdbsize_t var; + VARIANT v; + + switch (req) + { + case TI_GET_SYMTAG: + *((DWORD*)data) = SymTagData; + return MR_SUCCESS; + case TI_GET_SYMNAME: + var = offsetof(union codeview_fieldtype, enumerate_v3.data); + var += codeview_leaf_as_variant((const unsigned char *)cv_field + var, &v); + *((WCHAR **)data) = heap_allocate_symname((const char *)cv_field + var); + return *((WCHAR **)data) != NULL ? MR_SUCCESS : MR_FAILURE; + case TI_GET_DATAKIND: + *((DWORD*)data) = DataIsConstant; + return MR_SUCCESS; + case TI_GET_LEXICALPARENT: + *((DWORD*)data) = symt_symref_to_index(pdb->module, entry->container_symref); + return MR_SUCCESS; + case TI_GET_LENGTH: + case TI_GET_TYPE: + case TI_GET_TYPEID: + /* forward to container type */ + return pdb_reader_request_symref_t(pdb, entry->container_symref, req, data); + case TI_GET_VALUE: + var = offsetof(union codeview_fieldtype, enumerate_v3.data); + return codeview_leaf_as_variant((const unsigned char *)cv_field + var, (VARIANT*)data) != 0 ? MR_SUCCESS : MR_FAILURE; + case TI_FINDCHILDREN: + case TI_GET_CHILDRENCOUNT: + return pdb_reader_default_request(pdb, req, data); + default: + return MR_FAILURE; + } +} + +static enum method_result pdb_reader_TPI_field_request(struct pdb_reader *pdb, struct pdb_action_entry *entry, IMAGEHLP_SYMBOL_TYPE_INFO req, void *data) +{ + enum pdb_result result; + struct pdb_reader_walker walker; + union codeview_fieldtype *cv_field; + pdbsize_t num_read; + enum method_result ret = MR_FAILURE; + + walker = pdb->tpi_types_walker; + walker.offset = entry->stream_offset; + if ((result = pdb_reader_alloc(pdb, entry->action_length, (void**)&cv_field))) return MR_FAILURE; + if (!pdb_reader_read_from_stream(pdb, &walker, cv_field, entry->action_length, &num_read) && + num_read == entry->action_length) + { + switch (cv_field->generic.id) + { + case LF_ENUMERATE_V3: + ret = pdb_reader_TPI_enumerate_request(pdb, entry, cv_field, req, data); + break; + default: + PDB_REPORT_UNEXPECTED("field list", cv_field->generic.id); + break; + } + } + pdb_reader_free(pdb, cv_field); + return ret; +} + +static enum method_result pdb_reader_TPI_request(struct pdb_reader *pdb, symref_t symref, struct pdb_type_details *type_details, IMAGEHLP_SYMBOL_TYPE_INFO req, void *data) { enum pdb_result result; struct pdb_reader_walker walker; @@ -2286,8 +2562,11 @@ static enum method_result pdb_reader_TPI_request(struct pdb_reader *pdb, struct case LF_MFUNCTION_V2: return pdb_reader_TPI_procsignature_request(pdb, &cv_type, req, data); + case LF_ENUM_V3: + return pdb_reader_TPI_enum_request(pdb, symref, &cv_type, &walker, req, data); + default: - FIXME("Unexpected id %x\n", cv_type.generic.id); + PDB_REPORT_UNEXPECTED("codeview type id", cv_type.generic.id); ret = MR_FAILURE; break; } @@ -2295,16 +2574,14 @@ static enum method_result pdb_reader_TPI_request(struct pdb_reader *pdb, struct return ret; } -static enum method_result pdb_method_request_symref_t(struct module_format *modfmt, symref_t symref, IMAGEHLP_SYMBOL_TYPE_INFO req, void *data) +static enum method_result pdb_reader_request_symref_t(struct pdb_reader *pdb, symref_t symref, IMAGEHLP_SYMBOL_TYPE_INFO req, void *data) { - struct pdb_reader *pdb; struct pdb_type_details *type_details; struct symref_code code; - if (!pdb_hack_get_main_info(modfmt, &pdb, NULL)) return MR_FAILURE; if (req == TI_GET_SYMINDEX) { - *((DWORD*)data) = symt_symref_to_index(modfmt->module, symref); + *((DWORD*)data) = symt_symref_to_index(pdb->module, symref); return MR_SUCCESS; } @@ -2315,7 +2592,7 @@ static enum method_result pdb_method_request_symref_t(struct module_format *modf if (code.cv_typeid < T_MAXPREDEFINEDTYPE) return pdb_reader_basic_request(pdb, code.cv_typeid, req, data); if (pdb_reader_get_type_details(pdb, code.cv_typeid, &type_details)) return MR_FAILURE; - return pdb_reader_TPI_request(pdb, type_details, req, data); + return pdb_reader_TPI_request(pdb, symref, type_details, req, data); case symref_code_action: { struct pdb_action_entry *entry = &pdb->action_store[code.action]; @@ -2324,6 +2601,8 @@ static enum method_result pdb_method_request_symref_t(struct module_format *modf { case action_type_cv_typ_t: return pdb_reader_TPI_argtype_request(pdb, entry, req, data); + case action_type_field: + return pdb_reader_TPI_field_request(pdb, entry, req, data); default: return MR_FAILURE; } @@ -2334,6 +2613,14 @@ static enum method_result pdb_method_request_symref_t(struct module_format *modf } } +static enum method_result pdb_method_request_symref_t(struct module_format *modfmt, symref_t symref, IMAGEHLP_SYMBOL_TYPE_INFO req, void *data) +{ + struct pdb_reader *pdb; + + if (!pdb_hack_get_main_info(modfmt, &pdb, NULL)) return MR_FAILURE; + return pdb_reader_request_symref_t(pdb, symref, req, data); +} + symref_t cv_hack_ptr_to_symref(struct pdb_reader *pdb, cv_typ_t cv_typeid, struct symt *symt) { struct symref_code code; @@ -2349,7 +2636,9 @@ symref_t cv_hack_ptr_to_symref(struct pdb_reader *pdb, cv_typ_t cv_typeid, struc if (symt_check_tag(symt, SymTagPointerType) || symt_check_tag(symt, SymTagArrayType) || symt_check_tag(symt, SymTagFunctionType) || - symt_check_tag(symt, SymTagFunctionArgType)) + symt_check_tag(symt, SymTagFunctionArgType) || + symt_check_tag(symt, SymTagEnum) || + (symt_check_tag(symt, SymTagData) && symt_check_tag(((struct symt_data*)symt)->container, SymTagEnum))) { return pdb_reader_encode_symref(pdb, symref_code_init_from_cv_typeid(&code, cv_typeid), &symref) ? 0 : symref; } From a60ba7f2d53a1d45591658e7e366b64c705c151a Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Sat, 11 Jan 2025 16:32:01 +0100 Subject: [PATCH 262/454] dbghelp: Move UDT type handling to PDB backend. Signed-off-by: Eric Pouech (cherry picked from commit a244519ef28e6d4a2e9fdb29c673f5591d31191e) --- dlls/dbghelp/pdb.c | 334 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 322 insertions(+), 12 deletions(-) diff --git a/dlls/dbghelp/pdb.c b/dlls/dbghelp/pdb.c index 15ae1454ef2c..5354b97f64f4 100644 --- a/dlls/dbghelp/pdb.c +++ b/dlls/dbghelp/pdb.c @@ -1275,13 +1275,10 @@ static enum pdb_result pdb_reader_read_leaf_as_variant(struct pdb_reader *pdb, s } /* read a codeview numeric leaf from a partially loaded codeview type */ -static enum pdb_result codeview_fetch_leaf_as_int(const union codeview_type *cv_type, const unsigned char *data, int *value) +static enum pdb_result codeview_leaf_as_int(const unsigned char *data, int *value) { - unsigned data_len = min(sizeof(*cv_type), sizeof(cv_type->generic.len) + cv_type->generic.len) - (data - (const unsigned char*)cv_type); unsigned short int type; - if (data_len < sizeof(unsigned short)) return R_PDB_BUFFER_TOO_SMALL; - type = *(unsigned short int*)data; if (type < LF_NUMERIC) { @@ -1289,9 +1286,6 @@ static enum pdb_result codeview_fetch_leaf_as_int(const union codeview_type *cv_ } else { - if (data_len < codeview_get_leaf_length(type)) - return R_PDB_BUFFER_TOO_SMALL; - switch (type) { case LF_CHAR: *value = *(char*)(data + 2); break; @@ -1327,6 +1321,19 @@ static enum pdb_result codeview_fetch_leaf_as_int(const union codeview_type *cv_ return R_PDB_SUCCESS; } +static enum pdb_result codeview_fetch_leaf_as_int(const union codeview_type *cv_type, const unsigned char *data, int *value) +{ + unsigned data_len = min(sizeof(*cv_type), sizeof(cv_type->generic.len) + cv_type->generic.len) - (data - (const unsigned char*)cv_type); + unsigned short int type; + + if (data_len < sizeof(unsigned short)) return R_PDB_BUFFER_TOO_SMALL; + + type = *(unsigned short int*)data; + if (type >= LF_NUMERIC && data_len < codeview_get_leaf_length(type)) + return R_PDB_BUFFER_TOO_SMALL; + return codeview_leaf_as_int(data, value); +} + static WCHAR *heap_allocate_symname(const char *string) { unsigned sz = MultiByteToWideChar(CP_ACP, 0, string, -1, NULL, 0); @@ -2328,6 +2335,35 @@ static enum method_result pdb_reader_TPI_procsignature_request(struct pdb_reader } } +struct pdb_children_filler +{ + DWORD *indexes; + unsigned count; /* max allowed indexes */ + unsigned actual; /* actual index in indexes when filling */ +}; + +static enum pdb_result pdb_reader_push_children_filler(struct pdb_reader *pdb, struct pdb_children_filler *children, symref_t symref) +{ + if (children->actual >= children->count) return R_PDB_BUFFER_TOO_SMALL; + if (children->indexes) + children->indexes[children->actual] = symt_symref_to_index(pdb->module, symref); + children->actual++; + return R_PDB_SUCCESS; +} + +static enum pdb_result pdb_reader_push_action_and_filler(struct pdb_reader *pdb, enum pdb_action_type action, pdbsize_t stream_offset, + unsigned id_size, symref_t container_symref, struct pdb_children_filler *children) +{ + enum pdb_result result; + symref_t symref; + + if (!children->indexes) + symref = 0; + else + if ((result = pdb_reader_push_action(pdb, action, stream_offset, id_size, container_symref, &symref))) return result; + return pdb_reader_push_children_filler(pdb, children, symref); +} + static enum pdb_result pdb_reader_TPI_fillin_enumlist(struct pdb_reader *pdb, symref_t container_symref, cv_typ_t enumlist_cv_typeid, unsigned count, DWORD *index) { @@ -2433,10 +2469,214 @@ static enum method_result pdb_reader_TPI_enum_request(struct pdb_reader *pdb, sy } } +static enum pdb_result pdb_reader_TPI_fillin_UDTlist(struct pdb_reader *pdb, symref_t container_symref, + cv_typ_t udtlist_cv_typeid, struct pdb_children_filler *children) +{ + enum pdb_result result; + union codeview_reftype *cv_reftype; + pdbsize_t tpi_offset; + const unsigned char *start, *ptr, *last; + const union codeview_fieldtype *cv_field; + unsigned length; + VARIANT v; + + if ((result = pdb_reader_TPI_alloc_and_read_full_reftype(pdb, udtlist_cv_typeid, &cv_reftype, &tpi_offset))) return result; + if (cv_reftype->generic.id != LF_FIELDLIST_V2) return PDB_REPORT_UNEXPECTED("list", cv_reftype->generic.id); + start = ptr = (const unsigned char *)cv_reftype->fieldlist.list; + last = (const unsigned char *)cv_reftype + sizeof(cv_reftype->generic.id) + cv_reftype->generic.len; + + tpi_offset += offsetof(union codeview_reftype, fieldlist.list); + + while (ptr < last) + { + if (*ptr >= 0xf0) /* Padding */ + { + ptr += *ptr & 0x0f; + continue; + } + cv_field = (const union codeview_fieldtype *)ptr; + result = R_PDB_SUCCESS; + switch (cv_field->generic.id) + { + case LF_BCLASS_V2: + length = offsetof(union codeview_fieldtype, bclass_v2.data); + length += codeview_leaf_as_variant(ptr + length, &v); + /* FIXME: ignored for now */ + break; + + case LF_VBCLASS_V2: + case LF_IVBCLASS_V2: + length = offsetof(union codeview_fieldtype, vbclass_v2.data); + length += codeview_leaf_as_variant(ptr + length, &v); /* vbpoff */ + length += codeview_leaf_as_variant(ptr + length, &v); /* vboff */ + /* FIXME: ignored for now */ + break; + + case LF_MEMBER_V3: + length = offsetof(union codeview_fieldtype, member_v3.data); + length += codeview_leaf_as_variant(ptr + length, &v); + length += strlen((const char *)ptr + length) + 1; + result = pdb_reader_push_action_and_filler(pdb, action_type_field, tpi_offset + (ptr - start), length, container_symref, children); + break; + + case LF_STMEMBER_V3: + /* FIXME: ignored for now */ + length = offsetof(union codeview_fieldtype, stmember_v3.name) + strlen(cv_field->stmember_v3.name) + 1; + break; + + case LF_METHOD_V3: + /* FIXME: ignored for now */ + length = offsetof(union codeview_fieldtype, method_v3.name) + strlen(cv_field->method_v3.name) + 1; + break; + + case LF_NESTTYPE_V3: + /* FIXME: ignored for now */ + length = offsetof(union codeview_fieldtype, nesttype_v3.name) + strlen(cv_field->nesttype_v3.name) + 1; + break; + + case LF_VFUNCTAB_V2: + /* FIXME: ignored for now */ + length = sizeof(cv_field->vfunctab_v2); + break; + + case LF_ONEMETHOD_V3: + /* FIXME: ignored for now */ + switch ((cv_field->onemethod_v3.attribute >> 2) & 7) + { + case 4: case 6: /* (pure) introducing virtual method */ + length = offsetof(union codeview_fieldtype, onemethod_virt_v3.name); + break; + default: + length = offsetof(union codeview_fieldtype, onemethod_v3.name); + break; + } + length += strlen((const char *)ptr + length) + 1; + break; + + case LF_INDEX_V2: + if ((result = pdb_reader_TPI_fillin_UDTlist(pdb, container_symref, cv_field->index_v2.ref, children))) return result; + length = sizeof(cv_field->index_v2); + break; + + default: + result = PDB_REPORT_UNEXPECTED("UDT field list", cv_field->generic.id); + } + ptr += length; + if (result) break; + } + + pdb_reader_free(pdb, cv_reftype); + return result; +} + +static enum pdb_result pdb_reader_count_advertized_in_UDT_fieldlist(struct pdb_reader *pdb, symref_t container_symref, + cv_typ_t fieldlist_cv_typeid, DWORD *count) +{ + enum pdb_result result; + struct pdb_children_filler filler = {.actual = 0, .count = ~0u, .indexes = NULL}; + + if ((result = pdb_reader_TPI_fillin_UDTlist(pdb, container_symref, fieldlist_cv_typeid, &filler))) return result; + *count = filler.actual; + return R_PDB_SUCCESS; +} + +static enum method_result pdb_reader_TPI_UDT_request(struct pdb_reader *pdb, symref_t symref, const union codeview_type *cv_type, + const struct pdb_reader_walker *walker, IMAGEHLP_SYMBOL_TYPE_INFO req, void *data) +{ + enum UdtKind kind; + unsigned count; + const unsigned char *var; + cv_typ_t fieldlist_cv_typeid; + cv_property_t property; + + switch (cv_type->generic.id) + { + case LF_CLASS_V3: + case LF_STRUCTURE_V3: + kind = cv_type->generic.id == LF_CLASS_V3 ? UdtClass : UdtStruct; + count = cv_type->struct_v3.n_element; + fieldlist_cv_typeid = cv_type->struct_v3.fieldlist; + property = cv_type->struct_v3.property; + var = cv_type->struct_v3.data; + break; + case LF_UNION_V3: + kind = UdtUnion; + count = cv_type->union_v3.count; + fieldlist_cv_typeid = cv_type->union_v3.fieldlist; + property = cv_type->union_v3.property; + var = cv_type->union_v3.data; + break; + default: + return MR_FAILURE; + } + /* Note: we can have a forward type here when its declared but not defined. */ + + switch (req) + { + case TI_GET_SYMTAG: + *((DWORD*)data) = SymTagUDT; + return MR_SUCCESS; + case TI_FINDCHILDREN: + { + TI_FINDCHILDREN_PARAMS *p = data; + struct pdb_children_filler filler = {.actual = 0, .count = p->Count, .indexes = p->ChildId}; + + if (p->Start != 0) return MR_FAILURE; + if (property.is_forward_defn && !p->Count) return MR_SUCCESS; + if (pdb_reader_TPI_fillin_UDTlist(pdb, symref, fieldlist_cv_typeid, &filler)) return MR_FAILURE; + if (filler.actual != filler.count) {WARN("Count mismatch %u^%u %u\n", filler.actual, filler.count, count); return MR_FAILURE;} + } + return MR_SUCCESS; + case TI_GET_CHILDRENCOUNT: + if (!property.is_forward_defn) + /* Unfortunately the count field describes the number of entries in the field_list, not + * the count of entries we currently advertize (we drop a bunch of them in the fillin_ helpers). + * Be on the save side and always recompute. + */ + return pdb_reader_count_advertized_in_UDT_fieldlist(pdb, symref, fieldlist_cv_typeid, (DWORD*)data) ? MR_FAILURE : MR_SUCCESS; + *((DWORD*)data) = 0; + return MR_SUCCESS; + case TI_GET_SYMNAME: + { + VARIANT v; + char *string; + + if (pdb_reader_alloc_and_read_codeview_type_variablepart(pdb, *walker, cv_type, &v, &string, NULL)) + return MR_FAILURE; + *((WCHAR**)data) = heap_allocate_symname(string); + pdb_reader_free(pdb, string); + } + return *((WCHAR**)data) != NULL ? MR_SUCCESS : MR_FAILURE; + case TI_GET_LENGTH: + { + int value; + if (codeview_fetch_leaf_as_int(cv_type, var, &value)) return MR_FAILURE; + *((DWORD64*)data) = (unsigned)value; + } + return MR_SUCCESS; + case TI_GET_NESTED: + *((DWORD*)data) = property.is_nested; + return MR_SUCCESS; + case TI_GET_UDTKIND: + *((DWORD*)data) = kind; + return MR_SUCCESS; + case TI_GET_LEXICALPARENT: + *((DWORD*)data) = symt_ptr_to_index(pdb->module, &pdb->module->top->symt); + return MR_SUCCESS; + default: + return MR_FAILURE; + } +} + static enum method_result pdb_reader_request_symref_t(struct pdb_reader *pdb, symref_t symref, IMAGEHLP_SYMBOL_TYPE_INFO req, void *data); -static enum method_result pdb_reader_TPI_argtype_request(struct pdb_reader *pdb, struct pdb_action_entry *entry, IMAGEHLP_SYMBOL_TYPE_INFO req, void *data) +static enum method_result pdb_reader_TPI_modifier_request(struct pdb_reader *pdb, symref_t symref, const union codeview_type *cv_type, + IMAGEHLP_SYMBOL_TYPE_INFO req, void *data) +{ + return pdb_reader_request_cv_typeid(pdb, cv_type->modifier_v2.type, req, data) ? MR_SUCCESS : MR_FAILURE; +} +static enum method_result pdb_reader_TPI_argtype_request(struct pdb_reader *pdb, struct pdb_action_entry *entry, IMAGEHLP_SYMBOL_TYPE_INFO req, void *data) { enum pdb_result result; @@ -2472,7 +2712,7 @@ static enum method_result pdb_reader_TPI_argtype_request(struct pdb_reader *pdb, } static enum method_result pdb_reader_TPI_enumerate_request(struct pdb_reader *pdb, struct pdb_action_entry *entry, const union codeview_fieldtype *cv_field, - IMAGEHLP_SYMBOL_TYPE_INFO req, void *data) + IMAGEHLP_SYMBOL_TYPE_INFO req, void *data) { pdbsize_t var; VARIANT v; @@ -2509,6 +2749,61 @@ static enum method_result pdb_reader_TPI_enumerate_request(struct pdb_reader *pd } } +static enum method_result pdb_reader_TPI_UDT_field_request(struct pdb_reader *pdb, struct pdb_action_entry *entry, const union codeview_fieldtype *cv_field, + IMAGEHLP_SYMBOL_TYPE_INFO req, void *data) +{ + union codeview_reftype cv_reftype; + pdbsize_t tpi_offset; + + switch (req) + { + case TI_GET_SYMTAG: + *((DWORD*)data) = SymTagData; + return MR_SUCCESS; + case TI_GET_SYMNAME: + { + const unsigned char *var; + VARIANT v; + var = cv_field->member_v3.data; + var += codeview_leaf_as_variant(var, &v); + *((WCHAR **)data) = heap_allocate_symname((const char *)var); + } + return *((WCHAR **)data) != NULL ? MR_SUCCESS : MR_FAILURE; + case TI_GET_DATAKIND: + *((DWORD*)data) = DataIsMember; + return MR_SUCCESS; + case TI_GET_OFFSET: + return codeview_leaf_as_int( cv_field->member_v3.data, (int*)data) == R_PDB_SUCCESS ? MR_SUCCESS : MR_FAILURE; + case TI_GET_LENGTH: + if (cv_field->member_v3.type < T_FIRSTDEFINABLETYPE || + pdb_reader_TPI_read_partial_reftype(pdb, cv_field->member_v3.type, &cv_reftype, &tpi_offset) || + cv_reftype.generic.id != LF_BITFIELD_V2) + return MR_FAILURE; + *((DWORD64*)data) = cv_reftype.bitfield_v2.nbits; + return MR_SUCCESS; + case TI_GET_BITPOSITION: + if (cv_field->member_v3.type < T_FIRSTDEFINABLETYPE || + pdb_reader_TPI_read_partial_reftype(pdb, cv_field->member_v3.type, &cv_reftype, &tpi_offset) || + cv_reftype.generic.id != LF_BITFIELD_V2) + return MR_FAILURE; + *((DWORD*)data) = cv_reftype.bitfield_v2.bitoff; + return MR_SUCCESS; + case TI_GET_TYPE: + case TI_GET_TYPEID: + if (cv_field->member_v3.type >= T_FIRSTDEFINABLETYPE && + !pdb_reader_TPI_read_partial_reftype(pdb, cv_field->member_v3.type, &cv_reftype, &tpi_offset) && + cv_reftype.generic.id == LF_BITFIELD_V2) + return pdb_reader_index_from_cv_typeid(pdb, cv_reftype.bitfield_v2.type, (DWORD *)data) == R_PDB_SUCCESS ? MR_SUCCESS : MR_FAILURE; + return pdb_reader_index_from_cv_typeid(pdb, cv_field->member_v3.type, (DWORD *)data) == R_PDB_SUCCESS ? MR_SUCCESS : MR_FAILURE; + case TI_FINDCHILDREN: + case TI_GET_CHILDRENCOUNT: + case TI_GET_LEXICALPARENT: + return pdb_reader_default_request(pdb, req, data); + default: + return MR_FAILURE; + } +} + static enum method_result pdb_reader_TPI_field_request(struct pdb_reader *pdb, struct pdb_action_entry *entry, IMAGEHLP_SYMBOL_TYPE_INFO req, void *data) { enum pdb_result result; @@ -2519,15 +2814,19 @@ static enum method_result pdb_reader_TPI_field_request(struct pdb_reader *pdb, s walker = pdb->tpi_types_walker; walker.offset = entry->stream_offset; + if ((result = pdb_reader_alloc(pdb, entry->action_length, (void**)&cv_field))) return MR_FAILURE; - if (!pdb_reader_read_from_stream(pdb, &walker, cv_field, entry->action_length, &num_read) && - num_read == entry->action_length) + if ((result = pdb_reader_read_from_stream(pdb, &walker, cv_field, entry->action_length, &num_read))) return MR_FAILURE; + if (num_read == entry->action_length) { switch (cv_field->generic.id) { case LF_ENUMERATE_V3: ret = pdb_reader_TPI_enumerate_request(pdb, entry, cv_field, req, data); break; + case LF_MEMBER_V3: + ret = pdb_reader_TPI_UDT_field_request(pdb, entry, cv_field, req, data); + break; default: PDB_REPORT_UNEXPECTED("field list", cv_field->generic.id); break; @@ -2565,6 +2864,14 @@ static enum method_result pdb_reader_TPI_request(struct pdb_reader *pdb, symref_ case LF_ENUM_V3: return pdb_reader_TPI_enum_request(pdb, symref, &cv_type, &walker, req, data); + case LF_UNION_V3: + case LF_STRUCTURE_V3: + case LF_CLASS_V3: + return pdb_reader_TPI_UDT_request(pdb, symref, &cv_type, &walker, req, data); + + case LF_MODIFIER_V2: + return pdb_reader_TPI_modifier_request(pdb, symref, &cv_type, req, data); + default: PDB_REPORT_UNEXPECTED("codeview type id", cv_type.generic.id); ret = MR_FAILURE; @@ -2638,7 +2945,10 @@ symref_t cv_hack_ptr_to_symref(struct pdb_reader *pdb, cv_typ_t cv_typeid, struc symt_check_tag(symt, SymTagFunctionType) || symt_check_tag(symt, SymTagFunctionArgType) || symt_check_tag(symt, SymTagEnum) || - (symt_check_tag(symt, SymTagData) && symt_check_tag(((struct symt_data*)symt)->container, SymTagEnum))) + (symt_check_tag(symt, SymTagData) && symt_check_tag(((struct symt_data*)symt)->container, SymTagEnum)) || + symt_check_tag(symt, SymTagUDT) || + (symt_check_tag(symt, SymTagData) && symt_check_tag(((struct symt_data*)symt)->container, SymTagUDT))) + { return pdb_reader_encode_symref(pdb, symref_code_init_from_cv_typeid(&code, cv_typeid), &symref) ? 0 : symref; } From 58335df680f6c9b5cd94c75e93b600faa16b3ea9 Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Sun, 12 Jan 2025 10:08:05 +0100 Subject: [PATCH 263/454] dbghelp: No longer preload the types from PDB. Signed-off-by: Eric Pouech (cherry picked from commit 1260736da02a470d7e4cc9333f2f2a5170097b93) --- dlls/dbghelp/dbghelp_private.h | 5 +- dlls/dbghelp/msc.c | 110 +++++++++++++++++---------------- dlls/dbghelp/pdb.c | 77 +++++++++-------------- 3 files changed, 88 insertions(+), 104 deletions(-) diff --git a/dlls/dbghelp/dbghelp_private.h b/dlls/dbghelp/dbghelp_private.h index d8f82877180f..4470420dd4f3 100644 --- a/dlls/dbghelp/dbghelp_private.h +++ b/dlls/dbghelp/dbghelp_private.h @@ -316,7 +316,7 @@ struct symt_public struct symt symt; struct hash_table_elt hash_elt; struct symt* container; /* compiland */ - BOOL is_function; + BOOL is_function; ULONG_PTR address; ULONG_PTR size; }; @@ -1075,5 +1075,4 @@ struct pdb_reader; extern BOOL pdb_hack_get_main_info(struct module_format *modfmt, struct pdb_reader **pdb, unsigned *fpoext_stream); extern void pdb_reader_dispose(struct pdb_reader *pdb); extern struct pdb_reader *pdb_hack_reader_init(struct module *module, HANDLE file, const IMAGE_SECTION_HEADER *sections, unsigned num_sections); -extern symref_t cv_hack_ptr_to_symref(struct pdb_reader *pdb, unsigned typeno, struct symt *symt); -extern struct symt *pdb_hack_advertize_codeview_type(struct pdb_reader *pdb, unsigned typeid, struct symt *symt, unsigned offset); +extern BOOL cv_hack_ptr_to_symref(struct pdb_reader *pdb, unsigned typeno, symref_t *symref); diff --git a/dlls/dbghelp/msc.c b/dlls/dbghelp/msc.c index 729c860342c1..09b36803be8e 100644 --- a/dlls/dbghelp/msc.c +++ b/dlls/dbghelp/msc.c @@ -619,8 +619,6 @@ static int codeview_add_type(unsigned int typeno, struct symt* dt, unsigned offs return FALSE; } idx = typeno - cv_current_module->first_type_index; - if (cv_current_module->pdb) - pdb_hack_advertize_codeview_type(cv_current_module->pdb, typeno, dt, offset); if (cv_current_module->defined_types[idx]) { @@ -668,15 +666,25 @@ static struct symt* codeview_fetch_type(struct codeview_type_parse* ctp, static symref_t codeview_fetch_symref(struct codeview_type_parse* ctp, unsigned typeno) { - struct symt *symt = codeview_fetch_type(ctp, typeno); - return cv_hack_ptr_to_symref(cv_current_module->pdb, typeno, symt); + symref_t symref; + + if (!cv_hack_ptr_to_symref(ctp->module->format_info[DFI_PDB]->u.pdb_info->pdb_files[0].pdb_reader, typeno, &symref)) + { + symref = symt_ptr_to_symref(codeview_fetch_type(ctp, typeno)); + } + + return symref; } -static symref_t codeview_get_symref(struct module *module, unsigned typeno, struct symt *symt) +static symref_t codeview_get_symref(struct module *module, unsigned typeno) { - if (!symt) - symt = codeview_get_type(typeno, FALSE); - return cv_hack_ptr_to_symref(module->format_info[DFI_PDB]->u.pdb_info->pdb_files[0].pdb_reader, typeno, symt); + symref_t symref; + + if (!cv_hack_ptr_to_symref(module->format_info[DFI_PDB]->u.pdb_info->pdb_files[0].pdb_reader, typeno, &symref)) + { + symref = symt_ptr_to_symref(codeview_get_type(typeno, TRUE)); + } + return symref; } static UINT32 codeview_compute_hash(const char* ptr, unsigned len) @@ -1000,7 +1008,7 @@ static void codeview_add_udt_element(struct codeview_type_parse* ctp, { DWORD64 elem_size = 0; symt_get_info(ctp->module, subtype, TI_GET_LENGTH, &elem_size); - symt_add_udt_element(ctp->module, symt, name, codeview_get_symref(ctp->module, type, subtype), + symt_add_udt_element(ctp->module, symt, name, codeview_get_symref(ctp->module, type), value, 0, 0); } } @@ -1837,7 +1845,7 @@ static inline void codeview_add_variable(const struct msc_debug_info* msc_dbg, { if (!is_local || in_tls) WARN("Unsupported construct\n"); symt_add_func_local(msc_dbg->module, func, DataIsStaticLocal, &loc, block, - codeview_get_symref(msc_dbg->module, symtype, NULL), name); + codeview_get_symref(msc_dbg->module, symtype), name); return; } if (!dontcheck && !in_tls) @@ -1870,7 +1878,7 @@ static inline void codeview_add_variable(const struct msc_debug_info* msc_dbg, } if (is_local ^ (compiland != NULL)) FIXME("Unsupported construct\n"); symt_new_global_variable(msc_dbg->module, compiland, name, is_local, loc, 0, - codeview_get_symref(msc_dbg->module, symtype, NULL)); + codeview_get_symref(msc_dbg->module, symtype)); } } @@ -2205,14 +2213,14 @@ static struct symt_function* codeview_create_inline_site(const struct msc_debug_ case LF_FUNC_ID: inlined = symt_new_inlinesite(msc_dbg->module, top_func, container, cvt->func_id_v3.name, - codeview_get_symref(msc_dbg->module, cvt->func_id_v3.type, NULL), + codeview_get_symref(msc_dbg->module, cvt->func_id_v3.type), num_ranges); break; case LF_MFUNC_ID: /* FIXME we just declare a function, not a method */ inlined = symt_new_inlinesite(msc_dbg->module, top_func, container, cvt->mfunc_id_v3.name, - codeview_get_symref(msc_dbg->module, cvt->mfunc_id_v3.type, NULL), + codeview_get_symref(msc_dbg->module, cvt->mfunc_id_v3.type), num_ranges); break; default: @@ -2327,7 +2335,6 @@ static BOOL codeview_snarf(const struct msc_debug_info* msc_dbg, { struct symt_function* top_func = NULL; struct symt_function* curr_func = NULL; - struct symt* func_signature; int i, length; struct symt_block* block = NULL; struct symt_compiland* compiland = NULL; @@ -2427,14 +2434,12 @@ static BOOL codeview_snarf(const struct msc_debug_info* msc_dbg, case S_GPROC32_16t: case S_LPROC32_16t: if (top_func) FIXME("nested function\n"); - func_signature = codeview_get_type(sym->proc_v1.proctype, FALSE); - if (!func_signature || func_signature->tag == SymTagFunctionType) + if ((top_func = symt_new_function(msc_dbg->module, compiland, + terminate_string(&sym->proc_v1.p_name), + codeview_get_address(msc_dbg, sym->proc_v1.segment, sym->proc_v1.offset), + sym->proc_v1.proc_len, + codeview_get_symref(msc_dbg->module, sym->proc_v1.proctype)))) { - top_func = symt_new_function(msc_dbg->module, compiland, - terminate_string(&sym->proc_v1.p_name), - codeview_get_address(msc_dbg, sym->proc_v1.segment, sym->proc_v1.offset), - sym->proc_v1.proc_len, - codeview_get_symref(msc_dbg->module, sym->proc_v1.proctype, func_signature)); curr_func = top_func; loc.kind = loc_absolute; loc.offset = sym->proc_v1.debug_start; @@ -2446,14 +2451,12 @@ static BOOL codeview_snarf(const struct msc_debug_info* msc_dbg, case S_GPROC32_ST: case S_LPROC32_ST: if (top_func) FIXME("nested function\n"); - func_signature = codeview_get_type(sym->proc_v2.proctype, FALSE); - if (!func_signature || func_signature->tag == SymTagFunctionType) + if ((top_func = symt_new_function(msc_dbg->module, compiland, + terminate_string(&sym->proc_v2.p_name), + codeview_get_address(msc_dbg, sym->proc_v2.segment, sym->proc_v2.offset), + sym->proc_v2.proc_len, + codeview_get_symref(msc_dbg->module, sym->proc_v2.proctype)))) { - top_func = symt_new_function(msc_dbg->module, compiland, - terminate_string(&sym->proc_v2.p_name), - codeview_get_address(msc_dbg, sym->proc_v2.segment, sym->proc_v2.offset), - sym->proc_v2.proc_len, - codeview_get_symref(msc_dbg->module, sym->proc_v2.proctype, func_signature)); curr_func = top_func; loc.kind = loc_absolute; loc.offset = sym->proc_v2.debug_start; @@ -2465,14 +2468,12 @@ static BOOL codeview_snarf(const struct msc_debug_info* msc_dbg, case S_GPROC32: case S_LPROC32: if (top_func) FIXME("nested function\n"); - func_signature = codeview_get_type(sym->proc_v3.proctype, FALSE); - if (!func_signature || func_signature->tag == SymTagFunctionType) + if ((top_func = symt_new_function(msc_dbg->module, compiland, + sym->proc_v3.name, + codeview_get_address(msc_dbg, sym->proc_v3.segment, sym->proc_v3.offset), + sym->proc_v3.proc_len, + codeview_get_symref(msc_dbg->module, sym->proc_v3.proctype)))) { - top_func = symt_new_function(msc_dbg->module, compiland, - sym->proc_v3.name, - codeview_get_address(msc_dbg, sym->proc_v3.segment, sym->proc_v3.offset), - sym->proc_v3.proc_len, - codeview_get_symref(msc_dbg->module, sym->proc_v3.proctype, func_signature)); curr_func = top_func; loc.kind = loc_absolute; loc.offset = sym->proc_v3.debug_start; @@ -2492,7 +2493,7 @@ static BOOL codeview_snarf(const struct msc_debug_info* msc_dbg, symt_add_func_local(msc_dbg->module, curr_func, sym->stack_v1.offset > 0 ? DataIsParam : DataIsLocal, &loc, block, - codeview_get_symref(msc_dbg->module, sym->stack_v1.symtype, NULL), + codeview_get_symref(msc_dbg->module, sym->stack_v1.symtype), terminate_string(&sym->stack_v1.p_name)); break; case S_BPREL32_ST: @@ -2503,7 +2504,7 @@ static BOOL codeview_snarf(const struct msc_debug_info* msc_dbg, symt_add_func_local(msc_dbg->module, curr_func, sym->stack_v2.offset > 0 ? DataIsParam : DataIsLocal, &loc, block, - codeview_get_symref(msc_dbg->module, sym->stack_v2.symtype, NULL), + codeview_get_symref(msc_dbg->module, sym->stack_v2.symtype), terminate_string(&sym->stack_v2.p_name)); break; case S_BPREL32: @@ -2516,7 +2517,7 @@ static BOOL codeview_snarf(const struct msc_debug_info* msc_dbg, symt_add_func_local(msc_dbg->module, curr_func, sym->stack_v3.offset > 0 ? DataIsParam : DataIsLocal, &loc, block, - codeview_get_symref(msc_dbg->module, sym->stack_v3.symtype, NULL), + codeview_get_symref(msc_dbg->module, sym->stack_v3.symtype), sym->stack_v3.name); break; case S_REGREL32: @@ -2529,7 +2530,7 @@ static BOOL codeview_snarf(const struct msc_debug_info* msc_dbg, symt_add_func_local(msc_dbg->module, curr_func, sym->regrel_v3.offset >= top_frame_size ? DataIsParam : DataIsLocal, &loc, block, - codeview_get_symref(msc_dbg->module, sym->regrel_v3.symtype, NULL), + codeview_get_symref(msc_dbg->module, sym->regrel_v3.symtype), sym->regrel_v3.name); break; @@ -2539,7 +2540,7 @@ static BOOL codeview_snarf(const struct msc_debug_info* msc_dbg, loc.offset = 0; symt_add_func_local(msc_dbg->module, curr_func, DataIsLocal, &loc, block, - codeview_get_symref(msc_dbg->module, sym->register_v1.type, NULL), + codeview_get_symref(msc_dbg->module, sym->register_v1.type), terminate_string(&sym->register_v1.p_name)); break; case S_REGISTER_ST: @@ -2548,7 +2549,7 @@ static BOOL codeview_snarf(const struct msc_debug_info* msc_dbg, loc.offset = 0; symt_add_func_local(msc_dbg->module, curr_func, DataIsLocal, &loc, block, - codeview_get_symref(msc_dbg->module, sym->register_v2.type, NULL), + codeview_get_symref(msc_dbg->module, sym->register_v2.type), terminate_string(&sym->register_v2.p_name)); break; case S_REGISTER: @@ -2559,7 +2560,7 @@ static BOOL codeview_snarf(const struct msc_debug_info* msc_dbg, loc.offset = 0; symt_add_func_local(msc_dbg->module, curr_func, DataIsLocal, &loc, block, - codeview_get_symref(msc_dbg->module, sym->register_v3.type, NULL), + codeview_get_symref(msc_dbg->module, sym->register_v3.type), sym->register_v3.name); break; @@ -2657,7 +2658,7 @@ static BOOL codeview_snarf(const struct msc_debug_info* msc_dbg, TRACE("S-Constant-V1 %u %s %x\n", V_INT(&v), terminate_string(name), sym->constant_v1.type); symt_new_constant(msc_dbg->module, compiland, terminate_string(name), - codeview_get_symref(msc_dbg->module, sym->constant_v1.type, NULL), &v); + codeview_get_symref(msc_dbg->module, sym->constant_v1.type), &v); } break; case S_CONSTANT_ST: @@ -2671,7 +2672,7 @@ static BOOL codeview_snarf(const struct msc_debug_info* msc_dbg, TRACE("S-Constant-V2 %u %s %x\n", V_INT(&v), terminate_string(name), sym->constant_v2.type); symt_new_constant(msc_dbg->module, compiland, terminate_string(name), - codeview_get_symref(msc_dbg->module, sym->constant_v2.type, NULL), &v); + codeview_get_symref(msc_dbg->module, sym->constant_v2.type), &v); } break; case S_CONSTANT: @@ -2686,28 +2687,28 @@ static BOOL codeview_snarf(const struct msc_debug_info* msc_dbg, TRACE("S-Constant-V3 %u %s %x\n", V_INT(&v), debugstr_a(name), sym->constant_v3.type); /* FIXME: we should add this as a constant value */ symt_new_constant(msc_dbg->module, compiland, name, - codeview_get_symref(msc_dbg->module, sym->constant_v3.type, NULL), &v); + codeview_get_symref(msc_dbg->module, sym->constant_v3.type), &v); } break; case S_UDT_16t: if (sym->udt_v1.type) { - symt_new_typedef(msc_dbg->module, codeview_get_symref(msc_dbg->module, sym->udt_v1.type, NULL), + symt_new_typedef(msc_dbg->module, codeview_get_symref(msc_dbg->module, sym->udt_v1.type), terminate_string(&sym->udt_v1.p_name)); } break; case S_UDT_ST: if (sym->udt_v2.type) { - symt_new_typedef(msc_dbg->module, codeview_get_symref(msc_dbg->module, sym->udt_v2.type, NULL), + symt_new_typedef(msc_dbg->module, codeview_get_symref(msc_dbg->module, sym->udt_v2.type), terminate_string(&sym->udt_v2.p_name)); } break; case S_UDT: if (sym->udt_v3.type) { - symt_new_typedef(msc_dbg->module, codeview_get_symref(msc_dbg->module, sym->udt_v3.type, NULL), + symt_new_typedef(msc_dbg->module, codeview_get_symref(msc_dbg->module, sym->udt_v3.type), sym->udt_v3.name); } break; @@ -2730,7 +2731,7 @@ static BOOL codeview_snarf(const struct msc_debug_info* msc_dbg, symt_add_func_local(msc_dbg->module, curr_func, sym->local_v3.varflags.is_param ? DataIsParam : DataIsLocal, &loc, block, - codeview_get_symref(msc_dbg->module, sym->local_v3.symtype, NULL), + codeview_get_symref(msc_dbg->module, sym->local_v3.symtype), sym->local_v3.name); } else @@ -2990,21 +2991,21 @@ static BOOL pdb_global_feed_types(const struct msc_debug_info* msc_dbg, const un case S_UDT_16t: if (sym->udt_v1.type) { - symt_new_typedef(msc_dbg->module, codeview_get_symref(msc_dbg->module, sym->udt_v1.type, NULL), + symt_new_typedef(msc_dbg->module, codeview_get_symref(msc_dbg->module, sym->udt_v1.type), terminate_string(&sym->udt_v1.p_name)); } break; case S_UDT_ST: if (sym->udt_v2.type) { - symt_new_typedef(msc_dbg->module, codeview_get_symref(msc_dbg->module, sym->udt_v2.type, NULL), + symt_new_typedef(msc_dbg->module, codeview_get_symref(msc_dbg->module, sym->udt_v2.type), terminate_string(&sym->udt_v2.p_name)); } break; case S_UDT: if (sym->udt_v3.type) { - symt_new_typedef(msc_dbg->module, codeview_get_symref(msc_dbg->module, sym->udt_v3.type, NULL), + symt_new_typedef(msc_dbg->module, codeview_get_symref(msc_dbg->module, sym->udt_v3.type), sym->udt_v3.name); } break; @@ -3877,7 +3878,10 @@ static BOOL pdb_process_internal(const struct process *pcs, pdb_process_symbol_imports(pcs, msc_dbg, &symbols, symbols_image, image, pdb_module_info, module_index); - pdb_process_types(msc_dbg, pdb_file); + if (!pdb_file->pdb_reader) + { + pdb_process_types(msc_dbg, pdb_file); + } ipi_image = pdb_read_stream(pdb_file, 4); ipi_ok = pdb_init_type_parse(msc_dbg, pdb_file, &ipi_ctp, ipi_image); diff --git a/dlls/dbghelp/pdb.c b/dlls/dbghelp/pdb.c index 5354b97f64f4..b519868ff42a 100644 --- a/dlls/dbghelp/pdb.c +++ b/dlls/dbghelp/pdb.c @@ -76,7 +76,6 @@ struct pdb_type_details { pdbsize_t stream_offset; /* inside TPI stream */ cv_typ_t resolved_cv_typeid; - struct symt *symt; }; enum pdb_action_type @@ -303,6 +302,11 @@ static enum pdb_result pdb_reader_encode_symref(struct pdb_reader *pdb, const st switch (code->kind) { case symref_code_cv_typeid: + if (!code->cv_typeid) + { + *symref = 0; + return R_PDB_SUCCESS; + } if (code->cv_typeid < T_MAXPREDEFINEDTYPE) v = code->cv_typeid; else if (code->cv_typeid >= pdb->tpi_header.first_index && code->cv_typeid < pdb->tpi_header.last_index) @@ -410,6 +414,9 @@ static enum pdb_result pdb_reader_init(struct pdb_reader *pdb, struct module *mo } pdb->streams[i].name = NULL; } + /* hack (must be set before loading debug info so it can be used therein) */ + pdb->module->ops_symref_modfmt = module->format_info[DFI_PDB]; + return R_PDB_SUCCESS; failure: @@ -2025,14 +2032,14 @@ static enum method_result pdb_method_find_type(struct module_format *modfmt, con struct pdb_reader_walker walker; cv_typ_t cv_typeid; union codeview_type cv_type; + struct symref_code code; struct pdb_type_details *type_details; if (!pdb_hack_get_main_info(modfmt, &pdb, NULL)) return MR_FAILURE; if ((result = pdb_reader_init_TPI(pdb))) return pdb_method_result(result); if ((result = pdb_reader_read_codeview_type_by_name(pdb, name, &walker, &cv_type, &cv_typeid))) return pdb_method_result(result); if ((result = pdb_reader_get_type_details(pdb, cv_typeid, &type_details))) return pdb_method_result(result); - *ref = cv_hack_ptr_to_symref(pdb, cv_typeid, type_details->symt); - return MR_SUCCESS; + return pdb_reader_encode_symref(pdb, symref_code_init_from_cv_typeid(&code, cv_typeid), ref) == R_PDB_SUCCESS ? MR_SUCCESS : MR_FAILURE; } static BOOL codeview_type_is_forward(const union codeview_type* cvtype) @@ -2093,15 +2100,21 @@ static enum pdb_result pdb_reader_resolve_cv_typeid(struct pdb_reader *pdb, cv_t { case R_PDB_SUCCESS: type_details->resolved_cv_typeid = other_cv_typeid; + if (!type_details->stream_offset) type_details->stream_offset = other_walker.offset; break; case R_PDB_NOT_FOUND: /* we can have a forward decl without a real implementation */ type_details->resolved_cv_typeid = raw_cv_typeid; + if (!type_details->stream_offset) type_details->stream_offset = tpi_offset; break; default: return result; } } - else type_details->resolved_cv_typeid = raw_cv_typeid; + else + { + type_details->resolved_cv_typeid = raw_cv_typeid; + if (!type_details->stream_offset) type_details->stream_offset = tpi_offset; + } *cv_typeid = type_details->resolved_cv_typeid; return R_PDB_SUCCESS; @@ -2116,6 +2129,8 @@ static enum method_result pdb_method_enumerate_types(struct module_format *modfm struct pdb_type_details *type_details; struct pdb_type_hash_entry *hash_entry; union codeview_type cv_type; + struct symref_code code; + symref_t symref; char *name; VARIANT v; BOOL ret; @@ -2143,8 +2158,8 @@ static enum method_result pdb_method_enumerate_types(struct module_format *modfm result = pdb_reader_alloc_and_read_codeview_type_variablepart(pdb, walker, &cv_type, &v, &name, NULL); if (!result) { - if (*name) - ret = (*cb)(cv_hack_ptr_to_symref(pdb, hash_entry->cv_typeid, type_details->symt), name, user); + if (*name && pdb_reader_encode_symref(pdb, symref_code_init_from_cv_typeid(&code, hash_entry->cv_typeid), &symref) == R_PDB_SUCCESS) + ret = (*cb)(symref, name, user); else ret = TRUE; pdb_reader_free(pdb, name); @@ -2162,14 +2177,9 @@ static enum pdb_result pdb_reader_index_from_cv_typeid(struct pdb_reader *pdb, c symref_t symref; if (cv_typeid > T_MAXPREDEFINEDTYPE) - { - struct pdb_type_details *type_details; - if ((result = pdb_reader_resolve_cv_typeid(pdb, cv_typeid, &cv_typeid))) return result; - if ((result = pdb_reader_get_type_details(pdb, cv_typeid, &type_details))) return result; - symref = cv_hack_ptr_to_symref(pdb, cv_typeid, type_details->symt); - } - else if ((result = pdb_reader_encode_symref(pdb, symref_code_init_from_cv_typeid(&code, cv_typeid), &symref))) return result; + if ((result = pdb_reader_encode_symref(pdb, symref_code_init_from_cv_typeid(&code, cv_typeid), &symref))) return result; + *index = symt_symref_to_index(pdb->module, symref); return R_PDB_SUCCESS; } @@ -2898,6 +2908,7 @@ static enum method_result pdb_reader_request_symref_t(struct pdb_reader *pdb, sy case symref_code_cv_typeid: if (code.cv_typeid < T_MAXPREDEFINEDTYPE) return pdb_reader_basic_request(pdb, code.cv_typeid, req, data); + if (pdb_reader_resolve_cv_typeid(pdb, code.cv_typeid, &code.cv_typeid)) return MR_FAILURE; if (pdb_reader_get_type_details(pdb, code.cv_typeid, &type_details)) return MR_FAILURE; return pdb_reader_TPI_request(pdb, symref, type_details, req, data); case symref_code_action: @@ -2925,47 +2936,17 @@ static enum method_result pdb_method_request_symref_t(struct module_format *modf struct pdb_reader *pdb; if (!pdb_hack_get_main_info(modfmt, &pdb, NULL)) return MR_FAILURE; + if (pdb_reader_init_TPI(pdb)) return MR_FAILURE; return pdb_reader_request_symref_t(pdb, symref, req, data); } -symref_t cv_hack_ptr_to_symref(struct pdb_reader *pdb, cv_typ_t cv_typeid, struct symt *symt) +BOOL cv_hack_ptr_to_symref(struct pdb_reader *pdb, cv_typ_t cv_typeid, symref_t *symref) { struct symref_code code; - symref_t symref; - - if (pdb) - { - if (symt_check_tag(symt, SymTagBaseType)) - { - if (cv_typeid < T_MAXPREDEFINEDTYPE) - return pdb_reader_encode_symref(pdb, symref_code_init_from_cv_typeid(&code, cv_typeid), &symref) ? 0 : symref; - } - if (symt_check_tag(symt, SymTagPointerType) || - symt_check_tag(symt, SymTagArrayType) || - symt_check_tag(symt, SymTagFunctionType) || - symt_check_tag(symt, SymTagFunctionArgType) || - symt_check_tag(symt, SymTagEnum) || - (symt_check_tag(symt, SymTagData) && symt_check_tag(((struct symt_data*)symt)->container, SymTagEnum)) || - symt_check_tag(symt, SymTagUDT) || - (symt_check_tag(symt, SymTagData) && symt_check_tag(((struct symt_data*)symt)->container, SymTagUDT))) - - { - return pdb_reader_encode_symref(pdb, symref_code_init_from_cv_typeid(&code, cv_typeid), &symref) ? 0 : symref; - } - } - return symt_ptr_to_symref(symt); -} - -struct symt *pdb_hack_advertize_codeview_type(struct pdb_reader *pdb, cv_typ_t cv_typeid, struct symt *symt, unsigned offset) -{ - if (pdb_reader_init_TPI(pdb)) return symt; - if (!pdb->tpi_typemap[cv_typeid - pdb->tpi_header.first_index].symt) - { - pdb->tpi_typemap[cv_typeid - pdb->tpi_header.first_index].symt = symt; - pdb->tpi_typemap[cv_typeid - pdb->tpi_header.first_index].stream_offset = offset; - } - return symt; + if (!pdb) return FALSE; + if (pdb_reader_init_TPI(pdb)) return FALSE; + return pdb_reader_encode_symref(pdb, symref_code_init_from_cv_typeid(&code, cv_typeid), symref) == R_PDB_SUCCESS; } static struct module_format_vtable pdb_module_format_vtable = From f4cb6ad747ca3fce6593843507dc365dfb2f7584 Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Thu, 17 Apr 2025 13:04:59 +0200 Subject: [PATCH 264/454] dbghelp: Optimize request to codeview types. Signed-off-by: Eric Pouech (cherry picked from commit 22158c53e8b3040779aab0011457d2631c4cd887) --- dlls/dbghelp/pdb.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/dlls/dbghelp/pdb.c b/dlls/dbghelp/pdb.c index b519868ff42a..955318669594 100644 --- a/dlls/dbghelp/pdb.c +++ b/dlls/dbghelp/pdb.c @@ -2184,13 +2184,15 @@ static enum pdb_result pdb_reader_index_from_cv_typeid(struct pdb_reader *pdb, c return R_PDB_SUCCESS; } -/* FIXME: suboptimal implementation until we have moved all types */ +static enum method_result pdb_reader_request_symref_t(struct pdb_reader *pdb, symref_t symref, IMAGEHLP_SYMBOL_TYPE_INFO req, void *data); + static BOOL pdb_reader_request_cv_typeid(struct pdb_reader *pdb, cv_typ_t cv_typeid, IMAGEHLP_SYMBOL_TYPE_INFO req, void *data) { - DWORD index; + struct symref_code code; + symref_t target_symref; - if (pdb_reader_index_from_cv_typeid(pdb, cv_typeid, &index)) return FALSE; - return symt_get_info_from_index(pdb->module, index, req, data); + if ((pdb_reader_encode_symref(pdb, symref_code_init_from_cv_typeid(&code, cv_typeid), &target_symref))) return MR_FAILURE; + return pdb_reader_request_symref_t(pdb, target_symref, req, data) == MR_SUCCESS; } static enum method_result pdb_reader_TPI_pointer_request(struct pdb_reader *pdb, const union codeview_type *cv_type, IMAGEHLP_SYMBOL_TYPE_INFO req, void *data) @@ -2678,8 +2680,6 @@ static enum method_result pdb_reader_TPI_UDT_request(struct pdb_reader *pdb, sym } } -static enum method_result pdb_reader_request_symref_t(struct pdb_reader *pdb, symref_t symref, IMAGEHLP_SYMBOL_TYPE_INFO req, void *data); - static enum method_result pdb_reader_TPI_modifier_request(struct pdb_reader *pdb, symref_t symref, const union codeview_type *cv_type, IMAGEHLP_SYMBOL_TYPE_INFO req, void *data) { From 7985df6ae97adaf2e8fe9e570db7bb7f4134832c Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Fri, 17 Jan 2025 09:55:19 +0100 Subject: [PATCH 265/454] dbghelp: Move typedef handling to the new PDB backend. And no longer load typedef information from old PDB backend. Signed-off-by: Eric Pouech (cherry picked from commit 8b75e597e232bc1afcda2e3a36efd4732cd28b31) --- dlls/dbghelp/msc.c | 2 +- dlls/dbghelp/pdb.c | 367 ++++++++++++++++++++++++++++++++++++++++---- dlls/dbghelp/type.c | 18 +-- 3 files changed, 340 insertions(+), 47 deletions(-) diff --git a/dlls/dbghelp/msc.c b/dlls/dbghelp/msc.c index 09b36803be8e..8b346f0043f0 100644 --- a/dlls/dbghelp/msc.c +++ b/dlls/dbghelp/msc.c @@ -3890,7 +3890,7 @@ static BOOL pdb_process_internal(const struct process *pcs, * streams' loading can succeed them. */ globalimage = pdb_read_stream(pdb_file, symbols.gsym_stream); - if (globalimage) + if (globalimage && !pdb_file->pdb_reader) { const BYTE* data; unsigned global_size = pdb_get_stream_size(pdb_file, symbols.gsym_stream); diff --git a/dlls/dbghelp/pdb.c b/dlls/dbghelp/pdb.c index 955318669594..555c1c6574ff 100644 --- a/dlls/dbghelp/pdb.c +++ b/dlls/dbghelp/pdb.c @@ -29,7 +29,6 @@ #include "windef.h" #include "winbase.h" -#include "winternl.h" #include "wine/exception.h" #include "wine/debug.h" @@ -82,6 +81,7 @@ enum pdb_action_type { action_type_cv_typ_t, /* cv_typ_t or cv_typ16_t (depending on size) */ action_type_field, /* union codeview_fieldtype */ + action_type_globals, /* union codeview_symbol in DBI's globals stream */ }; struct pdb_action_entry @@ -98,6 +98,12 @@ struct pdb_type_hash_entry struct pdb_type_hash_entry *next; }; +struct pdb_dbi_hash_entry +{ + pdbsize_t dbi_stream_offset; + struct pdb_dbi_hash_entry *next; +}; + struct pdb_reader { struct module *module; @@ -127,8 +133,11 @@ struct pdb_reader struct pdb_type_hash_entry *tpi_types_hash; /* symbol (and types) management */ + PDB_SYMBOLS dbi_header; + unsigned num_action_globals; unsigned num_action_entries; struct pdb_action_entry *action_store; + struct pdb_dbi_hash_entry *dbi_symbols_hash; /* cache PE module sections for mapping... * we should rather use pe_module information @@ -361,6 +370,8 @@ static enum pdb_result pdb_reader_push_action(struct pdb_reader *pdb, enum pdb_a return R_PDB_SUCCESS; } +static enum pdb_result pdb_reader_init_DBI(struct pdb_reader *pdb); + static enum pdb_result pdb_reader_init(struct pdb_reader *pdb, struct module *module, HANDLE file) { enum pdb_result result; @@ -416,6 +427,7 @@ static enum pdb_result pdb_reader_init(struct pdb_reader *pdb, struct module *mo } /* hack (must be set before loading debug info so it can be used therein) */ pdb->module->ops_symref_modfmt = module->format_info[DFI_PDB]; + pdb_reader_init_DBI(pdb); return R_PDB_SUCCESS; @@ -642,6 +654,28 @@ static enum pdb_result pdb_reader_read_DBI_header(struct pdb_reader* pdb, PDB_SY return R_PDB_SUCCESS; } +static UINT32 codeview_compute_hash(const char* ptr, unsigned len) +{ + const char* last = ptr + len; + UINT32 ret = 0; + + while (ptr + sizeof(UINT32) <= last) + { + ret ^= *(UINT32*)ptr; + ptr += sizeof(UINT32); + } + if (ptr + sizeof(UINT16) <= last) + { + ret ^= *(UINT16*)ptr; + ptr += sizeof(UINT16); + } + if (ptr + sizeof(BYTE) <= last) + ret ^= *(BYTE*)ptr; + ret |= 0x20202020; + ret ^= (ret >> 11); + return ret ^ (ret >> 16); +} + static enum pdb_result pdb_reader_read_DBI_cu_header(struct pdb_reader* pdb, DWORD dbi_header_version, struct pdb_reader_walker *walker, PDB_SYMBOL_FILE_EX *dbi_cu_header) @@ -1404,9 +1438,191 @@ static enum pdb_result pdb_reader_alloc_and_read_full_codeview_symbol(struct pdb return pdb_reader_alloc_and_read_full_blob(pdb, walker, (void **)cv_symbol); } -static void pdb_method_location_compute(const struct module_format *modfmt, - const struct symt_function *func, - struct location *loc) +static enum pdb_result pdb_reader_load_DBI_hash_table(struct pdb_reader *pdb) +{ + enum pdb_result result; + struct pdb_reader_walker walker; + DBI_HASH_HEADER dbi_hash_header; + unsigned num_hash_records; + DBI_HASH_RECORD hash_record; + UINT32 bitmap; + UINT32 start_index, end_index; + unsigned index, last_index, i, j; + struct pdb_dbi_hash_entry *entry; + + if ((result = pdb_reader_walker_init(pdb, pdb->dbi_header.global_hash_stream, &walker))) return result; + if ((result = pdb_reader_READ(pdb, &walker, &dbi_hash_header))) return result; + if (dbi_hash_header.signature != 0xFFFFFFFF || + dbi_hash_header.version != 0xeffe0000 + 19990810) + { + WARN("Incorrect hash stream header\n"); + return R_PDB_INVALID_PDB_FILE; + } + if (dbi_hash_header.hash_records_size) + { + if ((dbi_hash_header.hash_records_size % sizeof(DBI_HASH_RECORD)) != 0 || + sizeof(DBI_HASH_HEADER) + dbi_hash_header.hash_records_size + DBI_BITMAP_HASH_SIZE > walker.last || + (walker.last - (sizeof(DBI_HASH_HEADER) + dbi_hash_header.hash_records_size + DBI_BITMAP_HASH_SIZE)) % sizeof(uint32_t)) + { + WARN("Incorrect hash structure\n"); + return R_PDB_INVALID_PDB_FILE; + } + } + + if ((result = pdb_reader_alloc(pdb, sizeof(pdb->dbi_symbols_hash[0]) * (DBI_MAX_HASH + 1), (void **)&pdb->dbi_symbols_hash))) return result; + memset(pdb->dbi_symbols_hash, 0, sizeof(pdb->dbi_symbols_hash[0]) * (DBI_MAX_HASH + 1)); + for (index = 0, i = 0; i <= DBI_MAX_HASH; i++) + pdb->dbi_symbols_hash[i].next = &pdb->dbi_symbols_hash[i]; + + if (!dbi_hash_header.hash_records_size) return R_PDB_SUCCESS; + num_hash_records = dbi_hash_header.hash_records_size / sizeof(DBI_HASH_RECORD); + last_index = (walker.last - (sizeof(DBI_HASH_HEADER) + dbi_hash_header.hash_records_size + DBI_BITMAP_HASH_SIZE)) / sizeof(UINT32); + + for (index = 0, i = 0; i <= DBI_MAX_HASH; i++) + { + if ((i & 31) == 0) + { + walker.offset = sizeof(DBI_HASH_HEADER) + dbi_hash_header.hash_records_size + (i / 32) * 4; + if ((result = pdb_reader_READ(pdb, &walker, &bitmap))) goto on_error; + } + if (bitmap & (1u << (i % 32))) + { + walker.offset = sizeof(DBI_HASH_HEADER) + dbi_hash_header.hash_records_size + DBI_BITMAP_HASH_SIZE + index * sizeof(UINT32); + /* Yes, offsets for accessing the hash_record:s are stored as multiple of 12; + * and not as multiple of sizeof(hash_record) = 8 as one might expect. + * Perhaps, native implementation likes to keep the same offsets between + * in memory representation vs on file representations. + */ + if ((result = pdb_reader_READ(pdb, &walker, &start_index))) goto on_error; + start_index /= 12; + if (index + 1 < last_index) + { + if ((result = pdb_reader_READ(pdb, &walker, &end_index))) goto on_error; + end_index /= 12; + } + else + end_index = num_hash_records; + index++; + + for (j = start_index; j < end_index; j++) + { + walker.offset = sizeof(DBI_HASH_HEADER) + j * sizeof(DBI_HASH_RECORD); + if ((result = pdb_reader_READ(pdb, &walker, &hash_record))) goto on_error; + if (pdb->dbi_symbols_hash[i].next == &pdb->dbi_symbols_hash[i]) /* empty slot */ + { + pdb->dbi_symbols_hash[i].dbi_stream_offset = hash_record.offset - 1; + pdb->dbi_symbols_hash[i].next = NULL; + } + else + { + struct pdb_dbi_hash_entry **last; + if ((result = pdb_reader_alloc(pdb, sizeof(*entry), (void **)&entry))) goto on_error; + entry->dbi_stream_offset = hash_record.offset - 1; + entry->next = NULL; + for (last = &pdb->dbi_symbols_hash[i].next; *last; last = &(*last)->next) {} + *last = entry; + } + } + } + } + return R_PDB_SUCCESS; +on_error: + for (i = 0; i <= DBI_MAX_HASH; i++) + { + struct pdb_dbi_hash_entry *current, *next; + if (pdb->dbi_symbols_hash[i].next == &pdb->dbi_symbols_hash[i]) continue; + for (current = pdb->dbi_symbols_hash[i].next; current; current = next) + { + next = current->next; + pdb_reader_free(pdb, current); + } + } + pdb_reader_free(pdb, pdb->dbi_symbols_hash); + pdb->dbi_symbols_hash = NULL; + return result; +} + +static enum pdb_result pdb_reader_extract_name_out_of_codeview_symbol(union codeview_symbol *cv_symbol, char **name, size_t *length) +{ + switch (cv_symbol->generic.id) + { + case S_UDT: + *name = cv_symbol->udt_v3.name; + break; + default: + return R_PDB_INVALID_ARGUMENT; + } + *length = strlen(*name); + return R_PDB_SUCCESS; +} + +static enum pdb_result pdb_reader_read_DBI_codeview_symbol_by_name(struct pdb_reader *pdb, const char *name, + pdbsize_t *stream_offset, union codeview_symbol *cv_symbol) +{ + enum pdb_result result; + UINT32 hash; + struct pdb_reader_walker walker; + union codeview_symbol *full_cv_symbol; + char *cv_name; + size_t cv_length; + + if ((result = pdb_reader_walker_init(pdb, pdb->dbi_header.gsym_stream, &walker))) return result; + hash = codeview_compute_hash(name, strlen(name)) % DBI_MAX_HASH; + if (pdb->dbi_symbols_hash[hash].next != &pdb->dbi_symbols_hash[hash]) + { + struct pdb_dbi_hash_entry *entry; + for (entry = &pdb->dbi_symbols_hash[hash]; entry; entry = entry->next) + { + BOOL match; + + walker.offset = entry->dbi_stream_offset; + if ((result = pdb_reader_alloc_and_read_full_codeview_symbol(pdb, &walker, &full_cv_symbol))) return result; + match = pdb_reader_extract_name_out_of_codeview_symbol(full_cv_symbol, &cv_name, &cv_length) == R_PDB_SUCCESS && + !strcmp(name, cv_name); + pdb_reader_free(pdb, full_cv_symbol); + if (match) + { + *cv_symbol = *full_cv_symbol; + *stream_offset = entry->dbi_stream_offset; + return R_PDB_SUCCESS; + } + } + } + TRACE("not found in hash bucket %s\n", debugstr_a(name)); + return R_PDB_NOT_FOUND; +} + +static enum pdb_result pdb_reader_init_DBI(struct pdb_reader *pdb) +{ + enum pdb_result result; + struct pdb_reader_walker walker; + union codeview_symbol cv_symbol; + symref_t symref; + + if ((result = pdb_reader_read_DBI_header(pdb, &pdb->dbi_header, &walker))) return result; + + /* register the globals entries not bound to a compiland */ + if ((result = pdb_reader_walker_init(pdb, pdb->dbi_header.gsym_stream, &walker))) return result; + while (pdb_reader_READ(pdb, &walker, &cv_symbol.generic) == R_PDB_SUCCESS) + { + switch (cv_symbol.generic.id) + { + case S_UDT: + if ((result = pdb_reader_push_action(pdb, action_type_globals, walker.offset - sizeof(cv_symbol.generic), + cv_symbol.generic.len + sizeof(cv_symbol.generic.len), 0, &symref))) return result; + } + walker.offset += cv_symbol.generic.len - sizeof(cv_symbol.generic.id); + } + pdb->num_action_globals = pdb->num_action_entries; + + if ((result = pdb_reader_load_DBI_hash_table(pdb))) return result; + + return R_PDB_SUCCESS; +} + +static void pdb_method_location_compute(const struct module_format* modfmt, + const struct symt_function* func, + struct location* loc) { enum pdb_result result; struct pdb_reader_walker walker; @@ -1965,28 +2181,6 @@ static enum pdb_result pdb_reader_TPI_alloc_and_read_full_reftype(struct pdb_rea return pdb_reader_alloc_and_read_full_blob(pdb, &walker, (void**)cv_reftype); } -static UINT32 codeview_compute_hash(const char* ptr, unsigned len) -{ - const char* last = ptr + len; - UINT32 ret = 0; - - while (ptr + sizeof(UINT32) <= last) - { - ret ^= *(UINT32*)ptr; - ptr += sizeof(UINT32); - } - if (ptr + sizeof(UINT16) <= last) - { - ret ^= *(UINT16*)ptr; - ptr += sizeof(UINT16); - } - if (ptr + sizeof(BYTE) <= last) - ret ^= *(BYTE*)ptr; - ret |= 0x20202020; - ret ^= (ret >> 11); - return ret ^ (ret >> 16); -} - static enum pdb_result pdb_reader_read_codeview_type_by_name(struct pdb_reader *pdb, const char *name, struct pdb_reader_walker *walker, union codeview_type *cv_type, cv_typ_t *cv_typeid) { @@ -2025,6 +2219,16 @@ static enum pdb_result pdb_reader_read_codeview_type_by_name(struct pdb_reader * return R_PDB_NOT_FOUND; } +static int my_action_global_cmp(const void *p1, const void *p2) +{ + pdbsize_t o1 = *(pdbsize_t*)p1; + pdbsize_t o2 = ((const struct pdb_action_entry *)p2)->stream_offset; + + if (o1 < o2) return -1; + if (o1 > o2) return +1; + return 0; +} + static enum method_result pdb_method_find_type(struct module_format *modfmt, const char *name, symref_t *ref) { struct pdb_reader *pdb; @@ -2032,14 +2236,30 @@ static enum method_result pdb_method_find_type(struct module_format *modfmt, con struct pdb_reader_walker walker; cv_typ_t cv_typeid; union codeview_type cv_type; + union codeview_symbol cv_symbol; struct symref_code code; struct pdb_type_details *type_details; + pdbsize_t stream_offset; if (!pdb_hack_get_main_info(modfmt, &pdb, NULL)) return MR_FAILURE; if ((result = pdb_reader_init_TPI(pdb))) return pdb_method_result(result); - if ((result = pdb_reader_read_codeview_type_by_name(pdb, name, &walker, &cv_type, &cv_typeid))) return pdb_method_result(result); - if ((result = pdb_reader_get_type_details(pdb, cv_typeid, &type_details))) return pdb_method_result(result); - return pdb_reader_encode_symref(pdb, symref_code_init_from_cv_typeid(&code, cv_typeid), ref) == R_PDB_SUCCESS ? MR_SUCCESS : MR_FAILURE; + /* search in TPI hash table */ + if ((result = pdb_reader_read_codeview_type_by_name(pdb, name, &walker, &cv_type, &cv_typeid)) == R_PDB_SUCCESS) + { + if ((result = pdb_reader_get_type_details(pdb, cv_typeid, &type_details))) return MR_FAILURE; + return pdb_reader_encode_symref(pdb, symref_code_init_from_cv_typeid(&code, cv_typeid), ref) == R_PDB_SUCCESS ? MR_SUCCESS : MR_FAILURE; + } + /* search in DBI globals' hash table */ + if ((result = pdb_reader_read_DBI_codeview_symbol_by_name(pdb, name, &stream_offset, &cv_symbol)) == R_PDB_SUCCESS) + { + struct pdb_action_entry *entry; + entry = bsearch(&stream_offset, pdb->action_store, pdb->num_action_globals, sizeof(pdb->action_store[0]), + &my_action_global_cmp); + if (entry) + return pdb_reader_encode_symref(pdb, symref_code_init_from_action(&code, entry - pdb->action_store), + ref) == R_PDB_SUCCESS ? MR_SUCCESS : MR_FAILURE; + } + return MR_FAILURE; } static BOOL codeview_type_is_forward(const union codeview_type* cvtype) @@ -2167,7 +2387,39 @@ static enum method_result pdb_method_enumerate_types(struct module_format *modfm } } } - return MR_NOT_FOUND; /* hack: as typedef are not migrated yet, ask to continue searching */ + + /* typedef:s are stored in DBI globals' stream */ + for (i = 0; i < pdb->num_action_globals; i++) + { + struct pdb_reader_walker walker; + union codeview_symbol *cv_symbol; + struct pdb_action_entry *entry; + pdbsize_t num_read; + + entry = &pdb->action_store[i]; + if ((result = pdb_reader_walker_init(pdb, pdb->dbi_header.gsym_stream, &walker))) return MR_FAILURE; + walker.offset = entry->stream_offset; + if ((result = pdb_reader_alloc(pdb, entry->action_length, (void**)&cv_symbol))) return MR_FAILURE; + if ((result = pdb_reader_read_from_stream(pdb, &walker, cv_symbol, entry->action_length, &num_read))) return MR_FAILURE; + if ((result = pdb_reader_encode_symref(pdb, symref_code_init_from_action(&code, i), &symref))) return MR_FAILURE; + if (num_read == entry->action_length) + { + switch (cv_symbol->generic.id) + { + case S_UDT: + ret = (*cb)(symref, cv_symbol->udt_v3.name, user); + break; + default: + WARN("Got unexpected %x\n", cv_symbol->generic.id); + ret = FALSE; + break; + } + } + else ret = TRUE; + pdb_reader_free(pdb, cv_symbol); + if (!ret) break; + } + return MR_SUCCESS; } static enum pdb_result pdb_reader_index_from_cv_typeid(struct pdb_reader *pdb, cv_typ_t cv_typeid, DWORD *index) @@ -2846,6 +3098,59 @@ static enum method_result pdb_reader_TPI_field_request(struct pdb_reader *pdb, s return ret; } +static enum method_result pdb_reader_DBI_typedef_request(struct pdb_reader *pdb, struct pdb_action_entry *entry, union codeview_symbol *cv_symbol, + IMAGEHLP_SYMBOL_TYPE_INFO req, void *data) +{ + switch (req) + { + case TI_GET_SYMTAG: + *((DWORD*)data) = SymTagTypedef; + return MR_SUCCESS; + case TI_GET_SYMNAME: + *((WCHAR **)data) = heap_allocate_symname(cv_symbol->udt_v3.name); + return *((WCHAR **)data) != NULL ? MR_SUCCESS : MR_FAILURE; + case TI_GET_LENGTH: + return pdb_reader_request_cv_typeid(pdb, cv_symbol->udt_v3.type, req, data) == R_PDB_SUCCESS ? MR_SUCCESS : MR_FAILURE; + case TI_GET_TYPE: + case TI_GET_TYPEID: + return pdb_reader_index_from_cv_typeid(pdb, cv_symbol->udt_v3.type, (DWORD*)data) == R_PDB_SUCCESS ? MR_SUCCESS : MR_FAILURE; + case TI_FINDCHILDREN: + case TI_GET_CHILDRENCOUNT: + case TI_GET_LEXICALPARENT: + return pdb_reader_default_request(pdb, req, data); + default: + return MR_FAILURE; + } +} + +static enum method_result pdb_reader_DBI_globals_request(struct pdb_reader *pdb, struct pdb_action_entry *entry, IMAGEHLP_SYMBOL_TYPE_INFO req, void *data) +{ + enum pdb_result result; + struct pdb_reader_walker walker; + union codeview_symbol *cv_symbol; + pdbsize_t num_read; + enum method_result ret = MR_FAILURE; + + if ((result = pdb_reader_walker_init(pdb, pdb->dbi_header.gsym_stream, &walker))) return MR_FAILURE; + walker.offset = entry->stream_offset; + if ((result = pdb_reader_alloc(pdb, entry->action_length, (void**)&cv_symbol))) return MR_FAILURE; + if ((result = pdb_reader_read_from_stream(pdb, &walker, cv_symbol, entry->action_length, &num_read))) return MR_FAILURE; + if (num_read == entry->action_length) + { + switch (cv_symbol->generic.id) + { + case S_UDT: + ret = pdb_reader_DBI_typedef_request(pdb, entry, cv_symbol, req, data); + break; + default: + WARN("Got unexpected %x\n", cv_symbol->generic.id); + break; + } + } + pdb_reader_free(pdb, cv_symbol); + return ret; +} + static enum method_result pdb_reader_TPI_request(struct pdb_reader *pdb, symref_t symref, struct pdb_type_details *type_details, IMAGEHLP_SYMBOL_TYPE_INFO req, void *data) { enum pdb_result result; @@ -2921,6 +3226,8 @@ static enum method_result pdb_reader_request_symref_t(struct pdb_reader *pdb, sy return pdb_reader_TPI_argtype_request(pdb, entry, req, data); case action_type_field: return pdb_reader_TPI_field_request(pdb, entry, req, data); + case action_type_globals: + return pdb_reader_DBI_globals_request(pdb, entry, req, data); default: return MR_FAILURE; } diff --git a/dlls/dbghelp/type.c b/dlls/dbghelp/type.c index 2d7285419980..b4c1a414d99c 100644 --- a/dlls/dbghelp/type.c +++ b/dlls/dbghelp/type.c @@ -485,7 +485,6 @@ static BOOL sym_enum_types(struct module_pair *pair, const char *type_name, PSYM void* ptr; struct symt_ht *type; DWORD64 size; - BOOL hack_only_typedef = FALSE; sym_info->SizeOfStruct = sizeof(SYMBOL_INFO); sym_info->MaxNameLen = sizeof(buffer) - sizeof(SYMBOL_INFO); @@ -496,17 +495,7 @@ static BOOL sym_enum_types(struct module_pair *pair, const char *type_name, PSYM { struct sym_modfmt_type_enum info = {pair->effective, sym_info, cb, user, type_name}; enum method_result result = iter.modfmt->vtable->enumerate_types(iter.modfmt, sym_modfmt_type_enum_cb, &info); - - switch (result) - { - case MR_FAILURE: - return FALSE; - case MR_SUCCESS: - return TRUE; - case MR_NOT_FOUND: - hack_only_typedef = TRUE; - break; - } + return result != MR_FAILURE; } hash_table_iter_init(&pair->effective->ht_types, &hti, type_name); @@ -515,7 +504,6 @@ static BOOL sym_enum_types(struct module_pair *pair, const char *type_name, PSYM type = CONTAINING_RECORD(ptr, struct symt_ht, hash_elt); if (type_name && !SymMatchStringA(type->hash_elt.name, type_name, TRUE)) continue; - if (hack_only_typedef && !symt_check_tag(&type->symt, SymTagTypedef)) continue; sym_info->TypeIndex = symt_ptr_to_index(pair->effective, &type->symt); sym_info->Index = 0; /* FIXME */ @@ -1207,7 +1195,6 @@ BOOL WINAPI SymGetTypeFromName(HANDLE hProcess, ULONG64 BaseOfDll, struct module_pair pair; struct symt* type; DWORD64 size; - BOOL hack_only_typedef = FALSE; struct module_format_vtable_iterator iter = {}; if (!module_init_pair(&pair, hProcess, BaseOfDll)) return FALSE; @@ -1231,12 +1218,11 @@ BOOL WINAPI SymGetTypeFromName(HANDLE hProcess, ULONG64 BaseOfDll, symt_get_info_from_symref(pair.effective, symref, TI_GET_SYMTAG, &Symbol->Tag); return TRUE; } - hack_only_typedef = TRUE; + if (result == MR_FAILURE) return FALSE; } type = symt_find_type_by_name(pair.effective, SymTagNull, Name); if (!type) return FALSE; - if (hack_only_typedef && !symt_check_tag(type, SymTagTypedef)) return FALSE; Symbol->Index = Symbol->TypeIndex = symt_ptr_to_index(pair.effective, type); symbol_setname(Symbol, symt_get_name(type)); symt_get_info(pair.effective, type, TI_GET_LENGTH, &size); From 83d29cf671e0fcb1392596da948d4e95120d00b6 Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Thu, 23 Jan 2025 15:20:55 +0100 Subject: [PATCH 266/454] dbghelp: Add user field to function and inline sites. And store DBI offset to function from old PDB backend (to be used by new PDB backend during migration). Signed-off-by: Eric Pouech (cherry picked from commit 5dcdaa170d3df092c0b39abe8cad3b2ac6c8c7dc) --- dlls/dbghelp/coff.c | 6 +++--- dlls/dbghelp/dbghelp_private.h | 4 +++- dlls/dbghelp/dwarf.c | 4 ++-- dlls/dbghelp/elf_module.c | 2 +- dlls/dbghelp/macho_module.c | 2 +- dlls/dbghelp/msc.c | 15 +++++++++------ dlls/dbghelp/stabs.c | 4 ++-- dlls/dbghelp/symbol.c | 16 ++++++++++------ 8 files changed, 31 insertions(+), 22 deletions(-) diff --git a/dlls/dbghelp/coff.c b/dlls/dbghelp/coff.c index aa7273db24e3..65ed3b7def20 100644 --- a/dlls/dbghelp/coff.c +++ b/dlls/dbghelp/coff.c @@ -285,7 +285,7 @@ BOOL coff_process_info(const struct msc_debug_info* msc_dbg) nampnt, msc_dbg->module->module.BaseOfImage + base + coff_sym->Value, 0 /* FIXME */, - 0 /* FIXME */)->symt); + 0 /* FIXME */, 0)->symt); continue; } @@ -318,13 +318,13 @@ BOOL coff_process_info(const struct msc_debug_info* msc_dbg) coff_add_symbol(&coff_files.files[j], &symt_new_function(msc_dbg->module, compiland, nampnt, msc_dbg->module->module.BaseOfImage + base + coff_sym->Value, - 0 /* FIXME */, 0 /* FIXME */)->symt); + 0 /* FIXME */, 0 /* FIXME */, 0)->symt); } else { symt_new_function(msc_dbg->module, NULL, nampnt, msc_dbg->module->module.BaseOfImage + base + coff_sym->Value, - 0 /* FIXME */, 0 /* FIXME */); + 0 /* FIXME */, 0 /* FIXME */, 0); } i += naux; continue; diff --git a/dlls/dbghelp/dbghelp_private.h b/dlls/dbghelp/dbghelp_private.h index 4470420dd4f3..6c0eabd62991 100644 --- a/dlls/dbghelp/dbghelp_private.h +++ b/dlls/dbghelp/dbghelp_private.h @@ -299,6 +299,7 @@ struct symt_function struct vector vlines; struct vector vchildren; /* locals, params, blocks, start/end, labels, inline sites */ struct symt_function* next_inlinesite;/* linked list of inline sites in this function */ + DWORD_PTR user; /* free to use by debug info backends */ unsigned num_ranges; struct addr_range ranges[]; }; @@ -922,13 +923,14 @@ extern struct symt_function* struct symt_compiland* parent, const char* name, ULONG_PTR addr, ULONG_PTR size, - symref_t type); + symref_t type, DWORD_PTR user); extern struct symt_function* symt_new_inlinesite(struct module* module, struct symt_function* func, struct symt* parent, const char* name, symref_t type, + DWORD_PTR user, unsigned num_ranges); extern void symt_add_func_line(struct module* module, struct symt_function* func, diff --git a/dlls/dbghelp/dwarf.c b/dlls/dbghelp/dwarf.c index cc4e4b360e78..ea79a636078a 100644 --- a/dlls/dbghelp/dwarf.c +++ b/dlls/dbghelp/dwarf.c @@ -2242,7 +2242,7 @@ static void dwarf2_parse_inlined_subroutine(dwarf2_subprogram_t* subpgm, subpgm->top_func, subpgm->current_block ? &subpgm->current_block->symt : &subpgm->current_func->symt, dwarf2_get_cpp_name(di, name.u.string), - symt_ptr_to_symref(dwarf2_parse_subroutine_type(di)), num_ranges); + symt_ptr_to_symref(dwarf2_parse_subroutine_type(di)), 0, num_ranges); subpgm->current_func = inlined; subpgm->current_block = NULL; @@ -2452,7 +2452,7 @@ static struct symt* dwarf2_parse_subprogram(dwarf2_debug_info_t* di) subpgm.top_func = symt_new_function(di->unit_ctx->module_ctx->module, di->unit_ctx->compiland, dwarf2_get_cpp_name(di, name.u.string), addr_ranges[0].low, addr_ranges[0].high - addr_ranges[0].low, - symt_ptr_to_symref(dwarf2_parse_subroutine_type(di))); + symt_ptr_to_symref(dwarf2_parse_subroutine_type(di)), 0); if (num_addr_ranges > 1) WARN("Function %s has multiple address ranges, only using the first one\n", debugstr_a(name.u.string)); free(addr_ranges); diff --git a/dlls/dbghelp/elf_module.c b/dlls/dbghelp/elf_module.c index dc06c5f74273..ac2d25af25c2 100644 --- a/dlls/dbghelp/elf_module.c +++ b/dlls/dbghelp/elf_module.c @@ -960,7 +960,7 @@ static int elf_new_wine_thunks(struct module* module, const struct hash_table* h { case ELF_STT_FUNC: symt_new_function(module, ste->compiland, ste->ht_elt.name, - addr, ste->sym.st_size, 0); + addr, ste->sym.st_size, 0, 0); break; case ELF_STT_OBJECT: loc.kind = loc_absolute; diff --git a/dlls/dbghelp/macho_module.c b/dlls/dbghelp/macho_module.c index 14ef358697da..2b1f35c4d784 100644 --- a/dlls/dbghelp/macho_module.c +++ b/dlls/dbghelp/macho_module.c @@ -1190,7 +1190,7 @@ static void macho_finish_stabs(struct module* module, struct hash_table* ht_symt if (ste->is_code) { symt_new_function(module, ste->compiland, ste->ht_elt.name, - ste->addr, 0, 0); + ste->addr, 0, 0, 0); } else { diff --git a/dlls/dbghelp/msc.c b/dlls/dbghelp/msc.c index 8b346f0043f0..d9a4934696de 100644 --- a/dlls/dbghelp/msc.c +++ b/dlls/dbghelp/msc.c @@ -2188,6 +2188,7 @@ static struct symt_function* codeview_create_inline_site(const struct msc_debug_ struct symt_function* top_func, struct symt* container, cv_itemid_t inlinee, + DWORD_PTR user, const unsigned char* annot, const unsigned char* last_annot) { @@ -2214,14 +2215,14 @@ static struct symt_function* codeview_create_inline_site(const struct msc_debug_ inlined = symt_new_inlinesite(msc_dbg->module, top_func, container, cvt->func_id_v3.name, codeview_get_symref(msc_dbg->module, cvt->func_id_v3.type), - num_ranges); + user, num_ranges); break; case LF_MFUNC_ID: /* FIXME we just declare a function, not a method */ inlined = symt_new_inlinesite(msc_dbg->module, top_func, container, cvt->mfunc_id_v3.name, codeview_get_symref(msc_dbg->module, cvt->mfunc_id_v3.type), - num_ranges); + user, num_ranges); break; default: FIXME("unsupported inlinee kind %x\n", cvt->generic.id); @@ -2438,7 +2439,7 @@ static BOOL codeview_snarf(const struct msc_debug_info* msc_dbg, terminate_string(&sym->proc_v1.p_name), codeview_get_address(msc_dbg, sym->proc_v1.segment, sym->proc_v1.offset), sym->proc_v1.proc_len, - codeview_get_symref(msc_dbg->module, sym->proc_v1.proctype)))) + codeview_get_symref(msc_dbg->module, sym->proc_v1.proctype), i))) { curr_func = top_func; loc.kind = loc_absolute; @@ -2455,7 +2456,7 @@ static BOOL codeview_snarf(const struct msc_debug_info* msc_dbg, terminate_string(&sym->proc_v2.p_name), codeview_get_address(msc_dbg, sym->proc_v2.segment, sym->proc_v2.offset), sym->proc_v2.proc_len, - codeview_get_symref(msc_dbg->module, sym->proc_v2.proctype)))) + codeview_get_symref(msc_dbg->module, sym->proc_v2.proctype), i))) { curr_func = top_func; loc.kind = loc_absolute; @@ -2472,7 +2473,7 @@ static BOOL codeview_snarf(const struct msc_debug_info* msc_dbg, sym->proc_v3.name, codeview_get_address(msc_dbg, sym->proc_v3.segment, sym->proc_v3.offset), sym->proc_v3.proc_len, - codeview_get_symref(msc_dbg->module, sym->proc_v3.proctype)))) + codeview_get_symref(msc_dbg->module, sym->proc_v3.proctype), i))) { curr_func = top_func; loc.kind = loc_absolute; @@ -2742,6 +2743,7 @@ static BOOL codeview_snarf(const struct msc_debug_info* msc_dbg, struct symt_function* inlined = codeview_create_inline_site(msc_dbg, cvmod, top_func, block ? &block->symt : &curr_func->symt, sym->inline_site_v3.inlinee, + i, sym->inline_site_v3.binaryAnnotations, (const unsigned char*)sym + length); if (inlined) @@ -2763,6 +2765,7 @@ static BOOL codeview_snarf(const struct msc_debug_info* msc_dbg, struct symt_function* inlined = codeview_create_inline_site(msc_dbg, cvmod, top_func, block ? &block->symt : &curr_func->symt, sym->inline_site2_v3.inlinee, + i, sym->inline_site2_v3.binaryAnnotations, (const unsigned char*)sym + length); if (inlined) @@ -2826,7 +2829,7 @@ static BOOL codeview_snarf(const struct msc_debug_info* msc_dbg, struct symt_function* pfunc = (struct symt_function*)parent; top_func = symt_new_function(msc_dbg->module, compiland, pfunc->hash_elt.name, codeview_get_address(msc_dbg, sym->sepcode_v3.sect, sym->sepcode_v3.off), - sym->sepcode_v3.length, pfunc->type); + sym->sepcode_v3.length, pfunc->type, i); curr_func = top_func; } else diff --git a/dlls/dbghelp/stabs.c b/dlls/dbghelp/stabs.c index efd2bb2f9432..ea5bed152e6a 100644 --- a/dlls/dbghelp/stabs.c +++ b/dlls/dbghelp/stabs.c @@ -1547,11 +1547,11 @@ BOOL stabs_parse(struct module* module, ULONG_PTR load_offset, n_value ? (load_offset + n_value - curr_func->ranges[0].low) : 0); } - func_type = symt_new_function_signature(module, + func_type = symt_new_function_signature(module, stabs_parse_type(ptr), -1); curr_func = symt_new_function(module, compiland, symname, load_offset + n_value, 0, - symt_ptr_to_symref(&func_type->symt)); + symt_ptr_to_symref(&func_type->symt), 0); pending_flush(&pending_func, module, curr_func, NULL); } else diff --git a/dlls/dbghelp/symbol.c b/dlls/dbghelp/symbol.c index ade18cc0a947..cf6bfe8c0377 100644 --- a/dlls/dbghelp/symbol.c +++ b/dlls/dbghelp/symbol.c @@ -322,18 +322,20 @@ static struct symt_function* init_function_or_inlinesite(struct module* module, struct symt* container, const char* name, symref_t sig_type, + DWORD_PTR user, unsigned num_ranges) { struct symt_function* sym; if ((sym = pool_alloc(&module->pool, offsetof(struct symt_function, ranges[num_ranges])))) { - sym->symt.tag = tag; + sym->symt.tag = tag; sym->hash_elt.name = pool_strdup(&module->pool, name); - sym->container = container; - sym->type = sig_type; + sym->container = container; + sym->type = sig_type; vector_init(&sym->vlines, sizeof(struct line_info), 0); vector_init(&sym->vchildren, sizeof(struct symt*), 0); + sym->user = user; sym->num_ranges = num_ranges; } return sym; @@ -343,13 +345,14 @@ struct symt_function* symt_new_function(struct module* module, struct symt_compiland* compiland, const char* name, ULONG_PTR addr, ULONG_PTR size, - symref_t sig_type) + symref_t sig_type, DWORD_PTR user) { struct symt_function* sym; TRACE_(dbghelp_symt)("Adding global function %s:%s @%Ix-%Ix\n", debugstr_w(module->modulename), debugstr_a(name), addr, addr + size - 1); - if ((sym = init_function_or_inlinesite(module, SymTagFunction, &compiland->symt, name, sig_type, 1))) + + if ((sym = init_function_or_inlinesite(module, SymTagFunction, &compiland->symt, name, sig_type, user, 1))) { struct symt** p; sym->ranges[0].low = addr; @@ -370,12 +373,13 @@ struct symt_function* symt_new_inlinesite(struct module* module, struct symt* container, const char* name, symref_t sig_type, + DWORD_PTR user, unsigned num_ranges) { struct symt_function* sym; TRACE_(dbghelp_symt)("Adding inline site %s\n", debugstr_a(name)); - if ((sym = init_function_or_inlinesite(module, SymTagInlineSite, container, name, sig_type, num_ranges))) + if ((sym = init_function_or_inlinesite(module, SymTagInlineSite, container, name, sig_type, user, num_ranges))) { struct symt** p; assert(container); From 63814af1fe435cf1f3b9e6d28cee78fd4f836602 Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Fri, 24 Jan 2025 11:33:14 +0100 Subject: [PATCH 267/454] dbghelp: Move reading inlinee line number information to PDB backend. Signed-off-by: Eric Pouech (cherry picked from commit e6c96072cec4380863181f789fcba8adcd6b9003) --- dlls/dbghelp/dbghelp_private.h | 2 + dlls/dbghelp/pdb.c | 444 ++++++++++++++++++++++++++++++++- dlls/dbghelp/symbol.c | 19 ++ 3 files changed, 464 insertions(+), 1 deletion(-) diff --git a/dlls/dbghelp/dbghelp_private.h b/dlls/dbghelp/dbghelp_private.h index 6c0eabd62991..88573cfccdb6 100644 --- a/dlls/dbghelp/dbghelp_private.h +++ b/dlls/dbghelp/dbghelp_private.h @@ -456,6 +456,8 @@ struct module_format_vtable struct lineinfo_t *line_info, BOOL forward); enum method_result (*enumerate_lines)(struct module_format *modfmt, const WCHAR* compiland_regex, const WCHAR *source_file_regex, PSYM_ENUMLINES_CALLBACK cb, void *user); + enum method_result (*get_line_from_inlined_address)(struct module_format *modfmt, struct symt_function *inlined, + DWORD64 address, struct lineinfo_t *line_info); /* source files information */ enum method_result (*enumerate_sources)(struct module_format *modfmt, const WCHAR *sourcefile_regex, diff --git a/dlls/dbghelp/pdb.c b/dlls/dbghelp/pdb.c index 555c1c6574ff..ba09f8e37ba7 100644 --- a/dlls/dbghelp/pdb.c +++ b/dlls/dbghelp/pdb.c @@ -189,6 +189,23 @@ static enum pdb_result pdb_reader_get_segment_address(struct pdb_reader *pdb, un return R_PDB_SUCCESS; } +static enum pdb_result pdb_reader_get_segment_offset_from_address(struct pdb_reader *pdb, DWORD64 address, unsigned *segment, unsigned *offset) +{ + unsigned i; + + for (i = 0; i < pdb->num_sections; i++) + { + if (address >= pdb->module->module.BaseOfImage + pdb->sections[i].VirtualAddress && + address < pdb->module->module.BaseOfImage + pdb->sections[i].VirtualAddress + pdb->sections[i].Misc.VirtualSize) + { + *segment = i + 1; + *offset = address - (pdb->module->module.BaseOfImage + pdb->sections[i].VirtualAddress); + return R_PDB_SUCCESS; + } + } + return R_PDB_NOT_FOUND; +} + static inline enum pdb_result pdb_reader_alloc(struct pdb_reader *pdb, size_t size, void **ptr) { return (*ptr = pool_alloc(&pdb->pool, size)) ? R_PDB_SUCCESS : R_PDB_OUT_OF_MEMORY; @@ -371,6 +388,9 @@ static enum pdb_result pdb_reader_push_action(struct pdb_reader *pdb, enum pdb_a } static enum pdb_result pdb_reader_init_DBI(struct pdb_reader *pdb); +static enum pdb_result pdb_reader_internal_binary_search(size_t num_elt, + enum pdb_result (*cmp)(unsigned idx, int *cmp_ressult, void *user), + size_t *found, void *user); static enum pdb_result pdb_reader_init(struct pdb_reader *pdb, struct module *module, HANDLE file) { @@ -730,6 +750,74 @@ static enum pdb_result pdb_reader_compiland_iterator_next(struct pdb_reader *pdb return pdb_reader_read_DBI_cu_header(pdb, iter->dbi_header.version, &iter->dbi_walker, &iter->dbi_cu_header); } +struct pdb_compiland_lookup +{ + struct pdb_reader *pdb; + struct pdb_reader_walker walker; + unsigned segment; + unsigned offset; + unsigned range_size; + PDB_SYMBOL_RANGE_EX range; +}; + +static enum pdb_result pdb_reader_contrib_range_cmp(unsigned idx, int *cmp, void *user) +{ + enum pdb_result result; + struct pdb_compiland_lookup *lookup = user; + struct pdb_reader_walker walker = lookup->walker; + + walker.offset += idx * lookup->range_size; + if ((result = pdb_reader_READ(lookup->pdb, &walker, &lookup->range))) return result; + *cmp = lookup->range.segment - lookup->segment; + if (!*cmp) + *cmp = lookup->range.offset - lookup->offset; + return R_PDB_SUCCESS; +} + +static enum pdb_result pdb_reader_lookup_compiland_by_address(struct pdb_reader *pdb, DWORD_PTR address, unsigned *compiland) +{ + enum pdb_result result; + struct pdb_compiland_lookup lookup = {.pdb = pdb}; + UINT32 version; + PDB_SYMBOLS dbi_header; + size_t found; + unsigned num_ranges; + + if ((result = pdb_reader_get_segment_offset_from_address(pdb, address, &lookup.segment, &lookup.offset))) return result; + if ((result = pdb_reader_walker_init(pdb, PDB_STREAM_DBI, &lookup.walker))) return result; + if ((result = pdb_reader_read_DBI_header(pdb, &dbi_header, &lookup.walker))) return result; + if ((result = pdb_reader_walker_narrow(&lookup.walker, lookup.walker.offset + dbi_header.module_size, dbi_header.sectcontrib_size))) return result; + + if ((result = pdb_reader_READ(pdb, &lookup.walker, &version))) return result; + lookup.range_size = sizeof(PDB_SYMBOL_RANGE_EX); + switch (version) + { + case 0xeffe0000 + 19970605: break; + case 0xeffe0000 + 20140516: lookup.range_size += sizeof(UINT32); break; + default: + WARN("Unsupported contrib version %x\n", version); + return R_PDB_INVALID_PDB_FILE; + } + if ((lookup.walker.last - lookup.walker.offset) % lookup.range_size) + { + WARN("Unexpected lookup values\n"); + return R_PDB_INVALID_PDB_FILE; + } + num_ranges = (lookup.walker.last - lookup.walker.offset) / lookup.range_size; + /* we assume contributions are stored in ascending order of segment / offset */ + result = pdb_reader_internal_binary_search(num_ranges, pdb_reader_contrib_range_cmp, &found, &lookup); + if (result) + { + if (result != R_PDB_NOT_FOUND) return result; + /* ensure address is within contribution range */ + if (lookup.segment != lookup.range.segment || + lookup.offset < lookup.range.offset || + lookup.offset >= lookup.range.offset + lookup.range.size) return R_PDB_NOT_FOUND; + } + *compiland = lookup.range.index; + return R_PDB_SUCCESS; +} + static enum pdb_result pdb_reader_subsection_next(struct pdb_reader *pdb, struct pdb_reader_walker *in_walker, enum DEBUG_S_SUBSECTION_TYPE subsection_type, struct pdb_reader_walker *sub_walker) @@ -863,8 +951,8 @@ static enum pdb_result pdb_reader_get_line_from_address_internal(struct pdb_read DWORD64 address, struct lineinfo_t *line_info, pdbsize_t *compiland_offset) { - enum pdb_result result; struct pdb_reader_compiland_iterator compiland_iter; + enum pdb_result result; if ((result = pdb_reader_compiland_iterator_init(pdb, &compiland_iter))) return result; do @@ -3256,6 +3344,359 @@ BOOL cv_hack_ptr_to_symref(struct pdb_reader *pdb, cv_typ_t cv_typeid, symref_t return pdb_reader_encode_symref(pdb, symref_code_init_from_cv_typeid(&code, cv_typeid), symref) == R_PDB_SUCCESS; } +static enum pdb_result pdb_reader_walker_from_compiland_index(struct pdb_reader *pdb, unsigned compiland, PDB_SYMBOL_FILE_EX *dbi_cu_header) +{ + enum pdb_result result; + struct pdb_reader_compiland_iterator compiland_iter; + + if ((result = pdb_reader_compiland_iterator_init(pdb, &compiland_iter))) return result; + do + { + if (!compiland--) + { + *dbi_cu_header = compiland_iter.dbi_cu_header; + return R_PDB_SUCCESS; + } + } while (pdb_reader_compiland_iterator_next(pdb, &compiland_iter) == R_PDB_SUCCESS); + return R_PDB_NOT_FOUND; +} + +/* walk the top level global symbols to find matching address */ +static enum pdb_result pdb_reader_search_codeview_symbol_by_address(struct pdb_reader *pdb, struct pdb_reader_walker *walker, + DWORD_PTR address, union codeview_symbol *cv_symbol, pdbsize_t *end_stream_offset) +{ + enum pdb_result result; + unsigned short segment; + unsigned offset, pend; + DWORD64 symbol_address; + + while (pdb_reader_read_partial_codeview_symbol(pdb, walker, cv_symbol) == R_PDB_SUCCESS && cv_symbol->generic.id) + { + switch (cv_symbol->generic.id) + { + case S_GDATA32: + case S_LDATA32: + segment = cv_symbol->data_v3.segment; + offset = cv_symbol->data_v3.offset; + pend = 0; + break; + case S_THUNK32: + segment = cv_symbol->thunk_v3.segment; + offset = cv_symbol->thunk_v3.offset; + pend = cv_symbol->thunk_v3.pend; + break; + case S_GPROC32: + case S_LPROC32: + segment = cv_symbol->proc_v3.segment; + offset = cv_symbol->proc_v3.offset; + pend = cv_symbol->proc_v3.pend; + break; + + default: + WARN("Unexpected codeview symbol id %x\n", cv_symbol->generic.id); + /* fall through */ + case S_OBJNAME: + case S_COMPILE: + case S_COMPILE2: + case S_COMPILE3: + case S_BUILDINFO: + case S_UDT: + case S_UNAMESPACE: + segment = 0; + offset = 0; + pend = 0; + break; + } + if (segment) + { + if ((result = pdb_reader_get_segment_address(pdb, segment, offset, &symbol_address))) return result; + if (address == symbol_address) + { + *end_stream_offset = pend; + return R_PDB_SUCCESS; + } + } + if (pend) /* jump to S_END, and skip it */ + { + walker->offset = pend; + if ((result = pdb_reader_read_partial_codeview_symbol(pdb, walker, cv_symbol))) return result; + if (cv_symbol->generic.id != S_END) return R_PDB_INVALID_PDB_FILE; + } + walker->offset += cv_symbol->generic.len; + } + return R_PDB_NOT_FOUND; +} + +static enum pdb_result pdb_reader_alloc_and_fetch_from_checksum(struct pdb_reader *pdb, struct pdb_reader_walker checksum_walker, + unsigned chksum_offset, char **string) +{ + enum pdb_result result; + struct CV_Checksum_t checksum; + + checksum_walker.offset += chksum_offset; + if ((result = pdb_reader_READ(pdb, &checksum_walker, &checksum))) return result; + return pdb_reader_alloc_and_fetch_global_string(pdb, checksum.strOffset, string); +} + +static enum pdb_result pdb_reader_alloc_and_fetch_from_checksum_subsection(struct pdb_reader *pdb, struct pdb_reader_walker linetab2_walker, + cv_itemid_t cv_inlinee, char **string, unsigned *line_number) +{ + enum pdb_result result; + struct pdb_reader_walker sub_walker; + struct pdb_reader_walker checksum_walker; + struct pdb_reader_walker inlineelines_walker; + + sub_walker = linetab2_walker; + if ((result = pdb_reader_subsection_next(pdb, &sub_walker, DEBUG_S_FILECHKSMS, &checksum_walker))) + { + WARN("No DEBUG_S_FILECHKSMS found\n"); + return R_PDB_MISSING_INFORMATION; + } + + for (sub_walker = linetab2_walker; !(result = pdb_reader_subsection_next(pdb, &sub_walker, DEBUG_S_INLINEELINES, &inlineelines_walker)); ) + { + UINT32 inlinee_kind; + struct CV_InlineeSourceLine_t inlsrc; + struct CV_InlineeSourceLineEx_t inlsrcex; + + if ((result = pdb_reader_READ(pdb, &inlineelines_walker, &inlinee_kind))) return result; + switch (inlinee_kind) + { + case CV_INLINEE_SOURCE_LINE_SIGNATURE: + while (!pdb_reader_READ(pdb, &inlineelines_walker, &inlsrc)) + { + if (inlsrc.inlinee == cv_inlinee) + { + if ((result = pdb_reader_alloc_and_fetch_from_checksum(pdb, checksum_walker, inlsrc.fileId, string))) return result; + *line_number = inlsrc.sourceLineNum; + return R_PDB_SUCCESS; + } + } + break; + case CV_INLINEE_SOURCE_LINE_SIGNATURE_EX: + while (!pdb_reader_READ(pdb, &inlineelines_walker, &inlsrcex)) + { + if (inlsrc.inlinee == cv_inlinee) + { + if ((result = pdb_reader_alloc_and_fetch_from_checksum(pdb, checksum_walker, inlsrcex.fileId, string))) return result; + *line_number = inlsrcex.sourceLineNum; + return R_PDB_SUCCESS; + } + inlineelines_walker.offset += inlsrcex.countOfExtraFiles * sizeof(inlsrcex.extraFileId[0]); + } + break; + default: + WARN("Unknown signature %x in INLINEELINES subsection\n", inlinee_kind); + break; + } + } + return R_PDB_NOT_FOUND; +} + +static enum pdb_result pdb_reader_uncompress_inlinesite_annotation(struct pdb_reader *pdb, struct pdb_reader_walker *walker, unsigned *value) +{ + enum pdb_result result; + unsigned res; + unsigned char ch; + unsigned i, num_shift; + + if ((result = pdb_reader_READ(pdb, walker, &ch))) return result; + + if ((ch & 0x80) == 0x00) + { + res = ch; + num_shift = 0; + } + else if ((ch & 0xC0) == 0x80) + { + res = ch & 0x3f; + num_shift = 1; + } + else if ((ch & 0xE0) == 0xC0) + { + res = (ch & 0x1f); + num_shift = 3; + } + else + { + res = (unsigned)(-1); + num_shift = 0; + } + for (i = 0; i < num_shift; i++) + { + if ((result = pdb_reader_READ(pdb, walker, &ch))) return result; + res <<= 8; + res |= ch; + } + *value = res; + return R_PDB_SUCCESS; +} + +static enum pdb_result pdb_reader_read_inlinesite_annotation(struct pdb_reader *pdb, struct pdb_reader_walker *annotation_walker, + unsigned *opcode, unsigned *arg1, unsigned *arg2) +{ + enum pdb_result result; + + if ((result = pdb_reader_uncompress_inlinesite_annotation(pdb, annotation_walker, opcode))) return result; + if (*opcode == BA_OP_Invalid) + *arg1 = *arg2 = 0; + else if (*opcode <= BA_OP_ChangeColumnEnd) + { + if ((result = pdb_reader_uncompress_inlinesite_annotation(pdb, annotation_walker, arg1))) return result; + if (*opcode == BA_OP_ChangeCodeOffsetAndLineOffset) + { + *arg2 = *arg1 >> 4; + *arg1 &= 0x0F; + } + else if (*opcode == BA_OP_ChangeCodeLengthAndCodeOffset) + { + if ((result = pdb_reader_uncompress_inlinesite_annotation(pdb, annotation_walker, arg2))) return result; + } + else *arg2 = 0; + } + else + { + WARN("Unexpected BA annotation options %x\n", *opcode); + return R_PDB_INVALID_PDB_FILE; + } + return R_PDB_SUCCESS; +} + +static inline int pdb_reader_convert_binannot_to_signed(unsigned i) +{ + return (i & 1) ? -(int)(i >> 1) : (int)(i >> 1); +} + +static enum pdb_result pdb_method_get_line_from_inlined_address_internal(struct pdb_reader *pdb, struct symt_function *inlined, + DWORD64 address, struct lineinfo_t *line_info) +{ + struct symt_function *function = symt_get_function_from_inlined(inlined); + enum pdb_result result; + PDB_SYMBOL_FILE_EX dbi_cu_header; + struct pdb_reader_walker cu_walker; + struct pdb_reader_walker linetab2_walker; + struct pdb_reader_walker inlinee_walker; + DWORD64 top_function_address; + unsigned compiland; + union codeview_symbol cv_top_function_symbol; + union codeview_symbol cv_inlinee_symbol; + pdbsize_t end_stream_offset; + cv_itemid_t cv_inlinee; + size_t annotation_offset; + char *source_file_name; + unsigned line_number; + unsigned opcode, arg1, arg2; + unsigned offset_top_function; + + if (!symt_get_info(pdb->module, &function->symt, TI_GET_ADDRESS, &top_function_address)) return R_PDB_INVALID_ARGUMENT; + if ((result = pdb_reader_lookup_compiland_by_address(pdb, top_function_address, &compiland))) return result; + if ((result = pdb_reader_walker_from_compiland_index(pdb, compiland, &dbi_cu_header))) return result; + if ((result = pdb_reader_walker_init(pdb, dbi_cu_header.stream, &cu_walker))) return result; + cu_walker.offset += sizeof(UINT32); + + if ((result = pdb_reader_search_codeview_symbol_by_address(pdb, &cu_walker, top_function_address, &cv_top_function_symbol, &end_stream_offset))) return result; + if (inlined->user < cu_walker.offset || inlined->user >= end_stream_offset) return R_PDB_INVALID_ARGUMENT; + + inlinee_walker.stream_id = cu_walker.stream_id; + inlinee_walker.offset = inlined->user; + inlinee_walker.last = end_stream_offset; + if ((result = pdb_reader_read_partial_codeview_symbol(pdb, &inlinee_walker, &cv_inlinee_symbol))) return result; + switch (cv_inlinee_symbol.generic.id) + { + case S_INLINESITE: + cv_inlinee = cv_inlinee_symbol.inline_site_v3.inlinee; + annotation_offset = offsetof(union codeview_symbol, inline_site_v3.binaryAnnotations); + break; + case S_INLINESITE2: + cv_inlinee = cv_inlinee_symbol.inline_site2_v3.inlinee; + annotation_offset = offsetof(union codeview_symbol, inline_site2_v3.binaryAnnotations); + break; + default: + WARN("Unexpected symbol id %x for %u\n", cv_inlinee_symbol.generic.id, inlined->symt.tag); + return R_PDB_INVALID_PDB_FILE; + } + if ((result = pdb_reader_walker_init_linetab2(pdb, &dbi_cu_header, &linetab2_walker))) return result; + + if ((result = pdb_reader_alloc_and_fetch_from_checksum_subsection(pdb, linetab2_walker, cv_inlinee, &source_file_name, &line_number))) return result; + + /* then walk annotations */ + if ((result = pdb_reader_walker_narrow(&inlinee_walker, + inlinee_walker.offset + annotation_offset - sizeof(cv_inlinee_symbol.generic.len), + cv_inlinee_symbol.generic.len - annotation_offset + sizeof(cv_inlinee_symbol.generic.len)))) return result; + offset_top_function = 0; + while (!(result = pdb_reader_read_inlinesite_annotation(pdb, &inlinee_walker, &opcode, &arg1, &arg2)) && + opcode != BA_OP_Invalid) + { + BOOL check_address = FALSE; + switch (opcode) + { + case BA_OP_CodeOffset: + offset_top_function = arg1; + break; + case BA_OP_ChangeCodeOffset: + offset_top_function += arg1; + check_address = TRUE; + break; + case BA_OP_ChangeCodeLength: + /* this op isn't widely used by MSVC, but clang uses it a lot... */ + offset_top_function += arg1; + break; + case BA_OP_ChangeFile: + pdb_reader_free(pdb, source_file_name); + { + struct pdb_reader_walker sub_walker = linetab2_walker; + struct pdb_reader_walker checksum_walker = linetab2_walker; + if ((result = pdb_reader_subsection_next(pdb, &sub_walker, DEBUG_S_FILECHKSMS, &checksum_walker))) + { + WARN("No DEBUG_S_FILECHKSMS found\n"); + return R_PDB_MISSING_INFORMATION; + } + if ((result = pdb_reader_alloc_and_fetch_from_checksum(pdb, checksum_walker, arg1, &source_file_name))) return result; + } + break; + case BA_OP_ChangeLineOffset: + line_number += pdb_reader_convert_binannot_to_signed(arg1); + break; + case BA_OP_ChangeCodeOffsetAndLineOffset: + line_number += pdb_reader_convert_binannot_to_signed(arg2); + offset_top_function += arg1; + check_address = TRUE; + break; + case BA_OP_ChangeCodeLengthAndCodeOffset: + offset_top_function += arg2; + check_address = TRUE; + break; + default: + WARN("Unsupported op %d\n", opcode); + break; + } + if (check_address) + { + if (top_function_address + offset_top_function > address) /* we're above the searched address */ + break; + line_info->address = top_function_address + offset_top_function; + line_info->line_number = line_number; + if (top_function_address + offset_top_function == address) /* we've reached our address */ + break; + } + } + line_info->key = NULL; + result = lineinfo_set_nameA(pdb->module->process, line_info, source_file_name) ? R_PDB_SUCCESS : R_PDB_OUT_OF_MEMORY; + pdb_reader_free(pdb, source_file_name); + + return result; +} + +static enum method_result pdb_method_get_line_from_inlined_address(struct module_format *modfmt, struct symt_function *inlined, + DWORD64 address, struct lineinfo_t *line_info) +{ + struct pdb_reader *pdb; + + if (!pdb_hack_get_main_info(modfmt, &pdb, NULL)) return MR_FAILURE; + + return pdb_method_result(pdb_method_get_line_from_inlined_address_internal(pdb, inlined, address, line_info)); +} + static struct module_format_vtable pdb_module_format_vtable = { NULL,/*pdb_module_remove*/ @@ -3266,6 +3707,7 @@ static struct module_format_vtable pdb_module_format_vtable = pdb_method_get_line_from_address, pdb_method_advance_line_info, pdb_method_enumerate_lines, + pdb_method_get_line_from_inlined_address, pdb_method_enumerate_sources, }; diff --git a/dlls/dbghelp/symbol.c b/dlls/dbghelp/symbol.c index cf6bfe8c0377..c3ec770b2ba1 100644 --- a/dlls/dbghelp/symbol.c +++ b/dlls/dbghelp/symbol.c @@ -2778,6 +2778,25 @@ static BOOL get_line_from_inline_context(HANDLE hProcess, DWORD64 addr, ULONG in { case IFC_MODE_INLINE: inlined = symt_find_inlined_site(pair.effective, addr, inline_ctx); + if (symt_check_tag(&inlined->symt, SymTagInlineSite)) + { + struct module_format_vtable_iterator iter = {}; + while ((module_format_vtable_iterator_next(pair.effective, &iter, + MODULE_FORMAT_VTABLE_INDEX(get_line_from_inlined_address)))) + { + enum method_result result = iter.modfmt->vtable->get_line_from_inlined_address(iter.modfmt, inlined, addr, line_info); + switch (result) + { + case MR_SUCCESS: + if (disp) *disp = addr - line_info->address; + return TRUE; + case MR_NOT_FOUND: /* continue */ + break; + default: + return FALSE; + } + } + } if (inlined && get_line_from_function(&pair, inlined, addr, disp, line_info)) return TRUE; /* fall through: check if we can find line info at top function level */ From 6c26c7c752d9ad1eeea025e62110a86ecd74ecbb Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Fri, 31 Jan 2025 14:13:47 +0100 Subject: [PATCH 268/454] dbghelp: Directly store compiland's name in symt_compiland. Signed-off-by: Eric Pouech (cherry picked from commit 213e9d48d49cb20052874550fcd0a60d06b1467d) --- dlls/dbghelp/coff.c | 9 ++++----- dlls/dbghelp/dbghelp_private.h | 9 +++++---- dlls/dbghelp/dwarf.c | 6 ++++-- dlls/dbghelp/elf_module.c | 13 ++++++------- dlls/dbghelp/msc.c | 5 ++--- dlls/dbghelp/pe_module.c | 2 +- dlls/dbghelp/source.c | 26 ++++++++++++++++---------- dlls/dbghelp/stabs.c | 2 +- dlls/dbghelp/symbol.c | 6 +++--- dlls/dbghelp/type.c | 3 +-- 10 files changed, 43 insertions(+), 38 deletions(-) diff --git a/dlls/dbghelp/coff.c b/dlls/dbghelp/coff.c index 65ed3b7def20..d4d804d935e4 100644 --- a/dlls/dbghelp/coff.c +++ b/dlls/dbghelp/coff.c @@ -114,7 +114,7 @@ static int coff_add_file(struct CoffFileSet* coff_files, struct module* module, file = coff_files->files + coff_files->nfiles; file->startaddr = 0xffffffff; file->endaddr = 0; - file->compiland = symt_new_compiland(module, source_new(module, NULL, filename)); + file->compiland = symt_new_compiland(module, filename); file->linetab_offset = -1; file->linecnt = 0; file->entries = NULL; @@ -218,8 +218,7 @@ BOOL coff_process_info(const struct msc_debug_info* msc_dbg) */ const char* fn; - fn = source_get(msc_dbg->module, - coff_files.files[curr_file_idx].compiland->source); + fn = coff_files.files[curr_file_idx].compiland->filename; TRACE("Duplicating sect from %s: %lx %x %x %d %d\n", fn, aux->Section.Length, @@ -241,7 +240,7 @@ BOOL coff_process_info(const struct msc_debug_info* msc_dbg) else { TRACE("New text sect from %s: %lx %x %x %d %d\n", - source_get(msc_dbg->module, coff_files.files[curr_file_idx].compiland->source), + coff_files.files[curr_file_idx].compiland->filename, aux->Section.Length, aux->Section.NumberOfRelocations, aux->Section.NumberOfLinenumbers, @@ -420,7 +419,7 @@ BOOL coff_process_info(const struct msc_debug_info* msc_dbg) { symt_add_func_line(msc_dbg->module, (struct symt_function*)coff_files.files[j].entries[l+1], - coff_files.files[j].compiland->source, + source_new(msc_dbg->module, NULL, coff_files.files[j].compiland->filename), linepnt->Linenumber, msc_dbg->module->module.BaseOfImage + linepnt->Type.VirtualAddress); } diff --git a/dlls/dbghelp/dbghelp_private.h b/dlls/dbghelp/dbghelp_private.h index 88573cfccdb6..6f1ac1905a47 100644 --- a/dlls/dbghelp/dbghelp_private.h +++ b/dlls/dbghelp/dbghelp_private.h @@ -207,7 +207,7 @@ struct symt_compiland struct symt symt; struct symt_module* container; /* symt_module */ ULONG_PTR address; - unsigned source; + const char *filename; struct vector vchildren; /* global variables & functions */ void* user; /* when debug info provider needs to store information */ }; @@ -866,6 +866,7 @@ extern BOOL pe_has_buildid_debug(struct image_file_map *fmap, GUID *guid extern unsigned source_new(struct module* module, const char* basedir, const char* source); extern const char* source_get(const struct module* module, unsigned idx); extern int source_rb_compare(const void *key, const struct wine_rb_entry *entry); +extern char *source_build_path(const char *base, const char *name); /* stabs.c */ typedef void (*stabs_def_cb)(struct module* module, ULONG_PTR load_offset, @@ -906,10 +907,10 @@ extern struct symt_ht* extern struct symt_module* symt_new_module(struct module* module); extern struct symt_compiland* - symt_new_compiland(struct module* module, unsigned src_idx); + symt_new_compiland(struct module* module, const char *filename); extern struct symt_public* - symt_new_public(struct module* module, - struct symt_compiland* parent, + symt_new_public(struct module* module, + struct symt_compiland* parent, const char* typename, BOOL is_function, ULONG_PTR address, diff --git a/dlls/dbghelp/dwarf.c b/dlls/dbghelp/dwarf.c index ea79a636078a..f59bcc3f4d71 100644 --- a/dlls/dbghelp/dwarf.c +++ b/dlls/dbghelp/dwarf.c @@ -3048,6 +3048,7 @@ static BOOL dwarf2_parse_compilation_unit(dwarf2_parse_context_t* ctx) struct attribute stmt_list, low_pc; struct attribute comp_dir; struct attribute language; + char *tmp; if (!dwarf2_find_attribute(di, DW_AT_name, &name)) name.u.string = NULL; @@ -3064,8 +3065,9 @@ static BOOL dwarf2_parse_compilation_unit(dwarf2_parse_context_t* ctx) ctx->language = language.u.uvalue; - ctx->compiland = symt_new_compiland(ctx->module_ctx->module, - source_new(ctx->module_ctx->module, comp_dir.u.string, name.u.string)); + tmp = source_build_path(comp_dir.u.string, name.u.string); + ctx->compiland = symt_new_compiland(ctx->module_ctx->module, tmp); + HeapFree(GetProcessHeap(), 0, tmp); ctx->compiland->address = ctx->module_ctx->load_offset + low_pc.u.uvalue; dwarf2_cache_cuhead(ctx->module_ctx->module->format_info[DFI_DWARF]->u.dwarf2_info, ctx->compiland, &ctx->head); di->symt = &ctx->compiland->symt; diff --git a/dlls/dbghelp/elf_module.c b/dlls/dbghelp/elf_module.c index ac2d25af25c2..001db67d533a 100644 --- a/dlls/dbghelp/elf_module.c +++ b/dlls/dbghelp/elf_module.c @@ -699,7 +699,7 @@ static void elf_hash_symtab(struct module* module, struct pool* pool, { case ELF_STT_FILE: if (symname) - compiland = symt_new_compiland(module, source_new(module, NULL, symname)); + compiland = symt_new_compiland(module, symname); else compiland = NULL; continue; @@ -778,12 +778,11 @@ static const struct elf_sym *elf_lookup_symtab(const struct module* module, */ if (compiland) { - compiland_name = source_get(module, - ((const struct symt_compiland*)compiland)->source); + compiland_name = ((const struct symt_compiland*)compiland)->filename; compiland_basename = file_nameA(compiland_name); } else compiland_name = compiland_basename = NULL; - + hash_table_iter_init(ht_symtab, &hti, name); while ((ste = hash_table_iter_up(&hti))) { @@ -794,7 +793,7 @@ static const struct elf_sym *elf_lookup_symtab(const struct module* module, continue; if (ste->compiland && compiland_name) { - const char* filename = source_get(module, ste->compiland->source); + const char* filename = ste->compiland->filename; if (strcmp(filename, compiland_name)) { base = file_nameA(filename); @@ -805,9 +804,9 @@ static const struct elf_sym *elf_lookup_symtab(const struct module* module, { FIXME("Already found symbol %s (%s) in symtab %s @%08x and %s @%08x\n", debugstr_a(name), debugstr_a(compiland_name), - debugstr_a(source_get(module, result->compiland->source)), + debugstr_a(result->compiland->filename), (unsigned int)result->sym.st_value, - debugstr_a(source_get(module, ste->compiland->source)), + debugstr_a(ste->compiland->filename), (unsigned int)ste->sym.st_value); } else diff --git a/dlls/dbghelp/msc.c b/dlls/dbghelp/msc.c index d9a4934696de..fbd7e29b531e 100644 --- a/dlls/dbghelp/msc.c +++ b/dlls/dbghelp/msc.c @@ -2312,7 +2312,6 @@ static struct symt_function* codeview_create_inline_site(const struct msc_debug_ static struct symt_compiland* codeview_new_compiland(const struct msc_debug_info* msc_dbg, const char* objname) { - unsigned int src_idx = source_new(msc_dbg->module, NULL, objname); unsigned int i; /* In some cases MSVC generates several compiland entries with same pathname in PDB file. @@ -2322,10 +2321,10 @@ static struct symt_compiland* codeview_new_compiland(const struct msc_debug_info for (i = 0; i < msc_dbg->module->top->vchildren.num_elts; i++) { struct symt_compiland** p = vector_at(&msc_dbg->module->top->vchildren, i); - if (symt_check_tag(&(*p)->symt, SymTagCompiland) && (*p)->source == src_idx) + if (symt_check_tag(&(*p)->symt, SymTagCompiland) && !strcmp((*p)->filename, objname)) return *p; } - return symt_new_compiland(msc_dbg->module, src_idx); + return symt_new_compiland(msc_dbg->module, objname); } static BOOL codeview_snarf(const struct msc_debug_info* msc_dbg, diff --git a/dlls/dbghelp/pe_module.c b/dlls/dbghelp/pe_module.c index 1f2022fc1af1..6b5a9a4b425f 100644 --- a/dlls/dbghelp/pe_module.c +++ b/dlls/dbghelp/pe_module.c @@ -503,7 +503,7 @@ static BOOL pe_load_coff_symbol_table(struct module* module) if (name[0] == '_') name++; if (!compiland && lastfilename) - compiland = symt_new_compiland(module, source_new(module, NULL, lastfilename)); + compiland = symt_new_compiland(module, lastfilename); if (!(dbghelp_options & SYMOPT_NO_PUBLICS)) symt_new_public(module, compiland, name, FALSE, diff --git a/dlls/dbghelp/source.c b/dlls/dbghelp/source.c index 24ba224a4993..79b4ff9989c2 100644 --- a/dlls/dbghelp/source.c +++ b/dlls/dbghelp/source.c @@ -56,6 +56,21 @@ static unsigned source_find(const char* name) return WINE_RB_ENTRY_VALUE(e, struct source_rb, entry)->source; } +char *source_build_path(const char *base, const char *name) +{ + char *dst; + unsigned bsz = strlen(base); + + dst = HeapAlloc(GetProcessHeap(), 0, bsz + 1 + strlen(name) + 1); + if (dst) + { + strcpy(dst, base); + if (bsz && dst[bsz - 1] != '/' && dst[bsz - 1] != '\\') dst[bsz++] = '/'; + strcpy(&dst[bsz], name); + } + return dst; +} + /****************************************************************** * source_new * @@ -71,16 +86,7 @@ unsigned source_new(struct module* module, const char* base, const char* name) if (!base || *name == '/') full = name; else - { - unsigned bsz = strlen(base); - - tmp = HeapAlloc(GetProcessHeap(), 0, bsz + 1 + strlen(name) + 1); - if (!tmp) return ret; - full = tmp; - strcpy(tmp, base); - if (bsz && tmp[bsz - 1] != '/') tmp[bsz++] = '/'; - strcpy(&tmp[bsz], name); - } + full = source_build_path(base, name); rb_module = module; if (!module->sources || (ret = source_find(full)) == (unsigned)-1) { diff --git a/dlls/dbghelp/stabs.c b/dlls/dbghelp/stabs.c index ea5bed152e6a..8aa9907449ce 100644 --- a/dlls/dbghelp/stabs.c +++ b/dlls/dbghelp/stabs.c @@ -1587,7 +1587,7 @@ BOOL stabs_parse(struct module* module, ULONG_PTR load_offset, { stabs_reset_includes(); source_idx = source_new(module, srcpath, ptr); - compiland = symt_new_compiland(module, source_idx); + compiland = symt_new_compiland(module, source_get(module, source_idx)); } else { diff --git a/dlls/dbghelp/symbol.c b/dlls/dbghelp/symbol.c index c3ec770b2ba1..e4c5361f897b 100644 --- a/dlls/dbghelp/symbol.c +++ b/dlls/dbghelp/symbol.c @@ -231,19 +231,19 @@ struct symt_module* symt_new_module(struct module* module) return sym; } -struct symt_compiland* symt_new_compiland(struct module* module, unsigned src_idx) +struct symt_compiland* symt_new_compiland(struct module* module, const char *filename) { struct symt_compiland* sym; struct symt_compiland** p; TRACE_(dbghelp_symt)("Adding compiland symbol %s:%s\n", - debugstr_w(module->modulename), debugstr_a(source_get(module, src_idx))); + debugstr_w(module->modulename), debugstr_a(filename)); if ((sym = pool_alloc(&module->pool, sizeof(*sym)))) { sym->symt.tag = SymTagCompiland; sym->container = module->top; sym->address = 0; - sym->source = src_idx; + sym->filename = pool_strdup(&module->pool, filename); vector_init(&sym->vchildren, sizeof(struct symt*), 0); sym->user = NULL; p = vector_add(&module->top->vchildren, &module->pool); diff --git a/dlls/dbghelp/type.c b/dlls/dbghelp/type.c index b4c1a414d99c..e0a79086ccf5 100644 --- a/dlls/dbghelp/type.c +++ b/dlls/dbghelp/type.c @@ -102,8 +102,7 @@ const char* symt_get_name(const struct symt* sym) case SymTagEnum: return ((const struct symt_enum*)sym)->hash_elt.name; case SymTagTypedef: return ((const struct symt_typedef*)sym)->hash_elt.name; case SymTagUDT: return ((const struct symt_udt*)sym)->hash_elt.name; - case SymTagCompiland: return source_get(((const struct symt_compiland*)sym)->container->module, - ((const struct symt_compiland*)sym)->source); + case SymTagCompiland: return ((const struct symt_compiland*)sym)->filename; default: FIXME("Unsupported sym-tag %s\n", symt_get_tag_str(sym->tag)); /* fall through */ From 3001da7004789ddcaea0a95ab2f32709f7bc591f Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Fri, 31 Jan 2025 14:00:49 +0100 Subject: [PATCH 269/454] dbghelp: Use symref_t to describe a symbol's container. Signed-off-by: Eric Pouech (cherry picked from commit 9cf063f68f0a18e1841cfa5d240d72fe9da3dc44) --- dlls/dbghelp/dbghelp_private.h | 29 ++++++--- dlls/dbghelp/dwarf.c | 34 +++++----- dlls/dbghelp/elf_module.c | 9 +-- dlls/dbghelp/module.c | 12 ++-- dlls/dbghelp/msc.c | 17 ++--- dlls/dbghelp/symbol.c | 109 ++++++++++++++++++--------------- dlls/dbghelp/type.c | 39 ++++++------ 7 files changed, 138 insertions(+), 111 deletions(-) diff --git a/dlls/dbghelp/dbghelp_private.h b/dlls/dbghelp/dbghelp_private.h index 6f1ac1905a47..f4545612a555 100644 --- a/dlls/dbghelp/dbghelp_private.h +++ b/dlls/dbghelp/dbghelp_private.h @@ -189,7 +189,7 @@ typedef ULONG_PTR symref_t; struct symt_block { struct symt symt; - struct symt* container; /* block, or func */ + symref_t container; /* block, or func */ struct vector vchildren; /* sub-blocks & local variables */ unsigned num_ranges; struct addr_range ranges[]; @@ -205,7 +205,7 @@ struct symt_module /* in fact any of .exe, .dll... */ struct symt_compiland { struct symt symt; - struct symt_module* container; /* symt_module */ + symref_t container; /* symt_module */ ULONG_PTR address; const char *filename; struct vector vchildren; /* global variables & functions */ @@ -217,7 +217,7 @@ struct symt_data struct symt symt; struct hash_table_elt hash_elt; /* if global symbol */ enum DataKind kind; - struct symt* container; + symref_t container; symref_t type; union /* depends on kind */ { @@ -294,7 +294,7 @@ struct symt_function { struct symt symt; /* SymTagFunction or SymTagInlineSite */ struct hash_table_elt hash_elt; /* if global symbol, inline site */ - struct symt* container; /* compiland (for SymTagFunction) or function (for SymTagInlineSite) */ + symref_t container; /* compiland (for SymTagFunction) or function (for SymTagInlineSite) */ symref_t type; /* points to function_signature */ struct vector vlines; struct vector vchildren; /* locals, params, blocks, start/end, labels, inline sites */ @@ -308,7 +308,7 @@ struct symt_hierarchy_point { struct symt symt; /* either SymTagFunctionDebugStart, SymTagFunctionDebugEnd, SymTagLabel */ struct hash_table_elt hash_elt; /* if label (and in compiland's hash table if global) */ - struct symt* parent; /* symt_function or symt_compiland */ + symref_t container; /* symt_function or symt_compiland */ struct location loc; }; @@ -316,7 +316,7 @@ struct symt_public { struct symt symt; struct hash_table_elt hash_elt; - struct symt* container; /* compiland */ + symref_t container; /* compiland */ BOOL is_function; ULONG_PTR address; ULONG_PTR size; @@ -326,7 +326,7 @@ struct symt_thunk { struct symt symt; struct hash_table_elt hash_elt; - struct symt* container; /* compiland */ + symref_t container; /* compiland */ ULONG_PTR address; ULONG_PTR size; THUNK_ORDINAL ordinal; /* FIXME: doesn't seem to be accessible */ @@ -978,12 +978,25 @@ extern struct symt_hierarchy_point* symt_new_label(struct module* module, struct symt_compiland* compiland, const char* name, ULONG_PTR address); +static inline BOOL symt_is_symref_ptr(symref_t ref) {return (ref & 3) == 0;} static inline symref_t symt_ptr_to_symref(const struct symt *symt) {return (ULONG_PTR)symt;} +static inline struct symt* + _symt_symref_to_ptr(const char *file, unsigned lineno, symref_t symref) +{ + if (!symt_is_symref_ptr(symref)) + { + MESSAGE("%s:%u can't convert symref to ptr\n", file, lineno); + return NULL; + } + return (struct symt*)symref; +} +/* this function shall be used with care as not all symref:s are actual pointers */ +#define SYMT_SYMREF_TO_PTR(s) _symt_symref_to_ptr(__FILE__, __LINE__, (s)) extern symref_t symt_index_to_symref(struct module* module, DWORD id); extern DWORD symt_symref_to_index(struct module* module, symref_t sym); static inline DWORD symt_ptr_to_index(struct module *module, const struct symt *symt) {return symt_symref_to_index(module, symt_ptr_to_symref(symt));} -static inline BOOL symt_is_symref_ptr(symref_t ref) {return (ref & 3) == 0;} + extern struct symt_custom* symt_new_custom(struct module* module, const char* name, DWORD64 addr, DWORD size); diff --git a/dlls/dbghelp/dwarf.c b/dlls/dbghelp/dwarf.c index f59bcc3f4d71..62e08eceb41b 100644 --- a/dlls/dbghelp/dwarf.c +++ b/dlls/dbghelp/dwarf.c @@ -2280,7 +2280,7 @@ static void dwarf2_parse_inlined_subroutine(dwarf2_subprogram_t* subpgm, child->abbrev->tag, dwarf2_debug_di(di)); } } - subpgm->current_block = symt_check_tag(subpgm->current_func->container, SymTagBlock) ? + subpgm->current_block = symt_check_tag(SYMT_SYMREF_TO_PTR(subpgm->current_func->container), SymTagBlock) ? (struct symt_block*)subpgm->current_func->container : NULL; subpgm->current_func = (struct symt_function*)symt_get_upper_inlined(subpgm->current_func); } @@ -2924,22 +2924,22 @@ static BOOL dwarf2_parse_line_numbers(dwarf2_parse_context_t* ctx, return TRUE; } -unsigned dwarf2_cache_cuhead(struct dwarf2_module_info_s* module, struct symt_compiland* c, const dwarf2_cuhead_t* head) +static unsigned dwarf2_cache_cuhead(struct module *module, struct dwarf2_module_info_s* module_info, struct symt_compiland* c, const dwarf2_cuhead_t* head) { dwarf2_cuhead_t* ah; unsigned i; - for (i = 0; i < module->num_cuheads; ++i) + for (i = 0; i < module_info->num_cuheads; ++i) { - if (memcmp(module->cuheads[i], head, sizeof(*head)) == 0) + if (memcmp(module_info->cuheads[i], head, sizeof(*head)) == 0) { - c->user = module->cuheads[i]; + c->user = module_info->cuheads[i]; return TRUE; } } - if (!(ah = pool_alloc(&c->container->module->pool, sizeof(*head)))) return FALSE; + if (!(ah = pool_alloc(&module->pool, sizeof(*head)))) return FALSE; memcpy(ah, head, sizeof(*head)); - module->cuheads = realloc(module->cuheads, ++module->num_cuheads * sizeof(head)); - module->cuheads[module->num_cuheads - 1] = ah; + module_info->cuheads = realloc(module_info->cuheads, ++module_info->num_cuheads * sizeof(head)); + module_info->cuheads[module_info->num_cuheads - 1] = ah; c->user = ah; return TRUE; } @@ -3069,7 +3069,7 @@ static BOOL dwarf2_parse_compilation_unit(dwarf2_parse_context_t* ctx) ctx->compiland = symt_new_compiland(ctx->module_ctx->module, tmp); HeapFree(GetProcessHeap(), 0, tmp); ctx->compiland->address = ctx->module_ctx->load_offset + low_pc.u.uvalue; - dwarf2_cache_cuhead(ctx->module_ctx->module->format_info[DFI_DWARF]->u.dwarf2_info, ctx->compiland, &ctx->head); + dwarf2_cache_cuhead(ctx->module_ctx->module, ctx->module_ctx->module->format_info[DFI_DWARF]->u.dwarf2_info, ctx->compiland, &ctx->head); di->symt = &ctx->compiland->symt; children = dwarf2_get_di_children(di); if (children) for (i = 0; i < vector_length(children); i++) @@ -3121,9 +3121,9 @@ static const dwarf2_cuhead_t* get_cuhead_from_func(const struct symt_function* f { if (symt_check_tag(&func->symt, SymTagInlineSite)) func = symt_get_function_from_inlined((struct symt_function*)func); - if (symt_check_tag(&func->symt, SymTagFunction) && symt_check_tag(func->container, SymTagCompiland)) + if (symt_check_tag(&func->symt, SymTagFunction) && symt_check_tag(SYMT_SYMREF_TO_PTR(func->container), SymTagCompiland)) { - struct symt_compiland* c = (struct symt_compiland*)func->container; + struct symt_compiland* c = (struct symt_compiland*)SYMT_SYMREF_TO_PTR(func->container); return (const dwarf2_cuhead_t*)c->user; } FIXME("Should have a compilation unit head\n"); @@ -3138,7 +3138,7 @@ static enum location_error loc_compute_frame(const struct module_format* modfmt, struct location* frame) { struct process *pcs = modfmt->module->process; - struct symt** psym = NULL; + struct symt* sym; struct location* pframe; dwarf2_traverse_context_t lctx; enum location_error err; @@ -3146,10 +3146,10 @@ static enum location_error loc_compute_frame(const struct module_format* modfmt, for (i=0; ivchildren); i++) { - psym = vector_at(&func->vchildren, i); - if (psym && symt_check_tag(*psym, SymTagCustom)) + sym = SYMT_SYMREF_TO_PTR(*(symref_t*)vector_at(&func->vchildren, i)); + if (symt_check_tag(sym, SymTagCustom)) { - pframe = &((struct symt_hierarchy_point*)*psym)->loc; + pframe = &((struct symt_hierarchy_point*)sym)->loc; /* First, recompute the frame information, if needed */ switch (pframe->kind) @@ -3173,7 +3173,7 @@ static enum location_error loc_compute_frame(const struct module_format* modfmt, } break; case loc_dwarf2_frame_cfa: - err = compute_call_frame_cfa(modfmt->module, ip + ((struct symt_compiland*)func->container)->address, frame); + err = compute_call_frame_cfa(modfmt->module, ip + ((struct symt_compiland*)SYMT_SYMREF_TO_PTR(func->container))->address, frame); if (err < 0) return err; break; default: @@ -4030,7 +4030,7 @@ static void dwarf2_location_compute(const struct module_format* modfmt, { struct process *pcs = modfmt->module->process; /* instruction pointer relative to compiland's start */ - ip = pcs->localscope_pc - ((struct symt_compiland*)func->container)->address; + ip = pcs->localscope_pc - ((struct symt_compiland*)SYMT_SYMREF_TO_PTR(func->container))->address; if ((err = loc_compute_frame(modfmt, func, ip, head, &frame)) == 0) { diff --git a/dlls/dbghelp/elf_module.c b/dlls/dbghelp/elf_module.c index 001db67d533a..a62a36f32f6e 100644 --- a/dlls/dbghelp/elf_module.c +++ b/dlls/dbghelp/elf_module.c @@ -762,7 +762,7 @@ static void elf_hash_symtab(struct module* module, struct pool* pool, */ static const struct elf_sym *elf_lookup_symtab(const struct module* module, const struct hash_table* ht_symtab, - const char* name, const struct symt* compiland) + const char* name, symref_t symref_compiland) { struct symtab_elt* weak_result = NULL; /* without compiland name */ struct symtab_elt* result = NULL; @@ -771,8 +771,9 @@ static const struct elf_sym *elf_lookup_symtab(const struct module* module, const char* compiland_name; const char* compiland_basename; const char* base; + struct symt_compiland *compiland = (struct symt_compiland*)SYMT_SYMREF_TO_PTR(symref_compiland); - /* we need weak match up (at least) when symbols of same name, + /* we need weak match up (at least) when symbols of same name, * defined several times in different compilation units, * are merged in a single one (hence a different filename for c.u.) */ @@ -856,7 +857,7 @@ static void elf_finish_stabs_info(struct module* module, const struct hash_table { break; } - symp = elf_lookup_symtab(module, symtab, sym->hash_elt.name, + symp = elf_lookup_symtab(module, symtab, sym->hash_elt.name, ((struct symt_function*)sym)->container); if (symp) { @@ -885,7 +886,7 @@ static void elf_finish_stabs_info(struct module* module, const struct hash_table if (((struct symt_data*)sym)->u.var.kind != loc_absolute || ((struct symt_data*)sym)->u.var.offset != elf_info->elf_addr) break; - symp = elf_lookup_symtab(module, symtab, sym->hash_elt.name, + symp = elf_lookup_symtab(module, symtab, sym->hash_elt.name, ((struct symt_data*)sym)->container); if (symp) { diff --git a/dlls/dbghelp/module.c b/dlls/dbghelp/module.c index af503a21a298..3e0e567b2287 100644 --- a/dlls/dbghelp/module.c +++ b/dlls/dbghelp/module.c @@ -1100,12 +1100,14 @@ BOOL module_remove(struct process* pcs, struct module* module) locsym = &symt_get_function_from_inlined((struct symt_function*)locsym)->symt; if (symt_check_tag(locsym, SymTagFunction)) { - locsym = ((struct symt_function*)locsym)->container; - if (symt_check_tag(locsym, SymTagCompiland) && - module == ((struct symt_compiland*)locsym)->container->module) + struct symt_compiland *compiland = (struct symt_compiland*)SYMT_SYMREF_TO_PTR(((struct symt_function*)locsym)->container); + if (symt_check_tag(&compiland->symt, SymTagCompiland)) { - pcs->localscope_pc = 0; - pcs->localscope_symt = NULL; + if (module == ((struct symt_module*)SYMT_SYMREF_TO_PTR(compiland->container))->module) + { + pcs->localscope_pc = 0; + pcs->localscope_symt = NULL; + } } } } diff --git a/dlls/dbghelp/msc.c b/dlls/dbghelp/msc.c index fbd7e29b531e..d65b77079216 100644 --- a/dlls/dbghelp/msc.c +++ b/dlls/dbghelp/msc.c @@ -1814,8 +1814,9 @@ static BOOL func_has_local(struct symt_function* func, const char* name) for (i = 0; i < func->vchildren.num_elts; ++i) { - struct symt* p = *(struct symt**)vector_at(&func->vchildren, i); - if (symt_check_tag(p, SymTagData) && !strcmp(((struct symt_data*)p)->hash_elt.name, name)) + struct symt *lsym = SYMT_SYMREF_TO_PTR(*(symref_t*)vector_at(&func->vchildren, i)); + + if (symt_check_tag(lsym, SymTagData) && !strcmp(((struct symt_data*)lsym)->hash_elt.name, name)) return TRUE; } return FALSE; @@ -1865,7 +1866,7 @@ static inline void codeview_add_variable(const struct msc_debug_info* msc_dbg, if (symdata->kind == (is_local ? DataIsFileStatic : DataIsGlobal) && symdata->u.var.kind == loc.kind && symdata->u.var.offset == loc.offset && - symdata->container == &compiland->symt) + symdata->container == symt_ptr_to_symref(&compiland->symt)) { /* We don't compare types yet... Unfortunately, they are not * always the same typeid... it'd require full type equivalence @@ -2320,9 +2321,9 @@ static struct symt_compiland* codeview_new_compiland(const struct msc_debug_info */ for (i = 0; i < msc_dbg->module->top->vchildren.num_elts; i++) { - struct symt_compiland** p = vector_at(&msc_dbg->module->top->vchildren, i); - if (symt_check_tag(&(*p)->symt, SymTagCompiland) && !strcmp((*p)->filename, objname)) - return *p; + struct symt_compiland* p = (struct symt_compiland*)SYMT_SYMREF_TO_PTR(*(symref_t*)vector_at(&msc_dbg->module->top->vchildren, i)); + if (symt_check_tag(&p->symt, SymTagCompiland) && !strcmp(p->filename, objname)) + return p; } return symt_new_compiland(msc_dbg->module, objname); } @@ -2783,8 +2784,8 @@ static BOOL codeview_snarf(const struct msc_debug_info* msc_dbg, break; case S_INLINESITE_END: - block = symt_check_tag(curr_func->container, SymTagBlock) ? - (struct symt_block*)curr_func->container : NULL; + block = symt_check_tag(SYMT_SYMREF_TO_PTR(curr_func->container), SymTagBlock) ? + (struct symt_block *)((struct symt_block*)curr_func->container) : NULL; curr_func = (struct symt_function*)symt_get_upper_inlined(curr_func); break; diff --git a/dlls/dbghelp/symbol.c b/dlls/dbghelp/symbol.c index e4c5361f897b..9f8049e3330a 100644 --- a/dlls/dbghelp/symbol.c +++ b/dlls/dbghelp/symbol.c @@ -226,7 +226,7 @@ struct symt_module* symt_new_module(struct module* module) { sym->symt.tag = SymTagExe; sym->module = module; - vector_init(&sym->vchildren, sizeof(struct symt*), 0); + vector_init(&sym->vchildren, sizeof(symref_t), 0); } return sym; } @@ -234,20 +234,20 @@ struct symt_module* symt_new_module(struct module* module) struct symt_compiland* symt_new_compiland(struct module* module, const char *filename) { struct symt_compiland* sym; - struct symt_compiland** p; + symref_t* p; TRACE_(dbghelp_symt)("Adding compiland symbol %s:%s\n", debugstr_w(module->modulename), debugstr_a(filename)); if ((sym = pool_alloc(&module->pool, sizeof(*sym)))) { sym->symt.tag = SymTagCompiland; - sym->container = module->top; + sym->container = symt_ptr_to_symref(&module->top->symt); sym->address = 0; sym->filename = pool_strdup(&module->pool, filename); - vector_init(&sym->vchildren, sizeof(struct symt*), 0); + vector_init(&sym->vchildren, sizeof(symref_t), 0); sym->user = NULL; p = vector_add(&module->top->vchildren, &module->pool); - *p = sym; + if (p) *p = symt_ptr_to_symref(&sym->symt); } return sym; } @@ -259,7 +259,7 @@ struct symt_public* symt_new_public(struct module* module, ULONG_PTR address, unsigned size) { struct symt_public* sym; - struct symt** p; + symref_t* p; TRACE_(dbghelp_symt)("Adding public symbol %s:%s @%Ix\n", debugstr_w(module->modulename), debugstr_a(name), address); @@ -270,7 +270,7 @@ struct symt_public* symt_new_public(struct module* module, { sym->symt.tag = SymTagPublicSymbol; sym->hash_elt.name = pool_strdup(&module->pool, name); - sym->container = compiland ? &compiland->symt : NULL; + sym->container = compiland ? symt_ptr_to_symref(&compiland->symt) : 0; sym->is_function = is_function; sym->address = address; sym->size = size; @@ -278,7 +278,7 @@ struct symt_public* symt_new_public(struct module* module, if (compiland) { p = vector_add(&compiland->vchildren, &module->pool); - *p = &sym->symt; + if (p) *p = symt_ptr_to_symref(&sym->symt); } } return sym; @@ -291,7 +291,7 @@ struct symt_data* symt_new_global_variable(struct module* module, symref_t type) { struct symt_data* sym; - struct symt** p; + symref_t* p; DWORD64 tsz; TRACE_(dbghelp_symt)("Adding global symbol %s:%s %d@%Ix %Ix\n", @@ -301,7 +301,7 @@ struct symt_data* symt_new_global_variable(struct module* module, sym->symt.tag = SymTagData; sym->hash_elt.name = pool_strdup(&module->pool, name); sym->kind = is_static ? DataIsFileStatic : DataIsGlobal; - sym->container = compiland ? &compiland->symt : &module->top->symt; + sym->container = symt_ptr_to_symref(compiland ? &compiland->symt : &module->top->symt); sym->type = type; sym->u.var = loc; if (type && size && symt_get_info_from_symref(module, type, TI_GET_LENGTH, &tsz)) @@ -312,7 +312,7 @@ struct symt_data* symt_new_global_variable(struct module* module, } symt_add_module_ht(module, (struct symt_ht*)sym); p = vector_add(compiland ? &compiland->vchildren : &module->top->vchildren, &module->pool); - *p = &sym->symt; + if (p) *p = symt_ptr_to_symref(&sym->symt); } return sym; } @@ -331,10 +331,10 @@ static struct symt_function* init_function_or_inlinesite(struct module* module, { sym->symt.tag = tag; sym->hash_elt.name = pool_strdup(&module->pool, name); - sym->container = container; + sym->container = symt_ptr_to_symref(container); sym->type = sig_type; vector_init(&sym->vlines, sizeof(struct line_info), 0); - vector_init(&sym->vchildren, sizeof(struct symt*), 0); + vector_init(&sym->vchildren, sizeof(symref_t), 0); sym->user = user; sym->num_ranges = num_ranges; } @@ -354,7 +354,7 @@ struct symt_function* symt_new_function(struct module* module, if ((sym = init_function_or_inlinesite(module, SymTagFunction, &compiland->symt, name, sig_type, user, 1))) { - struct symt** p; + symref_t* p; sym->ranges[0].low = addr; sym->ranges[0].high = addr + size; sym->next_inlinesite = NULL; /* first of list */ @@ -362,7 +362,7 @@ struct symt_function* symt_new_function(struct module* module, if (compiland) { p = vector_add(&compiland->vchildren, &module->pool); - *p = &sym->symt; + if (p) *p = symt_ptr_to_symref(&sym->symt); } } return sym; @@ -381,7 +381,7 @@ struct symt_function* symt_new_inlinesite(struct module* module, TRACE_(dbghelp_symt)("Adding inline site %s\n", debugstr_a(name)); if ((sym = init_function_or_inlinesite(module, SymTagInlineSite, container, name, sig_type, user, num_ranges))) { - struct symt** p; + symref_t* p; assert(container); /* chain inline sites */ @@ -394,7 +394,7 @@ struct symt_function* symt_new_inlinesite(struct module* module, assert(container->tag == SymTagBlock); p = vector_add(&((struct symt_block*)container)->vchildren, &module->pool); } - *p = &sym->symt; + if (p) *p = symt_ptr_to_symref(&sym->symt); } return sym; } @@ -472,7 +472,7 @@ struct symt_data* symt_add_func_local(struct module* module, symref_t type, const char* name) { struct symt_data* locsym; - struct symt** p; + symref_t* p; TRACE_(dbghelp_symt)("Adding local symbol (%s:%s): %s %Ix\n", debugstr_w(module->modulename), debugstr_a(func->hash_elt.name), @@ -486,14 +486,14 @@ struct symt_data* symt_add_func_local(struct module* module, locsym->hash_elt.name = pool_strdup(&module->pool, name); locsym->hash_elt.next = NULL; locsym->kind = dt; - locsym->container = block ? &block->symt : &func->symt; + locsym->container = symt_ptr_to_symref(block ? &block->symt : &func->symt); locsym->type = type; locsym->u.var = *loc; if (block) p = vector_add(&block->vchildren, &module->pool); else p = vector_add(&func->vchildren, &module->pool); - *p = &locsym->symt; + if (p) *p = symt_ptr_to_symref(&locsym->symt); if (dt == DataIsStaticLocal) symt_add_module_addr(module, (struct symt_ht*)locsym); return locsym; @@ -511,7 +511,7 @@ struct symt_data* symt_add_func_constant(struct module* module, VARIANT* v) { struct symt_data* locsym; - struct symt** p; + symref_t* p; TRACE_(dbghelp_symt)("Adding local constant (%s:%s): %s %Ix\n", debugstr_w(module->modulename), debugstr_a(func->hash_elt.name), @@ -524,14 +524,14 @@ struct symt_data* symt_add_func_constant(struct module* module, locsym->hash_elt.name = pool_strdup(&module->pool, name); locsym->hash_elt.next = NULL; locsym->kind = DataIsConstant; - locsym->container = block ? &block->symt : &func->symt; + locsym->container = symt_ptr_to_symref(block ? &block->symt : &func->symt); locsym->type = type; locsym->u.value = *v; if (block) p = vector_add(&block->vchildren, &module->pool); else p = vector_add(&func->vchildren, &module->pool); - *p = &locsym->symt; + if (p) *p = symt_ptr_to_symref(&locsym->symt); return locsym; } @@ -541,7 +541,7 @@ struct symt_block* symt_open_func_block(struct module* module, unsigned num_ranges) { struct symt_block* block; - struct symt** p; + symref_t* p; assert(symt_check_tag(&func->symt, SymTagFunction) || symt_check_tag(&func->symt, SymTagInlineSite)); assert(num_ranges > 0); @@ -550,13 +550,13 @@ struct symt_block* symt_open_func_block(struct module* module, block = pool_alloc(&module->pool, offsetof(struct symt_block, ranges[num_ranges])); block->symt.tag = SymTagBlock; block->num_ranges = num_ranges; - block->container = parent_block ? &parent_block->symt : &func->symt; - vector_init(&block->vchildren, sizeof(struct symt*), 0); + block->container = symt_ptr_to_symref(parent_block ? &parent_block->symt : &func->symt); + vector_init(&block->vchildren, sizeof(symref_t), 0); if (parent_block) p = vector_add(&parent_block->vchildren, &module->pool); else p = vector_add(&func->vchildren, &module->pool); - *p = &block->symt; + if (p) *p = symt_ptr_to_symref(&block->symt); return block; } @@ -565,10 +565,15 @@ struct symt_block* symt_close_func_block(struct module* module, const struct symt_function* func, struct symt_block* block) { + struct symt *container; + assert(symt_check_tag(&func->symt, SymTagFunction) || symt_check_tag(&func->symt, SymTagInlineSite)); - return (block->container->tag == SymTagBlock) ? - CONTAINING_RECORD(block->container, struct symt_block, symt) : NULL; + container = SYMT_SYMREF_TO_PTR(block->container); + assert(container); + + return (container->tag == SymTagBlock) ? + CONTAINING_RECORD(container, struct symt_block, symt) : NULL; } struct symt_hierarchy_point* symt_add_function_point(struct module* module, @@ -577,17 +582,17 @@ struct symt_hierarchy_point* symt_add_function_point(struct module* module, const struct location* loc, const char* name) { - struct symt_hierarchy_point*sym; - struct symt** p; + struct symt_hierarchy_point *sym; + symref_t *p; if ((sym = pool_alloc(&module->pool, sizeof(*sym)))) { - sym->symt.tag = point; - sym->parent = &func->symt; - sym->loc = *loc; + sym->symt.tag = point; + sym->container = symt_ptr_to_symref(&func->symt); + sym->loc = *loc; sym->hash_elt.name = name ? pool_strdup(&module->pool, name) : NULL; p = vector_add(&func->vchildren, &module->pool); - *p = &sym->symt; + if (p) *p = symt_ptr_to_symref(&sym->symt); } return sym; } @@ -606,16 +611,16 @@ struct symt_thunk* symt_new_thunk(struct module* module, { sym->symt.tag = SymTagThunk; sym->hash_elt.name = pool_strdup(&module->pool, name); - sym->container = &compiland->symt; + sym->container = symt_ptr_to_symref(&compiland->symt); sym->address = addr; sym->size = size; sym->ordinal = ord; symt_add_module_ht(module, (struct symt_ht*)sym); if (compiland) { - struct symt** p; + symref_t *p; p = vector_add(&compiland->vchildren, &module->pool); - *p = &sym->symt; + if (p) *p = symt_ptr_to_symref(&sym->symt); } } return sym; @@ -636,15 +641,15 @@ struct symt_data* symt_new_constant(struct module* module, sym->symt.tag = SymTagData; sym->hash_elt.name = pool_strdup(&module->pool, name); sym->kind = DataIsConstant; - sym->container = compiland ? &compiland->symt : &module->top->symt; + sym->container = symt_ptr_to_symref(compiland ? &compiland->symt : &module->top->symt); sym->type = type; sym->u.value = *v; symt_add_module_ht(module, (struct symt_ht*)sym); if (compiland) { - struct symt** p; + symref_t *p; p = vector_add(&compiland->vchildren, &module->pool); - *p = &sym->symt; + if (p) *p = symt_ptr_to_symref(&sym->symt); } } return sym; @@ -665,13 +670,13 @@ struct symt_hierarchy_point* symt_new_label(struct module* module, sym->hash_elt.name = pool_strdup(&module->pool, name); sym->loc.kind = loc_absolute; sym->loc.offset = address; - sym->parent = compiland ? &compiland->symt : NULL; + sym->container = compiland ? symt_ptr_to_symref(&compiland->symt) : 0; symt_add_module_ht(module, (struct symt_ht*)sym); if (compiland) { - struct symt** p; + symref_t *p; p = vector_add(&compiland->vchildren, &module->pool); - *p = &sym->symt; + if (p) *p = symt_ptr_to_symref(&sym->symt); } } return sym; @@ -795,8 +800,8 @@ static BOOL symt_fill_sym_info(struct module_pair* pair, break; case DataIsConstant: sym_info->Flags |= SYMFLAG_VALUEPRESENT; - if (data->container && - (data->container->tag == SymTagFunction || data->container->tag == SymTagBlock)) + if (symt_check_tag(SYMT_SYMREF_TO_PTR(data->container), SymTagFunction) || + symt_check_tag(SYMT_SYMREF_TO_PTR(data->container), SymTagBlock)) sym_info->Flags |= SYMFLAG_LOCAL; switch (V_VT(&data->u.value)) { @@ -1093,7 +1098,7 @@ static BOOL symt_enum_locals_helper(struct module_pair* pair, const WCHAR* match, const struct sym_enum* se, struct symt_function* func, const struct vector* v) { - struct symt* lsym = NULL; + const struct symt* lsym; DWORD_PTR pc = pair->pcs->localscope_pc; unsigned int i; WCHAR* nameW; @@ -1101,7 +1106,8 @@ static BOOL symt_enum_locals_helper(struct module_pair* pair, for (i=0; itag) { case SymTagBlock: @@ -1240,9 +1246,9 @@ struct symt* symt_get_upper_inlined(struct symt_function* inlined) { assert(symt); if (symt->tag == SymTagBlock) - symt = ((struct symt_block*)symt)->container; + symt = SYMT_SYMREF_TO_PTR(((struct symt_block*)symt)->container); else - symt = ((struct symt_function*)symt)->container; + symt = SYMT_SYMREF_TO_PTR(((struct symt_function*)symt)->container); } while (symt->tag == SymTagBlock); assert(symt->tag == SymTagFunction || symt->tag == SymTagInlineSite); return symt; @@ -1638,7 +1644,8 @@ BOOL WINAPI SymFromName(HANDLE hProcess, PCSTR Name, PSYMBOL_INFO Symbol) for (i = 0; i < vector_length(v); i++) { - struct symt* lsym = *(struct symt**)vector_at(v, i); + struct symt* lsym = SYMT_SYMREF_TO_PTR(*(symref_t*)vector_at(v, i)); + switch (lsym->tag) { case SymTagBlock: /* no recursion */ diff --git a/dlls/dbghelp/type.c b/dlls/dbghelp/type.c index e0a79086ccf5..f04698452771 100644 --- a/dlls/dbghelp/type.c +++ b/dlls/dbghelp/type.c @@ -159,10 +159,12 @@ BOOL symt_get_address(const struct symt* type, ULONG64* addr) case SymTagFuncDebugStart: case SymTagFuncDebugEnd: case SymTagLabel: - if (!((const struct symt_hierarchy_point*)type)->parent || - !symt_get_address(((const struct symt_hierarchy_point*)type)->parent, addr)) - *addr = 0; - *addr += ((const struct symt_hierarchy_point*)type)->loc.offset; + *addr = 0; + if (SYMT_SYMREF_TO_PTR(((const struct symt_hierarchy_point*)type)->container)) + { + if (symt_get_address(SYMT_SYMREF_TO_PTR(((const struct symt_hierarchy_point*)type)->container), addr)) + *addr += ((const struct symt_hierarchy_point*)type)->loc.offset; + } break; case SymTagThunk: *addr = ((const struct symt_thunk*)type)->address; @@ -308,13 +310,13 @@ BOOL symt_add_udt_element(struct module* module, struct symt_udt* udt_type, m->hash_elt.next = NULL; m->kind = DataIsMember; - m->container = &module->top->symt; /* native defines lexical parent as module, not udt... */ + m->container = symt_ptr_to_symref(&module->top->symt); /* native defines lexical parent as module, not udt... */ m->type = elt_type; m->u.member.offset = offset; m->u.member.bit_offset = bit_offset; m->u.member.bit_length = bit_size; p = vector_add(&udt_type->vchildren, &module->pool); - *p = &m->symt; + if (p) *p = &m->symt; return TRUE; } @@ -354,7 +356,7 @@ BOOL symt_add_enum_element(struct module* module, struct symt_enum* enum_type, e->hash_elt.name = pool_strdup(&module->pool, name); e->hash_elt.next = NULL; e->kind = DataIsConstant; - e->container = &enum_type->symt; + e->container = symt_ptr_to_symref(&enum_type->symt); e->type = symt_ptr_to_symref(enum_type->base_type); e->u.value = *variant; @@ -672,7 +674,7 @@ BOOL symt_get_info(struct module* module, const struct symt* type, case TI_FINDCHILDREN: { const struct vector* v; - struct symt** pt; + symref_t* symref; unsigned i; TI_FINDCHILDREN_PARAMS* tifp = pInfo; @@ -699,14 +701,14 @@ BOOL symt_get_info(struct module* module, const struct symt* type, /* for those, CHILDRENCOUNT returns 0 */ return tifp->Count == 0; default: - FIXME("Unsupported sym-tag %s for find-children\n", + FIXME("Unsupported sym-tag %s for find-children\n", symt_get_tag_str(type->tag)); return FALSE; } for (i = 0; i < tifp->Count; i++) { - if (!(pt = vector_at(v, tifp->Start + i))) return FALSE; - tifp->ChildId[i] = symt_ptr_to_index(module, *pt); + if (!(symref = (symref_t *)vector_at(v, tifp->Start + i))) return FALSE; + tifp->ChildId[i] = symt_symref_to_index(module, *symref); } } break; @@ -885,25 +887,25 @@ BOOL symt_get_info(struct module* module, const struct symt* type, switch (type->tag) { case SymTagCompiland: - X(DWORD) = symt_ptr_to_index(module, &((const struct symt_compiland*)type)->container->symt); + X(DWORD) = symt_symref_to_index(module, ((const struct symt_compiland*)type)->container); break; case SymTagBlock: - X(DWORD) = symt_ptr_to_index(module, ((const struct symt_block*)type)->container); + X(DWORD) = symt_symref_to_index(module, ((const struct symt_block*)type)->container); break; case SymTagData: - X(DWORD) = symt_ptr_to_index(module, ((const struct symt_data*)type)->container); + X(DWORD) = symt_symref_to_index(module, ((const struct symt_data*)type)->container); break; case SymTagFunction: case SymTagInlineSite: - X(DWORD) = symt_ptr_to_index(module, ((const struct symt_function*)type)->container); + X(DWORD) = symt_symref_to_index(module, ((const struct symt_function*)type)->container); break; case SymTagThunk: - X(DWORD) = symt_ptr_to_index(module, ((const struct symt_thunk*)type)->container); + X(DWORD) = symt_symref_to_index(module, ((const struct symt_thunk*)type)->container); break; case SymTagFuncDebugStart: case SymTagFuncDebugEnd: case SymTagLabel: - X(DWORD) = symt_ptr_to_index(module, ((const struct symt_hierarchy_point*)type)->parent); + X(DWORD) = symt_symref_to_index(module, ((const struct symt_hierarchy_point*)type)->container); break; case SymTagUDT: case SymTagEnum: @@ -1086,7 +1088,8 @@ BOOL symt_get_info(struct module* module, const struct symt* type, MODULE_FORMAT_VTABLE_INDEX(loc_compute)))) { iter.modfmt->vtable->loc_compute(iter.modfmt, - (const struct symt_function*)((const struct symt_data*)type)->container, &loc); + (const struct symt_function*)SYMT_SYMREF_TO_PTR(((const struct symt_data*)type)->container), + &loc); break; } if (loc.kind != loc_absolute) return FALSE; From 1bcbcdff436e73fee139aa9ae3863a2e758e1510 Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Sun, 16 Feb 2025 10:17:11 +0100 Subject: [PATCH 270/454] dbghelp: Introduce a cache for loading blocks in new PDB reader. Signed-off-by: Eric Pouech (cherry picked from commit 92def27a36d104282dc830ff45cd8f004cfd8c10) --- dlls/dbghelp/pdb.c | 56 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 48 insertions(+), 8 deletions(-) diff --git a/dlls/dbghelp/pdb.c b/dlls/dbghelp/pdb.c index ba09f8e37ba7..dc503b79dc9e 100644 --- a/dlls/dbghelp/pdb.c +++ b/dlls/dbghelp/pdb.c @@ -62,7 +62,7 @@ typedef uint64_t pdboff_t; /* offset in whole PDB file (64bit) */ typedef uint32_t pdbsize_t; /* size inside a stream (including offset from beg of stream) (2G max) */ struct pdb_reader; -typedef enum pdb_result (*pdb_reader_fetch_t)(struct pdb_reader *pdb, void *buffer, pdboff_t offset, pdbsize_t size); +typedef enum pdb_result (*pdb_reader_fetch_block_t)(struct pdb_reader *pdb, unsigned block_no, void **block_buffer); struct pdb_reader_walker { @@ -145,7 +145,10 @@ struct pdb_reader const IMAGE_SECTION_HEADER *sections; unsigned num_sections; - pdb_reader_fetch_t fetch; + /* PDB file access */ + pdb_reader_fetch_block_t fetch; + struct {unsigned block_no; unsigned age;} cache[4*4]; + char *fetch_cache_blocks; }; enum pdb_result @@ -170,7 +173,7 @@ static enum pdb_result pdb_reader_report_unexpected(const char *kind, const char static const unsigned short PDB_STREAM_TPI = 2; static const unsigned short PDB_STREAM_DBI = 3; -static enum pdb_result pdb_reader_fetch_file(struct pdb_reader *pdb, void *buffer, pdboff_t offset, pdbsize_t size) +static enum pdb_result pdb_reader_fetch_file_no_cache(struct pdb_reader *pdb, void *buffer, pdboff_t offset, pdbsize_t size) { OVERLAPPED ov = {.Offset = offset, .OffsetHigh = offset >> 32, .hEvent = (HANDLE)(DWORD_PTR)1}; DWORD num_read; @@ -178,6 +181,36 @@ static enum pdb_result pdb_reader_fetch_file(struct pdb_reader *pdb, void *buffe return ReadFile(pdb->file, buffer, size, &num_read, &ov) && num_read == size ? R_PDB_SUCCESS : R_PDB_IOERROR; } +static enum pdb_result pdb_reader_fetch_block_from_file(struct pdb_reader *pdb, unsigned block_no, void **buffer) +{ + enum pdb_result result; + unsigned i; + unsigned lru = ARRAY_SIZE(pdb->cache), found = ARRAY_SIZE(pdb->cache); + + for (i = 0; i < ARRAY_SIZE(pdb->cache); i++) + { + if (pdb->cache[i].block_no == block_no) + found = i; + else + { + pdb->cache[i].age++; + if (lru == ARRAY_SIZE(pdb->cache) || pdb->cache[lru].age < pdb->cache[i].age) + lru = i; + } + } + if (found == ARRAY_SIZE(pdb->cache)) + { + if ((result = pdb_reader_fetch_file_no_cache(pdb, pdb->fetch_cache_blocks + lru * pdb->block_size, + (pdboff_t)block_no * pdb->block_size, pdb->block_size))) return result; + pdb->cache[lru].block_no = block_no; + found = lru; + } + + pdb->cache[found].age = 0; + *buffer = pdb->fetch_cache_blocks + found * pdb->block_size; + return R_PDB_SUCCESS; +} + static const char PDB_JG_IDENT[] = "Microsoft C/C++ program database 2.00\r\n\032JG\0"; static const char PDB_DS_IDENT[] = "Microsoft C/C++ MSF 7.00\r\n\032DS\0"; @@ -242,13 +275,14 @@ static enum pdb_result pdb_reader_internal_read_from_blocks(struct pdb_reader *p enum pdb_result result; pdbsize_t initial_size = size; pdbsize_t toread; + void *block_buffer; while (size) { toread = min(pdb->block_size - delta, size); - if ((result = (*pdb->fetch)(pdb, buffer, (pdboff_t)*blocks * pdb->block_size + delta, toread))) - return result; + if ((result = (pdb->fetch)(pdb, *blocks, &block_buffer))) return result; + memcpy(buffer, (char *)block_buffer + delta, toread); size -= toread; blocks++; buffer = (char*)buffer + toread; @@ -406,9 +440,8 @@ static enum pdb_result pdb_reader_init(struct pdb_reader *pdb, struct module *mo pdb->module = module; pdb->file = file; pool_init(&pdb->pool, 65536); - pdb->fetch = &pdb_reader_fetch_file; - if ((result = (*pdb->fetch)(pdb, &hdr, 0, sizeof(hdr)))) return result; + if ((result = pdb_reader_fetch_file_no_cache(pdb, &hdr, 0, sizeof(hdr)))) return result; if (!memcmp(hdr.signature, PDB_JG_IDENT, sizeof(PDB_JG_IDENT))) { FIXME("PDB reader doesn't support old PDB JG file format\n"); @@ -420,9 +453,16 @@ static enum pdb_result pdb_reader_init(struct pdb_reader *pdb, struct module *mo return R_PDB_INVALID_PDB_FILE; } pdb->block_size = hdr.block_size; + if ((result = pdb_reader_alloc(pdb, pdb->block_size * ARRAY_SIZE(pdb->cache), (void **)&pdb->fetch_cache_blocks))) goto failure; + for (i = 0; i < ARRAY_SIZE(pdb->cache); i++) + { + pdb->cache[i].block_no = 0; /* block where PDB header is, so should never be matched by a read operation */ + pdb->cache[i].age = i; + } + pdb->fetch = &pdb_reader_fetch_block_from_file; toc_blocks_size = pdb_reader_num_blocks(pdb, hdr.toc_size) * sizeof(uint32_t); if ((result = pdb_reader_alloc(pdb, toc_blocks_size, (void**)&toc_blocks)) || - (result = (*pdb->fetch)(pdb, toc_blocks, (pdboff_t)hdr.toc_block * hdr.block_size, toc_blocks_size)) || + (result = pdb_reader_fetch_file_no_cache(pdb, toc_blocks, (pdboff_t)hdr.toc_block * hdr.block_size, toc_blocks_size)) || (result = pdb_reader_alloc(pdb, hdr.toc_size, (void**)&toc)) || (result = pdb_reader_internal_read_from_blocks(pdb, toc_blocks, 0, toc, hdr.toc_size, NULL)) || (result = pdb_reader_alloc(pdb, toc->num_streams * sizeof(pdb->streams[0]), (void**)&pdb->streams))) From b140f41cb3b3ba9ef86e145dc0a6e94b5b9aa75e Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Wed, 23 Apr 2025 13:14:01 +0200 Subject: [PATCH 271/454] winedump: Dump some symbols for managed code. Adding some missing definitions to include/wine/mscvpdb.h Signed-off-by: Eric Pouech (cherry picked from commit 2ca9ae96c54f67b9b92ba5cfa3fab295b88c2777) --- include/wine/mscvpdb.h | 38 ++++++++++++++++++++++++++++++++++++++ tools/winedump/msc.c | 38 +++++++++++++++++++++++++++++++++++++- 2 files changed, 75 insertions(+), 1 deletion(-) diff --git a/include/wine/mscvpdb.h b/include/wine/mscvpdb.h index a21cc897059b..118ccdb68d48 100644 --- a/include/wine/mscvpdb.h +++ b/include/wine/mscvpdb.h @@ -2090,6 +2090,43 @@ union codeview_symbol unsigned numInstrs; unsigned staInstLive; } pogoinfo_v3; + + struct + { + unsigned short int len; + unsigned short int id; + unsigned int pparent; + unsigned int pend; + unsigned int pnext; + unsigned int proc_len; + unsigned int debug_start; + unsigned int debug_end; + unsigned int token; + unsigned int off; + unsigned short sect; + unsigned char flags; + unsigned short ret_reg; + unsigned char name[]; + } managed_proc_v3; + + struct + { + unsigned short len; + unsigned short id; + unsigned int islot; + cv_typ_t typeid; + struct cv_local_varflag attr; + unsigned char name[]; + } managed_slot_v3; + + struct + { + unsigned short len; + unsigned short id; + GUID idOEM; + cv_typ_t typeid; + unsigned int rgl[]; + } oem_v3; }; enum BinaryAnnotationOpcode @@ -2145,6 +2182,7 @@ enum BinaryAnnotationOpcode #define S_DATAREF_ST 0x0401 #define S_ALIGN 0x0402 #define S_LPROCREF_ST 0x0403 +#define S_OEM 0x0404 #define S_REGISTER_ST 0x1001 /* Variants with new 32-bit type indices */ #define S_CONSTANT_ST 0x1002 diff --git a/tools/winedump/msc.c b/tools/winedump/msc.c index 997daab501ba..46925e2ea4fb 100644 --- a/tools/winedump/msc.c +++ b/tools/winedump/msc.c @@ -1429,9 +1429,11 @@ BOOL codeview_dump_symbols(const void* root, unsigned long start, unsigned long case S_DATAREF: case S_PROCREF: case S_LPROCREF: + case S_TOKENREF: printf("%sref V3 '%s' %04x:%08x name:%08x\n", sym->generic.id == S_DATAREF ? "Data" : - (sym->generic.id == S_PROCREF ? "Proc" : "Lproc"), + (sym->generic.id == S_PROCREF ? "Proc" : + (sym->generic.id == S_LPROCREF ? "Lproc" : "Token")), get_symbol_str(sym->refsym2_v3.name), sym->refsym2_v3.imod, sym->refsym2_v3.ibSym, sym->refsym2_v3.sumName); break; @@ -1958,6 +1960,40 @@ BOOL codeview_dump_symbols(const void* root, unsigned long start, unsigned long sym->pogoinfo_v3.numInstrs, sym->pogoinfo_v3.staInstLive); break; + case S_GMANPROC: + case S_LMANPROC: + printf("%s Managed Procedure V3: '%s' (%04x:%08x#%x) attr:%x\n", + sym->generic.id == S_GMANPROC ? "Global" : "Local", + sym->managed_proc_v3.name, + sym->managed_proc_v3.sect, sym->managed_proc_v3.off, sym->managed_proc_v3.proc_len, + sym->managed_proc_v3.flags); + printf("%*s\\- Debug: start=%08x end=%08x\n", + indent, "", sym->managed_proc_v3.debug_start, sym->managed_proc_v3.debug_end); + printf("%*s\\- parent:<%x> end:<%x> next<%x>\n", + indent, "", sym->managed_proc_v3.pparent, sym->managed_proc_v3.pend, sym->managed_proc_v3.pnext); + printf("%*s\\- token:%x retReg:%x\n", + indent, "", sym->managed_proc_v3.token, sym->managed_proc_v3.ret_reg); + push_symbol_dumper(&sd, sym, sym->managed_proc_v3.pend); + break; + + case S_MANSLOT: + printf("Managed slot V3: '%s' type:%x attr:%s slot:%u\n", + sym->managed_slot_v3.name, sym->managed_slot_v3.typeid, + get_varflags(sym->managed_slot_v3.attr), sym->managed_slot_v3.islot); + break; + + case S_OEM: + printf("OEM symbol V3 guid=%s type=%x\n", + get_guid_str(&sym->oem_v3.idOEM), sym->oem_v3.typeid); + { + const unsigned int *from = (const void*)sym->oem_v3.rgl; + const unsigned int *last = (unsigned int*)((unsigned char*)sym + 2 + sym->generic.len); + printf("%*s\\- rgl: [", indent, ""); + for (; from < last; from++) printf("%08x%s", *from, (from + 1) < last ? " " : ""); + printf("]\n"); + } + break; + default: printf("\n\t\t>>> Unsupported symbol-id %x sz=%d\n", sym->generic.id, sym->generic.len + 2); dump_data((const void*)sym, sym->generic.len + 2, " "); From 6b03079912de5c2684b2d30e980bcf908a2ce972 Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Wed, 23 Apr 2025 17:31:27 +0200 Subject: [PATCH 272/454] dbghelp: Silence symbol for managed code. A step towards a full fix of: Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=34990 Signed-off-by: Eric Pouech (cherry picked from commit c9c45d782be2d8beaff5f6ea3d969a2a26d32f70) --- dlls/dbghelp/msc.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/dlls/dbghelp/msc.c b/dlls/dbghelp/msc.c index d65b77079216..2078881d8f20 100644 --- a/dlls/dbghelp/msc.c +++ b/dlls/dbghelp/msc.c @@ -2848,6 +2848,19 @@ static BOOL codeview_snarf(const struct msc_debug_info* msc_dbg, FIXME("Unexpected S_FRAMEPROC %d (%p %p) %x\n", top_frame_size, top_func, curr_func, i); break; + case S_GMANPROC: + case S_LMANPROC: + /* skip whole record and sub-records */ + i = sym->managed_proc_v3.pend; + sym = (const union codeview_symbol*)(root + i); + if (i + sizeof(sym->generic) > size || sym->generic.id != S_END) + { + FIXME("Wrong relocation after managed proc, aborting\n"); + return FALSE; + } + length = 2 + sym->generic.len; + break; + /* the symbols we can safely ignore for now */ case S_SKIP: case S_TRAMPOLINE: @@ -2857,6 +2870,9 @@ static BOOL codeview_snarf(const struct msc_debug_info* msc_dbg, case S_EXPORT: case S_CALLSITEINFO: case S_ARMSWITCHTABLE: + case S_TOKENREF: + case S_OEM: + case S_MANSLOT: /* even if S_LOCAL groks all the S_DEFRANGE* records following itself, * those kinds of records can also be present after a S_FILESTATIC record * so silence them until (at least) S_FILESTATIC is supported From d2291484ae68b4ebbc256ebd9c691b70fd3ad088 Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Wed, 23 Apr 2025 18:43:57 +0200 Subject: [PATCH 273/454] dbghelp: Silence FIXME when dealing with empty hash table. Signed-off-by: Eric Pouech (cherry picked from commit 75f34d181b2167f636a046f39f4694594edf6b79) --- dlls/dbghelp/msc.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dlls/dbghelp/msc.c b/dlls/dbghelp/msc.c index 2078881d8f20..788c6bc585d4 100644 --- a/dlls/dbghelp/msc.c +++ b/dlls/dbghelp/msc.c @@ -2977,11 +2977,13 @@ static BOOL codeview_snarf_sym_hashtable(const struct msc_debug_info* msc_dbg, c if (hashsize < sizeof(DBI_HASH_HEADER) || hash_hdr->signature != 0xFFFFFFFF || hash_hdr->version != 0xeffe0000 + 19990810 || + !hash_hdr->hash_records_size || (hash_hdr->hash_records_size % sizeof(DBI_HASH_RECORD)) != 0 || sizeof(DBI_HASH_HEADER) + hash_hdr->hash_records_size + DBI_BITMAP_HASH_SIZE > hashsize || (hashsize - (sizeof(DBI_HASH_HEADER) + hash_hdr->hash_records_size + DBI_BITMAP_HASH_SIZE)) % sizeof(unsigned)) { - FIXME("Incorrect hash structure\n"); + if (hash_hdr->hash_records_size) + FIXME("Incorrect hash structure\n"); return FALSE; } From fcc3c38528f10b18e4b9d253e8e606e4f3353600 Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Fri, 9 May 2025 12:05:58 +0200 Subject: [PATCH 274/454] dbghelp: Fix using Start parameter in TI_FINDCHILDREN request. Signed-off-by: Eric Pouech (cherry picked from commit 3add94b5fb9350a24242b649ad3ab2db312aeadd) --- dlls/dbghelp/pdb.c | 111 +++++++++++++++++++++++--------------------- dlls/dbghelp/type.c | 2 +- 2 files changed, 58 insertions(+), 55 deletions(-) diff --git a/dlls/dbghelp/pdb.c b/dlls/dbghelp/pdb.c index dc503b79dc9e..349fe8be0877 100644 --- a/dlls/dbghelp/pdb.c +++ b/dlls/dbghelp/pdb.c @@ -2635,7 +2635,7 @@ static enum method_result pdb_reader_TPI_array_request(struct pdb_reader *pdb, c } static enum pdb_result pdb_reader_TPI_fillin_arglist(struct pdb_reader *pdb, unsigned num_ids, unsigned id_size, pdbsize_t tpi_offset, - DWORD *indexes) + TI_FINDCHILDREN_PARAMS *tfp) { enum pdb_result result; symref_t symref; @@ -2643,8 +2643,12 @@ static enum pdb_result pdb_reader_TPI_fillin_arglist(struct pdb_reader *pdb, uns for (i = 0; i < num_ids; i++, tpi_offset += id_size) { - if ((result = pdb_reader_push_action(pdb, action_type_cv_typ_t, tpi_offset, id_size, 0, &symref))) return result; - indexes[i] = symt_symref_to_index(pdb->module, symref); + if (i >= tfp->Start + tfp->Count) return R_PDB_BUFFER_TOO_SMALL; + if (i >= tfp->Start) + { + if ((result = pdb_reader_push_action(pdb, action_type_cv_typ_t, tpi_offset, id_size, 0, &symref))) return result; + tfp->ChildId[i] = symt_symref_to_index(pdb->module, symref); + } } return R_PDB_SUCCESS; } @@ -2691,14 +2695,14 @@ static enum method_result pdb_reader_TPI_procsignature_request(struct pdb_reader /* sigh... the codeview format doesn't have an explicit storage for the arg list item * so we have to fake one using the 'action' field in storage */ - if (p->Start != 0) return MR_FAILURE; if ((result = pdb_reader_TPI_read_partial_reftype(pdb, arglist_cv_typeid, &cv_reftype, &tpi_arglist_offset))) return MR_FAILURE; tpi_arglist_offset += offsetof(union codeview_reftype, arglist_v2.args[0]); - if (p->Count != cv_reftype.arglist_v2.num || num_args > cv_reftype.arglist_v2.num) + if (num_args > cv_reftype.arglist_v2.num) return MR_FAILURE; - result = pdb_reader_TPI_fillin_arglist(pdb, cv_reftype.arglist_v2.num, sizeof(cv_typ_t), tpi_arglist_offset, p->ChildId); - if (result) return MR_FAILURE; + result = pdb_reader_TPI_fillin_arglist(pdb, cv_reftype.arglist_v2.num, sizeof(cv_typ_t), tpi_arglist_offset, p); + if (result && result != R_PDB_BUFFER_TOO_SMALL) return MR_FAILURE; + if (p->Start + p->Count > cv_reftype.arglist_v2.num) return MR_FAILURE; } return MR_SUCCESS; case TI_GET_CHILDRENCOUNT: @@ -2727,37 +2731,28 @@ static enum method_result pdb_reader_TPI_procsignature_request(struct pdb_reader } } -struct pdb_children_filler -{ - DWORD *indexes; - unsigned count; /* max allowed indexes */ - unsigned actual; /* actual index in indexes when filling */ -}; - -static enum pdb_result pdb_reader_push_children_filler(struct pdb_reader *pdb, struct pdb_children_filler *children, symref_t symref) -{ - if (children->actual >= children->count) return R_PDB_BUFFER_TOO_SMALL; - if (children->indexes) - children->indexes[children->actual] = symt_symref_to_index(pdb->module, symref); - children->actual++; - return R_PDB_SUCCESS; -} - static enum pdb_result pdb_reader_push_action_and_filler(struct pdb_reader *pdb, enum pdb_action_type action, pdbsize_t stream_offset, - unsigned id_size, symref_t container_symref, struct pdb_children_filler *children) + unsigned id_size, symref_t container_symref, unsigned *where, TI_FINDCHILDREN_PARAMS *tfp) { enum pdb_result result; symref_t symref; - if (!children->indexes) - symref = 0; - else - if ((result = pdb_reader_push_action(pdb, action, stream_offset, id_size, container_symref, &symref))) return result; - return pdb_reader_push_children_filler(pdb, children, symref); + if (tfp) + { + if (*where >= tfp->Start + tfp->Count) return R_PDB_BUFFER_TOO_SMALL; + if (*where >= tfp->Start) + { + if ((result = pdb_reader_push_action(pdb, action, stream_offset, id_size, container_symref, &symref))) return result; + tfp->ChildId[*where] = symt_symref_to_index(pdb->module, symref); + } + } + (*where)++; + return R_PDB_SUCCESS; } static enum pdb_result pdb_reader_TPI_fillin_enumlist(struct pdb_reader *pdb, symref_t container_symref, - cv_typ_t enumlist_cv_typeid, unsigned count, DWORD *index) + cv_typ_t enumlist_cv_typeid, unsigned *where, + TI_FINDCHILDREN_PARAMS *tfp) { enum pdb_result result; union codeview_reftype *cv_reftype; @@ -2786,23 +2781,30 @@ static enum pdb_result pdb_reader_TPI_fillin_enumlist(struct pdb_reader *pdb, sy switch (cv_field->generic.id) { case LF_ENUMERATE_V3: - if (!count) return R_PDB_INVALID_ARGUMENT; length = offsetof(union codeview_fieldtype, enumerate_v3.data); length += codeview_leaf_as_variant(ptr + length, &v); length += strlen((const char *)ptr + length) + 1; - if ((result = pdb_reader_push_action(pdb, action_type_field, tpi_offset + (ptr - start), length, container_symref, &symref))) + if (*where >= tfp->Start + tfp->Count) { pdb_reader_free(pdb, cv_reftype); - return result; + return R_PDB_BUFFER_TOO_SMALL; } - *index = symt_symref_to_index(pdb->module, symref); - index++; - count--; + if (*where >= tfp->Start) + { + if ((result = pdb_reader_push_action(pdb, action_type_field, tpi_offset + (ptr - start), length, container_symref, &symref))) + { + pdb_reader_free(pdb, cv_reftype); + return result; + } + tfp->ChildId[*where] = symt_symref_to_index(pdb->module, symref); + } + (*where)++; ptr += length; break; case LF_INDEX_V2: - if ((result = pdb_reader_TPI_fillin_enumlist(pdb, container_symref, cv_field->index_v2.ref, count, index))) return result; + if ((result = pdb_reader_TPI_fillin_enumlist(pdb, container_symref, cv_field->index_v2.ref, + where, tfp))) return result; ptr += sizeof(cv_field->index_v2); break; @@ -2823,8 +2825,11 @@ static enum method_result pdb_reader_TPI_enum_request(struct pdb_reader *pdb, sy case TI_FINDCHILDREN: { TI_FINDCHILDREN_PARAMS *p = data; - if (p->Start != 0 || p->Count != cv_type->enumeration_v3.count) return MR_FAILURE; - if (pdb_reader_TPI_fillin_enumlist(pdb, symref, cv_type->enumeration_v3.fieldlist, cv_type->enumeration_v3.count, p->ChildId)) return MR_FAILURE; + unsigned where = 0; + enum pdb_result result; + result = pdb_reader_TPI_fillin_enumlist(pdb, symref, cv_type->enumeration_v3.fieldlist, &where, p); + if (result && result != R_PDB_BUFFER_TOO_SMALL) return MR_FAILURE; + if (p->Start + p->Count > where) return MR_FAILURE; } return MR_SUCCESS; case TI_GET_CHILDRENCOUNT: @@ -2862,7 +2867,7 @@ static enum method_result pdb_reader_TPI_enum_request(struct pdb_reader *pdb, sy } static enum pdb_result pdb_reader_TPI_fillin_UDTlist(struct pdb_reader *pdb, symref_t container_symref, - cv_typ_t udtlist_cv_typeid, struct pdb_children_filler *children) + cv_typ_t udtlist_cv_typeid, unsigned *where, TI_FINDCHILDREN_PARAMS *tfp) { enum pdb_result result; union codeview_reftype *cv_reftype; @@ -2908,7 +2913,7 @@ static enum pdb_result pdb_reader_TPI_fillin_UDTlist(struct pdb_reader *pdb, sym length = offsetof(union codeview_fieldtype, member_v3.data); length += codeview_leaf_as_variant(ptr + length, &v); length += strlen((const char *)ptr + length) + 1; - result = pdb_reader_push_action_and_filler(pdb, action_type_field, tpi_offset + (ptr - start), length, container_symref, children); + result = pdb_reader_push_action_and_filler(pdb, action_type_field, tpi_offset + (ptr - start), length, container_symref, where, tfp); break; case LF_STMEMBER_V3: @@ -2946,7 +2951,7 @@ static enum pdb_result pdb_reader_TPI_fillin_UDTlist(struct pdb_reader *pdb, sym break; case LF_INDEX_V2: - if ((result = pdb_reader_TPI_fillin_UDTlist(pdb, container_symref, cv_field->index_v2.ref, children))) return result; + if ((result = pdb_reader_TPI_fillin_UDTlist(pdb, container_symref, cv_field->index_v2.ref, where, tfp))) return result; length = sizeof(cv_field->index_v2); break; @@ -2965,10 +2970,10 @@ static enum pdb_result pdb_reader_count_advertized_in_UDT_fieldlist(struct pdb_r cv_typ_t fieldlist_cv_typeid, DWORD *count) { enum pdb_result result; - struct pdb_children_filler filler = {.actual = 0, .count = ~0u, .indexes = NULL}; + unsigned where = 0; - if ((result = pdb_reader_TPI_fillin_UDTlist(pdb, container_symref, fieldlist_cv_typeid, &filler))) return result; - *count = filler.actual; + if ((result = pdb_reader_TPI_fillin_UDTlist(pdb, container_symref, fieldlist_cv_typeid, &where, NULL))) return result; + *count = where; return R_PDB_SUCCESS; } @@ -2976,24 +2981,22 @@ static enum method_result pdb_reader_TPI_UDT_request(struct pdb_reader *pdb, sym const struct pdb_reader_walker *walker, IMAGEHLP_SYMBOL_TYPE_INFO req, void *data) { enum UdtKind kind; - unsigned count; const unsigned char *var; cv_typ_t fieldlist_cv_typeid; cv_property_t property; + enum pdb_result result; switch (cv_type->generic.id) { case LF_CLASS_V3: case LF_STRUCTURE_V3: kind = cv_type->generic.id == LF_CLASS_V3 ? UdtClass : UdtStruct; - count = cv_type->struct_v3.n_element; fieldlist_cv_typeid = cv_type->struct_v3.fieldlist; property = cv_type->struct_v3.property; var = cv_type->struct_v3.data; break; case LF_UNION_V3: kind = UdtUnion; - count = cv_type->union_v3.count; fieldlist_cv_typeid = cv_type->union_v3.fieldlist; property = cv_type->union_v3.property; var = cv_type->union_v3.data; @@ -3010,13 +3013,13 @@ static enum method_result pdb_reader_TPI_UDT_request(struct pdb_reader *pdb, sym return MR_SUCCESS; case TI_FINDCHILDREN: { - TI_FINDCHILDREN_PARAMS *p = data; - struct pdb_children_filler filler = {.actual = 0, .count = p->Count, .indexes = p->ChildId}; + TI_FINDCHILDREN_PARAMS *tfp = data; + unsigned where = 0; - if (p->Start != 0) return MR_FAILURE; - if (property.is_forward_defn && !p->Count) return MR_SUCCESS; - if (pdb_reader_TPI_fillin_UDTlist(pdb, symref, fieldlist_cv_typeid, &filler)) return MR_FAILURE; - if (filler.actual != filler.count) {WARN("Count mismatch %u^%u %u\n", filler.actual, filler.count, count); return MR_FAILURE;} + if (property.is_forward_defn) return !tfp->Count ? MR_SUCCESS : MR_FAILURE; + result = pdb_reader_TPI_fillin_UDTlist(pdb, symref, fieldlist_cv_typeid, &where, tfp); + if (result && result != R_PDB_BUFFER_TOO_SMALL) return MR_FAILURE; + if (tfp->Start + tfp->Count > where) return MR_FAILURE; } return MR_SUCCESS; case TI_GET_CHILDRENCOUNT: diff --git a/dlls/dbghelp/type.c b/dlls/dbghelp/type.c index f04698452771..a0529ec37fbd 100644 --- a/dlls/dbghelp/type.c +++ b/dlls/dbghelp/type.c @@ -708,7 +708,7 @@ BOOL symt_get_info(struct module* module, const struct symt* type, for (i = 0; i < tifp->Count; i++) { if (!(symref = (symref_t *)vector_at(v, tifp->Start + i))) return FALSE; - tifp->ChildId[i] = symt_symref_to_index(module, *symref); + tifp->ChildId[tifp->Start + i] = symt_symref_to_index(module, *symref); } } break; From bc4477a5915fe5b214da248c7a75b1fda40b0941 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Mon, 12 May 2025 23:43:22 +0200 Subject: [PATCH 275/454] dbghelp: Fix wrong variable used in EX case. (cherry picked from commit bceff039c85231dc6c44eb88038fa302d769fdfe) --- dlls/dbghelp/pdb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/dbghelp/pdb.c b/dlls/dbghelp/pdb.c index 349fe8be0877..84ef921e5cb3 100644 --- a/dlls/dbghelp/pdb.c +++ b/dlls/dbghelp/pdb.c @@ -3519,7 +3519,7 @@ static enum pdb_result pdb_reader_alloc_and_fetch_from_checksum_subsection(struc case CV_INLINEE_SOURCE_LINE_SIGNATURE_EX: while (!pdb_reader_READ(pdb, &inlineelines_walker, &inlsrcex)) { - if (inlsrc.inlinee == cv_inlinee) + if (inlsrcex.inlinee == cv_inlinee) { if ((result = pdb_reader_alloc_and_fetch_from_checksum(pdb, checksum_walker, inlsrcex.fileId, string))) return result; *line_number = inlsrcex.sourceLineNum; From 1eb376f30f0ffe4595562ad7a0c2f43b87ddc1c9 Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Tue, 13 May 2025 15:51:37 +0200 Subject: [PATCH 276/454] dbghelp: Get rid of code quality warning. Signed-off-by: Eric Pouech (cherry picked from commit 824e3e9e37263874c763cc7c51b9f3e6bfb95597) --- dlls/dbghelp/pdb.c | 1 + 1 file changed, 1 insertion(+) diff --git a/dlls/dbghelp/pdb.c b/dlls/dbghelp/pdb.c index 84ef921e5cb3..b7fc6c63a0e7 100644 --- a/dlls/dbghelp/pdb.c +++ b/dlls/dbghelp/pdb.c @@ -2957,6 +2957,7 @@ static enum pdb_result pdb_reader_TPI_fillin_UDTlist(struct pdb_reader *pdb, sym default: result = PDB_REPORT_UNEXPECTED("UDT field list", cv_field->generic.id); + length = 0; /* keep SAST happy */ } ptr += length; if (result) break; From 65a0701b8141a67f5411df67c55f198410ad555f Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Tue, 13 May 2025 15:43:27 +0200 Subject: [PATCH 277/454] dbghelp: Return method_result from pdb_reader_request_cv_typeid. Signed-off-by: Eric Pouech (cherry picked from commit 497352014cd46f2bc1499b3d60919b7a6861b185) --- dlls/dbghelp/pdb.c | 51 ++++++++++++++++++++++++---------------------- 1 file changed, 27 insertions(+), 24 deletions(-) diff --git a/dlls/dbghelp/pdb.c b/dlls/dbghelp/pdb.c index b7fc6c63a0e7..2202d7c4bae6 100644 --- a/dlls/dbghelp/pdb.c +++ b/dlls/dbghelp/pdb.c @@ -1885,26 +1885,26 @@ static struct {enum BasicType bt; unsigned char size;} supported_basic[T_MAXBASI [T_REAL64] = {btFloat, 8}, [T_REAL80] = {btFloat, 10}, [T_REAL128] = {btFloat, 16}, - [T_RCHAR] = {btChar, 1}, - [T_WCHAR] = {btWChar, 2}, - [T_CHAR16] = {btChar16, 2}, - [T_CHAR32] = {btChar32, 4}, - [T_CHAR8] = {btChar8, 1}, - [T_INT2] = {btInt, 2}, - [T_UINT2] = {btUInt, 2}, - [T_INT4] = {btInt, 4}, - [T_UINT4] = {btUInt, 4}, - [T_INT8] = {btInt, 8}, - [T_UINT8] = {btUInt, 8}, - [T_HRESULT] = {btUInt, 4}, - [T_CPLX32] = {btComplex, 8}, - [T_CPLX64] = {btComplex, 16}, - [T_CPLX128] = {btComplex, 32}, + [T_RCHAR] = {btChar, 1}, + [T_WCHAR] = {btWChar, 2}, + [T_CHAR16] = {btChar16, 2}, + [T_CHAR32] = {btChar32, 4}, + [T_CHAR8] = {btChar8, 1}, + [T_INT2] = {btInt, 2}, + [T_UINT2] = {btUInt, 2}, + [T_INT4] = {btInt, 4}, + [T_UINT4] = {btUInt, 4}, + [T_INT8] = {btInt, 8}, + [T_UINT8] = {btUInt, 8}, + [T_HRESULT] = {btUInt, 4}, + [T_CPLX32] = {btComplex, 8}, + [T_CPLX64] = {btComplex, 16}, + [T_CPLX128] = {btComplex, 32}, }; static inline BOOL is_basic_supported(unsigned basic) { - return basic <= T_MAXBASICTYPE && supported_basic[basic].bt != btNoType; + return basic < T_MAXBASICTYPE && supported_basic[basic].bt != btNoType; } static enum method_result pdb_reader_default_request(struct pdb_reader *pdb, IMAGEHLP_SYMBOL_TYPE_INFO req, void *data) @@ -1940,12 +1940,12 @@ static enum method_result pdb_reader_basic_request(struct pdb_reader *pdb, unsig { case TI_GET_BASETYPE: if (basic >= T_MAXBASICTYPE) return MR_FAILURE; - *((DWORD*)data) = supported_basic[basic].bt; + *((DWORD*)data) = supported_basic[basic & T_BASICTYPE_MASK].bt; break; case TI_GET_LENGTH: switch (basic & T_MODE_MASK) { - case 0: *((DWORD64*)data) = supported_basic[basic].size; break; + case 0: *((DWORD64*)data) = supported_basic[basic & T_BASICTYPE_MASK].size; break; /* pointer type */ case T_NEARPTR_BITS: *((DWORD64*)data) = pdb->module->cpu->word_size; break; case T_NEAR32PTR_BITS: *((DWORD64*)data) = 4; break; @@ -2566,13 +2566,15 @@ static enum pdb_result pdb_reader_index_from_cv_typeid(struct pdb_reader *pdb, c static enum method_result pdb_reader_request_symref_t(struct pdb_reader *pdb, symref_t symref, IMAGEHLP_SYMBOL_TYPE_INFO req, void *data); -static BOOL pdb_reader_request_cv_typeid(struct pdb_reader *pdb, cv_typ_t cv_typeid, IMAGEHLP_SYMBOL_TYPE_INFO req, void *data) +static enum method_result pdb_reader_request_cv_typeid(struct pdb_reader *pdb, cv_typ_t cv_typeid, IMAGEHLP_SYMBOL_TYPE_INFO req, void *data) { + enum pdb_result result; struct symref_code code; symref_t target_symref; - if ((pdb_reader_encode_symref(pdb, symref_code_init_from_cv_typeid(&code, cv_typeid), &target_symref))) return MR_FAILURE; - return pdb_reader_request_symref_t(pdb, target_symref, req, data) == MR_SUCCESS; + if ((result = pdb_reader_encode_symref(pdb, symref_code_init_from_cv_typeid(&code, cv_typeid), &target_symref)) != R_PDB_SUCCESS) + return pdb_method_result(result); + return pdb_reader_request_symref_t(pdb, target_symref, req, data); } static enum method_result pdb_reader_TPI_pointer_request(struct pdb_reader *pdb, const union codeview_type *cv_type, IMAGEHLP_SYMBOL_TYPE_INFO req, void *data) @@ -2601,6 +2603,7 @@ static enum method_result pdb_reader_TPI_pointer_request(struct pdb_reader *pdb, static enum method_result pdb_reader_TPI_array_request(struct pdb_reader *pdb, const union codeview_type *cv_type, IMAGEHLP_SYMBOL_TYPE_INFO req, void *data) { + enum method_result mr; DWORD64 element_length; int array_size; @@ -2610,7 +2613,7 @@ static enum method_result pdb_reader_TPI_array_request(struct pdb_reader *pdb, c *((DWORD*)data) = SymTagArrayType; return MR_SUCCESS; case TI_GET_COUNT: - if (!pdb_reader_request_cv_typeid(pdb, cv_type->array_v3.elemtype, TI_GET_LENGTH, &element_length)) return MR_FAILURE; + if ((mr = pdb_reader_request_cv_typeid(pdb, cv_type->array_v3.elemtype, TI_GET_LENGTH, &element_length)) != MR_SUCCESS) return mr; if (codeview_fetch_leaf_as_int(cv_type, cv_type->array_v3.data, &array_size)) return MR_FAILURE; if (!element_length || (array_size % element_length)) WARN("Incorrect array %u %I64u\n", array_size, element_length); @@ -3067,7 +3070,7 @@ static enum method_result pdb_reader_TPI_UDT_request(struct pdb_reader *pdb, sym static enum method_result pdb_reader_TPI_modifier_request(struct pdb_reader *pdb, symref_t symref, const union codeview_type *cv_type, IMAGEHLP_SYMBOL_TYPE_INFO req, void *data) { - return pdb_reader_request_cv_typeid(pdb, cv_type->modifier_v2.type, req, data) ? MR_SUCCESS : MR_FAILURE; + return pdb_reader_request_cv_typeid(pdb, cv_type->modifier_v2.type, req, data); } static enum method_result pdb_reader_TPI_argtype_request(struct pdb_reader *pdb, struct pdb_action_entry *entry, IMAGEHLP_SYMBOL_TYPE_INFO req, void *data) @@ -3242,7 +3245,7 @@ static enum method_result pdb_reader_DBI_typedef_request(struct pdb_reader *pdb, *((WCHAR **)data) = heap_allocate_symname(cv_symbol->udt_v3.name); return *((WCHAR **)data) != NULL ? MR_SUCCESS : MR_FAILURE; case TI_GET_LENGTH: - return pdb_reader_request_cv_typeid(pdb, cv_symbol->udt_v3.type, req, data) == R_PDB_SUCCESS ? MR_SUCCESS : MR_FAILURE; + return pdb_reader_request_cv_typeid(pdb, cv_symbol->udt_v3.type, req, data); case TI_GET_TYPE: case TI_GET_TYPEID: return pdb_reader_index_from_cv_typeid(pdb, cv_symbol->udt_v3.type, (DWORD*)data) == R_PDB_SUCCESS ? MR_SUCCESS : MR_FAILURE; From 0fc40d87170d442b431bbf34cb1c4eba50f81ab4 Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Sun, 4 May 2025 12:34:56 +0200 Subject: [PATCH 278/454] dbghelp: Load global symbols from DBI. Signed-off-by: Eric Pouech (cherry picked from commit 3785ae2eb423854098c3e8ceb2b1c22899abcfde) --- dlls/dbghelp/msc.c | 2 +- dlls/dbghelp/pdb.c | 124 ++++++++++++++++++++++++++++++++++++--------- 2 files changed, 100 insertions(+), 26 deletions(-) diff --git a/dlls/dbghelp/msc.c b/dlls/dbghelp/msc.c index 788c6bc585d4..f0aa45c43492 100644 --- a/dlls/dbghelp/msc.c +++ b/dlls/dbghelp/msc.c @@ -3973,7 +3973,7 @@ static BOOL pdb_process_internal(const struct process *pcs, file = (BYTE*)((DWORD_PTR)(file_name + strlen(file_name) + 1 + 3) & ~3); } /* Load the global variables and constants (if not yet loaded) and public information */ - if (globalimage) + if (globalimage && !pdb_file->pdb_reader) { const BYTE* data; unsigned global_size = pdb_get_stream_size(pdb_file, symbols.gsym_stream); diff --git a/dlls/dbghelp/pdb.c b/dlls/dbghelp/pdb.c index 2202d7c4bae6..7ddec5538b5a 100644 --- a/dlls/dbghelp/pdb.c +++ b/dlls/dbghelp/pdb.c @@ -425,8 +425,10 @@ static enum pdb_result pdb_reader_init_DBI(struct pdb_reader *pdb); static enum pdb_result pdb_reader_internal_binary_search(size_t num_elt, enum pdb_result (*cmp)(unsigned idx, int *cmp_ressult, void *user), size_t *found, void *user); +static enum pdb_result pdb_reader_symref_from_cv_typeid(struct pdb_reader *pdb, cv_typ_t cv_typeid, symref_t *symref); -static enum pdb_result pdb_reader_init(struct pdb_reader *pdb, struct module *module, HANDLE file) +static enum pdb_result pdb_reader_init(struct pdb_reader *pdb, struct module *module, HANDLE file, + const IMAGE_SECTION_HEADER *sections, unsigned num_sections) { enum pdb_result result; struct PDB_DS_HEADER hdr; @@ -441,6 +443,10 @@ static enum pdb_result pdb_reader_init(struct pdb_reader *pdb, struct module *mo pdb->file = file; pool_init(&pdb->pool, 65536); + pdb->module = module; + pdb->sections = sections; + pdb->num_sections = num_sections; + if ((result = pdb_reader_fetch_file_no_cache(pdb, &hdr, 0, sizeof(hdr)))) return result; if (!memcmp(hdr.signature, PDB_JG_IDENT, sizeof(PDB_JG_IDENT))) { @@ -1540,14 +1546,19 @@ static enum pdb_result pdb_reader_read_partial_blob(struct pdb_reader *pdb, stru static enum pdb_result pdb_reader_alloc_and_read_full_blob(struct pdb_reader *pdb, struct pdb_reader_walker *walker, void **blob) { enum pdb_result result; - unsigned short len; + unsigned short int len; - if ((result = pdb_reader_internal_read_advance(pdb, walker, &len, sizeof(len)))) return result; - if ((result = pdb_reader_alloc(pdb, len + 2, blob))) return result; + if ((result = pdb_reader_READ(pdb, walker, &len))) return result; + if ((result = pdb_reader_alloc(pdb, len + sizeof(len), blob))) + { + walker->offset -= sizeof(len); + return result; + } if ((result = pdb_reader_internal_read_advance(pdb, walker, (char*)*blob + sizeof(len), len))) { - pdb_reader_free(pdb, blob); + pdb_reader_free(pdb, *blob); + walker->offset -= sizeof(len); return result; } *(unsigned short int*)*blob = len; @@ -1677,6 +1688,22 @@ static enum pdb_result pdb_reader_extract_name_out_of_codeview_symbol(union code case S_UDT: *name = cv_symbol->udt_v3.name; break; + case S_PROCREF: + case S_LPROCREF: + case S_DATAREF: + *name = cv_symbol->refsym2_v3.name; + break; + case S_CONSTANT: + *name = (char *)(cv_symbol->constant_v3.data + codeview_get_leaf_length(*(unsigned short*)cv_symbol->constant_v3.data)); + break; + case S_LDATA32: + case S_GDATA32: + *name = cv_symbol->data_v3.name; + break; + case S_LTHREAD32: + case S_GTHREAD32: + *name = cv_symbol->thread_v3.name; + break; default: return R_PDB_INVALID_ARGUMENT; } @@ -1701,18 +1728,18 @@ static enum pdb_result pdb_reader_read_DBI_codeview_symbol_by_name(struct pdb_re struct pdb_dbi_hash_entry *entry; for (entry = &pdb->dbi_symbols_hash[hash]; entry; entry = entry->next) { - BOOL match; - walker.offset = entry->dbi_stream_offset; if ((result = pdb_reader_alloc_and_read_full_codeview_symbol(pdb, &walker, &full_cv_symbol))) return result; - match = pdb_reader_extract_name_out_of_codeview_symbol(full_cv_symbol, &cv_name, &cv_length) == R_PDB_SUCCESS && - !strcmp(name, cv_name); - pdb_reader_free(pdb, full_cv_symbol); - if (match) + if (pdb_reader_extract_name_out_of_codeview_symbol(full_cv_symbol, &cv_name, &cv_length) == R_PDB_SUCCESS) { - *cv_symbol = *full_cv_symbol; - *stream_offset = entry->dbi_stream_offset; - return R_PDB_SUCCESS; + if (!strcmp(name, cv_name)) + { + *cv_symbol = *full_cv_symbol; + pdb_reader_free(pdb, full_cv_symbol); + *stream_offset = entry->dbi_stream_offset; + return R_PDB_SUCCESS; + } + pdb_reader_free(pdb, full_cv_symbol); } } } @@ -1729,6 +1756,8 @@ static enum pdb_result pdb_reader_init_DBI(struct pdb_reader *pdb) if ((result = pdb_reader_read_DBI_header(pdb, &pdb->dbi_header, &walker))) return result; + if ((result = pdb_reader_load_DBI_hash_table(pdb))) return result; + /* register the globals entries not bound to a compiland */ if ((result = pdb_reader_walker_init(pdb, pdb->dbi_header.gsym_stream, &walker))) return result; while (pdb_reader_READ(pdb, &walker, &cv_symbol.generic) == R_PDB_SUCCESS) @@ -1738,13 +1767,47 @@ static enum pdb_result pdb_reader_init_DBI(struct pdb_reader *pdb) case S_UDT: if ((result = pdb_reader_push_action(pdb, action_type_globals, walker.offset - sizeof(cv_symbol.generic), cv_symbol.generic.len + sizeof(cv_symbol.generic.len), 0, &symref))) return result; + break; + case S_GDATA32: + { + DWORD64 address; + symref_t type_symref; + union codeview_symbol *cv_global_symbol; + union codeview_symbol zzcv; + struct pdb_reader_walker global_walker = walker; + pdbsize_t global_hash_offset, global_offset; + + /* There are cases (incremental linking) where we have several entries of same name, but + * only one is valid. + * We discriminate valid with: + * - the hash for that name points to this global entry + * - the address is valid + * - the typeid is valid + * Note: checking address map doesn't bring nothing as the invalid entries are also listed + * there. + */ + global_walker.offset -= sizeof(cv_symbol.generic); + global_offset = global_walker.offset; + if (!pdb_reader_alloc_and_read_full_codeview_symbol(pdb, &global_walker, &cv_global_symbol)) + { + if (!pdb_reader_read_DBI_codeview_symbol_by_name(pdb, cv_global_symbol->data_v3.name, &global_hash_offset, &zzcv) && + global_hash_offset == global_offset && + !pdb_reader_get_segment_address(pdb, cv_global_symbol->data_v3.segment, cv_global_symbol->data_v3.offset, &address) && + !pdb_reader_symref_from_cv_typeid(pdb, cv_global_symbol->data_v3.symtype, &type_symref)) + { + struct location loc = {.kind = loc_absolute, .reg = 0, .offset = address}; + symt_new_global_variable(pdb->module, 0, cv_global_symbol->data_v3.name, + FALSE, loc, 0, type_symref); + } + pdb_reader_free(pdb, cv_global_symbol); + } + } + break; } walker.offset += cv_symbol.generic.len - sizeof(cv_symbol.generic.id); } pdb->num_action_globals = pdb->num_action_entries; - if ((result = pdb_reader_load_DBI_hash_table(pdb))) return result; - return R_PDB_SUCCESS; } @@ -2468,6 +2531,16 @@ static enum pdb_result pdb_reader_resolve_cv_typeid(struct pdb_reader *pdb, cv_t return R_PDB_SUCCESS; } +static enum pdb_result pdb_reader_symref_from_cv_typeid(struct pdb_reader *pdb, cv_typ_t cv_typeid, symref_t *symref) +{ + enum pdb_result result; + struct symref_code code; + + if (cv_typeid > T_MAXPREDEFINEDTYPE) + if ((result = pdb_reader_resolve_cv_typeid(pdb, cv_typeid, &cv_typeid))) return result; + return pdb_reader_encode_symref(pdb, symref_code_init_from_cv_typeid(&code, cv_typeid), symref); +} + static enum method_result pdb_method_enumerate_types(struct module_format *modfmt, BOOL (*cb)(symref_t, const char *, void*), void *user) { struct pdb_reader *pdb; @@ -3761,17 +3834,18 @@ static struct module_format_vtable pdb_module_format_vtable = struct pdb_reader *pdb_hack_reader_init(struct module *module, HANDLE file, const IMAGE_SECTION_HEADER *sections, unsigned num_sections) { struct pdb_reader *pdb = pool_alloc(&module->pool, sizeof(*pdb) + num_sections * sizeof(*sections)); - if (pdb && pdb_reader_init(pdb, module, file) == R_PDB_SUCCESS) + if (pdb) { - pdb->sections = (void*)(pdb + 1); - memcpy((void *)pdb->sections, sections, num_sections * sizeof(*sections)); - pdb->num_sections = num_sections; - pdb->module = module; - /* hack (copy old pdb methods until they are moved here) */ - pdb_module_format_vtable.remove = module->format_info[DFI_PDB]->vtable->remove; + IMAGE_SECTION_HEADER *new_sections = (void*)(pdb + 1); + memcpy(new_sections, sections, num_sections * sizeof(*sections)); + if (pdb_reader_init(pdb, module, file, new_sections, num_sections) == R_PDB_SUCCESS) + { + /* hack (copy old pdb methods until they are moved here) */ + pdb_module_format_vtable.remove = module->format_info[DFI_PDB]->vtable->remove; - module->format_info[DFI_PDB]->vtable = &pdb_module_format_vtable; - return pdb; + module->format_info[DFI_PDB]->vtable = &pdb_module_format_vtable; + return pdb; + } } pool_free(&module->pool, pdb); return NULL; From 7760e495601663a0301e20e7997fab032e06226d Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Thu, 8 May 2025 15:37:06 +0200 Subject: [PATCH 279/454] dbghelp: Beef up reading compiland header helper. Signed-off-by: Eric Pouech (cherry picked from commit f52e98397299367c51ec07a35ab2d013212bc463) --- dlls/dbghelp/pdb.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/dlls/dbghelp/pdb.c b/dlls/dbghelp/pdb.c index 7ddec5538b5a..d71282988569 100644 --- a/dlls/dbghelp/pdb.c +++ b/dlls/dbghelp/pdb.c @@ -796,6 +796,14 @@ static enum pdb_result pdb_reader_compiland_iterator_next(struct pdb_reader *pdb return pdb_reader_read_DBI_cu_header(pdb, iter->dbi_header.version, &iter->dbi_walker, &iter->dbi_cu_header); } +static enum pdb_result pdb_reader_compiland_iterator_alloc_and_read_compiland_name(struct pdb_reader *pdb, const struct pdb_reader_compiland_iterator *iter, + char **obj_name) +{ + struct pdb_reader_walker walker = iter->dbi_walker; + + return pdb_reader_alloc_and_fetch_string(pdb, &walker, obj_name); +} + struct pdb_compiland_lookup { struct pdb_reader *pdb; @@ -3464,7 +3472,10 @@ BOOL cv_hack_ptr_to_symref(struct pdb_reader *pdb, cv_typ_t cv_typeid, symref_t return pdb_reader_encode_symref(pdb, symref_code_init_from_cv_typeid(&code, cv_typeid), symref) == R_PDB_SUCCESS; } -static enum pdb_result pdb_reader_walker_from_compiland_index(struct pdb_reader *pdb, unsigned compiland, PDB_SYMBOL_FILE_EX *dbi_cu_header) +static enum pdb_result pdb_reader_walker_from_compiland_index(struct pdb_reader *pdb, unsigned compiland, + struct pdb_reader_walker *compiland_walker, + /* optional */ + PDB_SYMBOL_FILE_EX *dbi_cu_header, char **obj_name) { enum pdb_result result; struct pdb_reader_compiland_iterator compiland_iter; @@ -3474,7 +3485,10 @@ static enum pdb_result pdb_reader_walker_from_compiland_index(struct pdb_reader { if (!compiland--) { - *dbi_cu_header = compiland_iter.dbi_cu_header; + if ((result = pdb_reader_walker_init(pdb, compiland_iter.dbi_cu_header.stream, compiland_walker))) return result; + if (obj_name && (result = pdb_reader_compiland_iterator_alloc_and_read_compiland_name(pdb, &compiland_iter, obj_name))) return result; + if (dbi_cu_header) *dbi_cu_header = compiland_iter.dbi_cu_header; + compiland_walker->offset += sizeof(UINT32); return R_PDB_SUCCESS; } } while (pdb_reader_compiland_iterator_next(pdb, &compiland_iter) == R_PDB_SUCCESS); @@ -3710,9 +3724,7 @@ static enum pdb_result pdb_method_get_line_from_inlined_address_internal(struct if (!symt_get_info(pdb->module, &function->symt, TI_GET_ADDRESS, &top_function_address)) return R_PDB_INVALID_ARGUMENT; if ((result = pdb_reader_lookup_compiland_by_address(pdb, top_function_address, &compiland))) return result; - if ((result = pdb_reader_walker_from_compiland_index(pdb, compiland, &dbi_cu_header))) return result; - if ((result = pdb_reader_walker_init(pdb, dbi_cu_header.stream, &cu_walker))) return result; - cu_walker.offset += sizeof(UINT32); + if ((result = pdb_reader_walker_from_compiland_index(pdb, compiland, &cu_walker, &dbi_cu_header, NULL))) return result; if ((result = pdb_reader_search_codeview_symbol_by_address(pdb, &cu_walker, top_function_address, &cv_top_function_symbol, &end_stream_offset))) return result; if (inlined->user < cu_walker.offset || inlined->user >= end_stream_offset) return R_PDB_INVALID_ARGUMENT; From 23307fbf6c8ea6032bc555315f5edaafcd203990 Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Thu, 8 May 2025 16:06:02 +0200 Subject: [PATCH 280/454] dbghelp: Build compiland table for new PDB reader. Signed-off-by: Eric Pouech (cherry picked from commit 3a85830155ca9a03926e789518d957547acc7897) --- dlls/dbghelp/pdb.c | 48 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 45 insertions(+), 3 deletions(-) diff --git a/dlls/dbghelp/pdb.c b/dlls/dbghelp/pdb.c index d71282988569..1936165a1176 100644 --- a/dlls/dbghelp/pdb.c +++ b/dlls/dbghelp/pdb.c @@ -104,6 +104,14 @@ struct pdb_dbi_hash_entry struct pdb_dbi_hash_entry *next; }; +struct pdb_compiland +{ + pdbsize_t stream_offset; /* in DBI stream for compiland description */ + unsigned short are_symbols_loaded; + unsigned short stream_id; /* for all symbols of given compiland */ + struct symt_compiland* compiland; +}; + struct pdb_reader { struct module *module; @@ -139,6 +147,10 @@ struct pdb_reader struct pdb_action_entry *action_store; struct pdb_dbi_hash_entry *dbi_symbols_hash; + /* compilands */ + unsigned num_compilands; + struct pdb_compiland *compilands; + /* cache PE module sections for mapping... * we should rather use pe_module information */ @@ -828,16 +840,15 @@ static enum pdb_result pdb_reader_contrib_range_cmp(unsigned idx, int *cmp, void return R_PDB_SUCCESS; } -static enum pdb_result pdb_reader_lookup_compiland_by_address(struct pdb_reader *pdb, DWORD_PTR address, unsigned *compiland) +static enum pdb_result pdb_reader_lookup_compiland_by_segment_offset(struct pdb_reader *pdb, unsigned segment, unsigned offset, unsigned *compiland) { enum pdb_result result; - struct pdb_compiland_lookup lookup = {.pdb = pdb}; + struct pdb_compiland_lookup lookup = {.pdb = pdb, .segment = segment, .offset = offset}; UINT32 version; PDB_SYMBOLS dbi_header; size_t found; unsigned num_ranges; - if ((result = pdb_reader_get_segment_offset_from_address(pdb, address, &lookup.segment, &lookup.offset))) return result; if ((result = pdb_reader_walker_init(pdb, PDB_STREAM_DBI, &lookup.walker))) return result; if ((result = pdb_reader_read_DBI_header(pdb, &dbi_header, &lookup.walker))) return result; if ((result = pdb_reader_walker_narrow(&lookup.walker, lookup.walker.offset + dbi_header.module_size, dbi_header.sectcontrib_size))) return result; @@ -872,6 +883,14 @@ static enum pdb_result pdb_reader_lookup_compiland_by_address(struct pdb_reader return R_PDB_SUCCESS; } +static enum pdb_result pdb_reader_lookup_compiland_by_address(struct pdb_reader *pdb, DWORD_PTR address, unsigned *compiland) +{ + enum pdb_result result; + unsigned segment, offset; + if ((result = pdb_reader_get_segment_offset_from_address(pdb, address, &segment, &offset))) return result; + return pdb_reader_lookup_compiland_by_segment_offset(pdb, segment, offset, compiland); +} + static enum pdb_result pdb_reader_subsection_next(struct pdb_reader *pdb, struct pdb_reader_walker *in_walker, enum DEBUG_S_SUBSECTION_TYPE subsection_type, struct pdb_reader_walker *sub_walker) @@ -1758,12 +1777,34 @@ static enum pdb_result pdb_reader_read_DBI_codeview_symbol_by_name(struct pdb_re static enum pdb_result pdb_reader_init_DBI(struct pdb_reader *pdb) { enum pdb_result result; + struct pdb_reader_compiland_iterator compiland_iter; struct pdb_reader_walker walker; union codeview_symbol cv_symbol; symref_t symref; + unsigned i; if ((result = pdb_reader_read_DBI_header(pdb, &pdb->dbi_header, &walker))) return result; + /* count number of compilands */ + if ((result = pdb_reader_compiland_iterator_init(pdb, &compiland_iter))) return result; + do + { + pdb->num_compilands++; + } while ((result = pdb_reader_compiland_iterator_next(pdb, &compiland_iter)) == R_PDB_SUCCESS); + if ((result = pdb_reader_alloc(pdb, pdb->num_compilands * sizeof(pdb->compilands[0]), (void **)&pdb->compilands))) return result; + memset(pdb->compilands, 0, pdb->num_compilands * sizeof(pdb->compilands[0])); + /* fill-in compiland information */ + if ((result = pdb_reader_compiland_iterator_init(pdb, &compiland_iter))) return result; + for (i = 0; i < pdb->num_compilands; i++) + { + pdb->compilands[i].stream_offset = compiland_iter.dbi_walker.offset; + pdb->compilands[i].are_symbols_loaded = FALSE; + pdb->compilands[i].compiland = NULL; + pdb->compilands[i].stream_id = compiland_iter.dbi_cu_header.stream; + result = pdb_reader_compiland_iterator_next(pdb, &compiland_iter); + if ((result == R_PDB_SUCCESS) != (i + 1 < pdb->num_compilands)) return result ? result : R_PDB_INVALID_PDB_FILE; + } + if ((result = pdb_reader_load_DBI_hash_table(pdb))) return result; /* register the globals entries not bound to a compiland */ @@ -2194,6 +2235,7 @@ static enum pdb_result pdb_reader_init_TPI(struct pdb_reader *pdb) } pdb_reader_free(pdb, pdb->tpi_typemap); pdb->TPI_types_invalid = 1; + return result; } From 77a0629f1a051301151d8cc741b799952e34271f Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Thu, 8 May 2025 15:48:58 +0200 Subject: [PATCH 281/454] dbghelp: Create all symt* from new PDB reader. Signed-off-by: Eric Pouech (cherry picked from commit 8e5c47067bf1f199486cc6af1715e643b624eca5) --- dlls/dbghelp/msc.c | 1 + dlls/dbghelp/pdb.c | 680 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 679 insertions(+), 2 deletions(-) diff --git a/dlls/dbghelp/msc.c b/dlls/dbghelp/msc.c index f0aa45c43492..b5ccddadb67e 100644 --- a/dlls/dbghelp/msc.c +++ b/dlls/dbghelp/msc.c @@ -3929,6 +3929,7 @@ static BOOL pdb_process_internal(const struct process *pcs, /* Read per-module symbols' tables */ file = symbols_image + header_size; + if (pdb_file->pdb_reader) file += symbols.module_size; /* skip it */ while (file - symbols_image < header_size + symbols.module_size) { PDB_SYMBOL_FILE_EX sfile; diff --git a/dlls/dbghelp/pdb.c b/dlls/dbghelp/pdb.c index 1936165a1176..96ee28585027 100644 --- a/dlls/dbghelp/pdb.c +++ b/dlls/dbghelp/pdb.c @@ -132,7 +132,8 @@ struct pdb_reader char *stream_names; unsigned source_listed : 1, - TPI_types_invalid; + TPI_types_invalid : 1, + IPI_types_invalid : 1; /* types management */ PDB_TYPES tpi_header; @@ -140,6 +141,9 @@ struct pdb_reader struct pdb_type_details *tpi_typemap; /* from first to last */ struct pdb_type_hash_entry *tpi_types_hash; + PDB_TYPES ipi_header; + struct pdb_reader_walker ipi_walker; + /* symbol (and types) management */ PDB_SYMBOLS dbi_header; unsigned num_action_globals; @@ -184,6 +188,7 @@ static enum pdb_result pdb_reader_report_unexpected(const char *kind, const char static const unsigned short PDB_STREAM_TPI = 2; static const unsigned short PDB_STREAM_DBI = 3; +static const unsigned short PDB_STREAM_IPI = 4; static enum pdb_result pdb_reader_fetch_file_no_cache(struct pdb_reader *pdb, void *buffer, pdboff_t offset, pdbsize_t size) { @@ -1774,6 +1779,9 @@ static enum pdb_result pdb_reader_read_DBI_codeview_symbol_by_name(struct pdb_re return R_PDB_NOT_FOUND; } +/* FIXME temp forward */ +static enum pdb_result pdb_reader_ensure_symbols_loaded_from_compiland(struct pdb_reader *pdb, unsigned compiland_index); + static enum pdb_result pdb_reader_init_DBI(struct pdb_reader *pdb) { enum pdb_result result; @@ -1804,7 +1812,11 @@ static enum pdb_result pdb_reader_init_DBI(struct pdb_reader *pdb) result = pdb_reader_compiland_iterator_next(pdb, &compiland_iter); if ((result == R_PDB_SUCCESS) != (i + 1 < pdb->num_compilands)) return result ? result : R_PDB_INVALID_PDB_FILE; } - + /* TEMP force loading of all compilands */ + for (i = 0; i < pdb->num_compilands; i++) + { + if ((result = pdb_reader_ensure_symbols_loaded_from_compiland(pdb, i))) return result; + } if ((result = pdb_reader_load_DBI_hash_table(pdb))) return result; /* register the globals entries not bound to a compiland */ @@ -3537,6 +3549,89 @@ static enum pdb_result pdb_reader_walker_from_compiland_index(struct pdb_reader return R_PDB_NOT_FOUND; } +static enum pdb_result pdb_reader_init_IPI(struct pdb_reader *pdb) +{ + enum pdb_result result; + + if (pdb->IPI_types_invalid) return R_PDB_INVALID_PDB_FILE; + if (pdb->ipi_walker.stream_id == 0) /* load basic types information and hash table */ + { + if ((result = pdb_reader_walker_init(pdb, PDB_STREAM_IPI, &pdb->ipi_walker))) goto invalid_file; + /* assuming stream is always big enough go hold a full PDB_TYPES */ + if ((result = pdb_reader_READ(pdb, &pdb->ipi_walker, &pdb->ipi_header))) goto invalid_file; + result = R_PDB_INVALID_PDB_FILE; + if (pdb->ipi_header.version < 19960000 || pdb->ipi_header.type_offset < sizeof(PDB_TYPES)) + { + /* not supported yet... */ + FIXME("Old PDB_TYPES header, skipping\n"); + goto invalid_file; + } + /* validate some bits */ + if (pdb->ipi_header.hash_size != (pdb->ipi_header.last_index - pdb->ipi_header.first_index) * pdb->ipi_header.hash_value_size || + pdb->ipi_header.search_size % sizeof(uint32_t[2])) + goto invalid_file; + if (pdb->ipi_header.hash_value_size > sizeof(unsigned)) + { + FIXME("Unexpected hash value size %u\n", pdb->ipi_header.hash_value_size); + goto invalid_file; + } + pdb->ipi_walker.offset = pdb->ipi_header.type_offset; + } + return R_PDB_SUCCESS; +invalid_file: + memset(&pdb->ipi_walker, 0, sizeof(pdb->ipi_walker)); + pdb->IPI_types_invalid = 1; + return result; +} + +static enum pdb_result pdb_reader_IPI_offset_from_cv_itemid(struct pdb_reader *pdb, cv_itemid_t cv_itemid, pdbsize_t *found_type_offset) +{ + enum pdb_result result; + struct pdb_reader_walker walker; + struct type_offset type_offset; + size_t found; + unsigned short int cv_type_len; + + if ((result = pdb_reader_init_IPI(pdb))) return result; + walker = pdb->ipi_walker; + + type_offset.pdb = pdb; + if ((result = pdb_reader_walker_init(pdb, pdb->ipi_header.hash_stream, &type_offset.walker))) return result; + type_offset.to_search = cv_itemid; + if ((result = pdb_reader_walker_narrow(&type_offset.walker, pdb->ipi_header.search_offset, pdb->ipi_header.search_size))) return result; + result = pdb_reader_internal_binary_search(pdb->ipi_header.search_size / sizeof(uint32_t[2]), + pdb_reader_type_offset_cmp, &found, &type_offset); + + if (result) + { + if (result != R_PDB_NOT_FOUND) return result; + + if (type_offset.values[0] > cv_itemid) {WARN("Out of bounds\n"); return R_PDB_INVALID_PDB_FILE;} + walker.offset += type_offset.values[1]; + + for ( ; type_offset.values[0] < cv_itemid; type_offset.values[0]++) + { + if ((result = pdb_reader_READ(pdb, &walker, &cv_type_len))) return result; + walker.offset += cv_type_len; + } + } + else walker.offset += type_offset.values[1]; + *found_type_offset = walker.offset; + + return R_PDB_SUCCESS; +} + +static enum pdb_result pdb_reader_IPI_alloc_and_read_full_codeview_type(struct pdb_reader *pdb, cv_itemid_t cv_itemid, union codeview_type **cv_type) +{ + enum pdb_result result; + struct pdb_reader_walker walker; + + if ((result = pdb_reader_init_IPI(pdb))) return result; + walker = pdb->ipi_walker; + if ((result = pdb_reader_IPI_offset_from_cv_itemid(pdb, cv_itemid, &walker.offset))) return result; + return pdb_reader_alloc_and_read_full_blob(pdb, &walker, (void **)cv_type); +} + /* walk the top level global symbols to find matching address */ static enum pdb_result pdb_reader_search_codeview_symbol_by_address(struct pdb_reader *pdb, struct pdb_reader_walker *walker, DWORD_PTR address, union codeview_symbol *cv_symbol, pdbsize_t *end_stream_offset) @@ -3871,6 +3966,587 @@ static enum method_result pdb_method_get_line_from_inlined_address(struct module return pdb_method_result(pdb_method_get_line_from_inlined_address_internal(pdb, inlined, address, line_info)); } +static enum pdb_result pdb_reader_symbol_skip_defranges(struct pdb_reader *pdb, struct pdb_reader_walker *walker) +{ + enum pdb_result result; + union codeview_symbol cv_symbol; + + while ((result = pdb_reader_read_partial_codeview_symbol(pdb, walker, &cv_symbol)) == R_PDB_SUCCESS) + { + if (cv_symbol.generic.id < S_DEFRANGE || cv_symbol.generic.id > S_DEFRANGE_REGISTER_REL) + { + walker->offset -= sizeof(cv_symbol.generic.len); + break; + } + walker->offset += cv_symbol.generic.len; + } + return R_PDB_SUCCESS; +} + +static enum pdb_result pdb_reader_symbol_count_range_annotations(struct pdb_reader *pdb, struct pdb_reader_walker annotation_walker, + unsigned *pnum_ranges) +{ + enum pdb_result result; + unsigned num_ranges = 0, opcode, arg1, arg2; + + while ((result = pdb_reader_read_inlinesite_annotation(pdb, &annotation_walker, &opcode, &arg1, &arg2)) == R_PDB_SUCCESS) + { + if (opcode == BA_OP_Invalid) break; + switch (opcode) + { + case BA_OP_CodeOffset: + case BA_OP_ChangeCodeLength: + case BA_OP_ChangeFile: + case BA_OP_ChangeLineOffset: + break; + case BA_OP_ChangeCodeOffset: + case BA_OP_ChangeCodeOffsetAndLineOffset: + case BA_OP_ChangeCodeLengthAndCodeOffset: + num_ranges++; + break; + default: + WARN("Unsupported op %d\n", opcode); + break; + } + } + *pnum_ranges = num_ranges; + return R_PDB_SUCCESS; +} + +static enum pdb_result pdb_reader_symbol_skip_if(struct pdb_reader *pdb, struct pdb_reader_walker *walker, unsigned id) +{ + enum pdb_result result; + union codeview_symbol cv_symbol; + + if ((result = pdb_reader_read_partial_codeview_symbol(pdb, walker, &cv_symbol))) return result; + if (cv_symbol.generic.id != id) + { + walker->offset -= sizeof(cv_symbol.generic.len); + return R_PDB_NOT_FOUND; + } + walker->offset += cv_symbol.generic.len; + return R_PDB_SUCCESS; +} + +static inline void inline_site_update_last_range(struct symt_function* inlined, unsigned index, ULONG_PTR hi) +{ + if (index && index <= inlined->num_ranges) + { + struct addr_range* range = &inlined->ranges[index - 1]; + /* only change range if it has no span (code start without code end) */ + if (range->low == range->high) + range->high = hi; + } +} + +static enum pdb_result pdb_reader_create_inline_site(struct pdb_reader *pdb, struct symt_function *top_func, + struct symt *container, + cv_itemid_t cv_inlinee_id, + pdbsize_t symbol_start_offset, + struct pdb_reader_walker *annotation_walker, + struct symt_function **pinlined) +{ + enum pdb_result result; + union codeview_type *cv_type; + struct symt_function *inlined; + unsigned num_ranges; + unsigned offset, index; + symref_t type_symref; + unsigned opcode, arg1, arg2; + + if ((result = pdb_reader_IPI_alloc_and_read_full_codeview_type(pdb, cv_inlinee_id, &cv_type))) + { + WARN("Couldn't find type %x in IPI stream\n", cv_inlinee_id); + return result; + } + if ((result = pdb_reader_symbol_count_range_annotations(pdb, *annotation_walker, &num_ranges))) return result; + if (!num_ranges) return R_PDB_INVALID_PDB_FILE; + + switch (cv_type->generic.id) + { + case LF_FUNC_ID: + if ((result = pdb_reader_symref_from_cv_typeid(pdb, cv_type->func_id_v3.type, &type_symref))) return result; + inlined = symt_new_inlinesite(pdb->module, top_func, container, cv_type->func_id_v3.name, + type_symref, symbol_start_offset, num_ranges); + break; + case LF_MFUNC_ID: + /* FIXME we just declare a function, not a method */ + if ((result = pdb_reader_symref_from_cv_typeid(pdb, cv_type->mfunc_id_v3.type, &type_symref))) return result; + inlined = symt_new_inlinesite(pdb->module, top_func, container, cv_type->mfunc_id_v3.name, + type_symref, symbol_start_offset, num_ranges); + break; + default: + result = PDB_REPORT_UNEXPECTED("inlinee kind", cv_type->generic.id); + pdb_reader_free(pdb, cv_type); + return result; + } + pdb_reader_free(pdb, cv_type); + + /* rescan all annotations and store ranges & line information */ + offset = 0; + index = 0; + + while (pdb_reader_read_inlinesite_annotation(pdb, annotation_walker, &opcode, &arg1, &arg2) == R_PDB_SUCCESS) + { + switch (opcode) + { + case BA_OP_CodeOffset: + offset = arg1; + break; + case BA_OP_ChangeCodeOffset: + offset += arg1; + inline_site_update_last_range(inlined, index, top_func->ranges[0].low + offset); + inlined->ranges[index ].low = top_func->ranges[0].low + offset; + inlined->ranges[index++].high = top_func->ranges[0].low + offset; + break; + case BA_OP_ChangeCodeLength: + /* this op isn't widely used by MSVC, but clang uses it a lot... */ + offset += arg1; + inline_site_update_last_range(inlined, index, top_func->ranges[0].low + offset); + break; + case BA_OP_ChangeFile: + break; + case BA_OP_ChangeLineOffset: + break; + case BA_OP_ChangeCodeOffsetAndLineOffset: + offset += arg1; + inline_site_update_last_range(inlined, index, top_func->ranges[0].low + offset); + inlined->ranges[index ].low = top_func->ranges[0].low + offset; + inlined->ranges[index++].high = top_func->ranges[0].low + offset; + break; + case BA_OP_ChangeCodeLengthAndCodeOffset: + offset += arg2; + inline_site_update_last_range(inlined, index, top_func->ranges[0].low + offset); + inlined->ranges[index ].low = top_func->ranges[0].low + offset; + inlined->ranges[index++].high = top_func->ranges[0].low + offset + arg1; + break; + default: + WARN("Unsupported op %d\n", opcode); + break; + } + } + if (index != num_ranges) /* sanity check */ + return PDB_REPORT_UNEXPECTED("Internal logic error", 0); + if (inlined->num_ranges) + { + struct addr_range* range = &inlined->ranges[inlined->num_ranges - 1]; + if (range->low == range->high) WARN("pending empty range at end of %s inside %s\n", + debugstr_a(inlined->hash_elt.name), + debugstr_a(top_func->hash_elt.name)); + } + *pinlined = inlined; + return R_PDB_SUCCESS; +} + +/* likely to review */ +static enum pdb_result pdb_reader_create_variable(struct pdb_reader *pdb, + struct symt_compiland *compiland, + struct symt_function* func, + struct symt_block* block, + const char* name, + struct location *loc, + cv_typ_t cv_typeid, BOOL is_local) +{ + if (name && *name) + { + enum pdb_result result; + symref_t symref; + + if ((result = pdb_reader_symref_from_cv_typeid(pdb, cv_typeid, &symref))) return result; + if (func) + { + if (!is_local || loc->kind == loc_tlsrel) WARN("Unsupported construct\n"); + symt_add_func_local(pdb->module, func, DataIsStaticLocal, loc, block, symref, name); + return R_PDB_SUCCESS; + } + + if (is_local ^ (compiland != 0)) FIXME("Unsupported construct\n"); + symt_new_global_variable(pdb->module, compiland, name, is_local, *loc, 0, symref); + } + return R_PDB_SUCCESS; +} + +static BOOL symt_function_has_local_variable(struct symt_function* func, const char* name) +{ + int i; + + for (i = 0; i < vector_length(&func->vchildren); ++i) + { + struct symt* p = *(struct symt**)vector_at(&func->vchildren, i); + if (symt_check_tag(p, SymTagData) && !strcmp(((struct symt_data*)p)->hash_elt.name, name)) + return TRUE; + } + return FALSE; +} + +static enum pdb_result pdb_reader_load_compiland_symbols(struct pdb_reader *pdb, struct symt_compiland *compiland, struct pdb_reader_walker *walker) +{ + enum pdb_result result; + union codeview_symbol *cv_symbol; + struct symt_function *top_func = NULL; + struct symt_function *curr_func = NULL; + struct symt_block *block = NULL; + struct location loc; + unsigned top_frame_size = -1; + DWORD64 address; + symref_t type_symref; + /* + * Loop over the different types of records and whenever we + * find something we are interested in, record it and move on. + */ + while ((result = pdb_reader_alloc_and_read_full_codeview_symbol(pdb, walker, &cv_symbol)) == R_PDB_SUCCESS) + { + pdbsize_t symbol_start_offset = walker->offset - (sizeof(cv_symbol->generic.len) + cv_symbol->generic.len); + if (!cv_symbol->generic.id || cv_symbol->generic.len < 2) break; + if ((cv_symbol->generic.len + 2) & 3) WARN("unpadded len %u\n", cv_symbol->generic.len + 2); + + switch (cv_symbol->generic.id) + { + case S_LDATA32: + loc.kind = loc_absolute; + loc.reg = 0; + if ((result = pdb_reader_get_segment_address(pdb, cv_symbol->data_v3.segment, cv_symbol->data_v3.offset, &address))) + goto failure; + loc.offset = address; + + if ((result = pdb_reader_create_variable(pdb, compiland, curr_func, block, cv_symbol->data_v3.name, &loc, + cv_symbol->data_v3.symtype, TRUE))) goto failure; + break; + + /* variables with thread storage */ + case S_GTHREAD32: + case S_LTHREAD32: + loc.kind = loc_tlsrel; + loc.reg = 0; + loc.offset = cv_symbol->thread_v3.offset; + + if ((result = pdb_reader_create_variable(pdb, compiland, curr_func, block, cv_symbol->thread_v3.name, + &loc, cv_symbol->thread_v3.symtype, + cv_symbol->generic.id == S_LTHREAD32))) goto failure; + break; + + case S_THUNK32: + if ((result = pdb_reader_get_segment_address(pdb, cv_symbol->thunk_v3.segment, cv_symbol->thunk_v3.offset, &address))) goto failure; + symt_new_thunk(pdb->module, compiland, + cv_symbol->thunk_v3.name, cv_symbol->thunk_v3.thtype, + address, cv_symbol->thunk_v3.thunk_len); + break; + + /* + * Global and static functions. + */ + case S_GPROC32: + case S_LPROC32: + if (top_func) WARN("nested function\n"); + if ((result = pdb_reader_get_segment_address(pdb, cv_symbol->proc_v3.segment, cv_symbol->proc_v3.offset, &address))) goto failure; + if ((result = pdb_reader_symref_from_cv_typeid(pdb, cv_symbol->proc_v3.proctype, &type_symref))) goto failure; + if ((top_func = symt_new_function(pdb->module, compiland, + cv_symbol->proc_v3.name, + address, cv_symbol->proc_v3.proc_len, + type_symref, symbol_start_offset))) + { + curr_func = top_func; + loc.kind = loc_absolute; + loc.offset = cv_symbol->proc_v3.debug_start; + symt_add_function_point(pdb->module, curr_func, SymTagFuncDebugStart, &loc, NULL); + loc.offset = cv_symbol->proc_v3.debug_end; + symt_add_function_point(pdb->module, curr_func, SymTagFuncDebugEnd, &loc, NULL); + } + break; + /* + * Function parameters and stack variables. + */ + case S_BPREL32: + /* S_BPREL32 can be present after S_LOCAL; prefer S_LOCAL when present */ + if (symt_function_has_local_variable(curr_func, cv_symbol->stack_v3.name)) break; + if ((result = pdb_reader_symref_from_cv_typeid(pdb, cv_symbol->stack_v3.symtype, &type_symref))) goto failure; + loc.kind = loc_regrel; + /* Yes, it's i386 dependent, but that's the symbol purpose. S_REGREL is used on other CPUs */ + loc.reg = CV_REG_EBP; + loc.offset = cv_symbol->stack_v3.offset; + symt_add_func_local(pdb->module, curr_func, + cv_symbol->stack_v3.offset > 0 ? DataIsParam : DataIsLocal, + &loc, block, type_symref, cv_symbol->stack_v3.name); + break; + case S_REGREL32: + /* S_REGREL32 can be present after S_LOCAL; prefer S_LOCAL when present */ + if (symt_function_has_local_variable(curr_func, cv_symbol->regrel_v3.name)) break; + if ((result = pdb_reader_symref_from_cv_typeid(pdb, cv_symbol->regrel_v3.symtype, &type_symref))) goto failure; + loc.kind = loc_regrel; + loc.reg = cv_symbol->regrel_v3.reg; + loc.offset = cv_symbol->regrel_v3.offset; + if (top_frame_size == -1) WARN("S_REGREL32 without frameproc\n"); + symt_add_func_local(pdb->module, curr_func, + cv_symbol->regrel_v3.offset >= top_frame_size ? DataIsParam : DataIsLocal, + &loc, block, type_symref, cv_symbol->regrel_v3.name); + break; + + case S_REGISTER: + /* S_REGISTER can be present after S_LOCAL; prefer S_LOCAL when present */ + if (symt_function_has_local_variable(curr_func, cv_symbol->register_v3.name)) break; + if ((result = pdb_reader_symref_from_cv_typeid(pdb, cv_symbol->register_v3.type, &type_symref))) goto failure; + loc.kind = loc_register; + loc.reg = cv_symbol->register_v3.reg; + loc.offset = 0; + symt_add_func_local(pdb->module, curr_func, + DataIsLocal, &loc, block, type_symref, cv_symbol->register_v3.name); + break; + + case S_BLOCK32: + if ((result = pdb_reader_get_segment_address(pdb, cv_symbol->block_v3.segment, cv_symbol->block_v3.offset, &address))) goto failure; + block = symt_open_func_block(pdb->module, curr_func, block, 1); + block->ranges[0].low = address; + block->ranges[0].high = block->ranges[0].low + cv_symbol->block_v3.length; + break; + + case S_END: + if (block) + { + block = symt_close_func_block(pdb->module, curr_func, block); + } + else if (top_func) + { + if (curr_func != top_func) {FIXME("shouldn't close a top function with an opened inlined function\n"); result = R_PDB_INVALID_PDB_FILE; goto failure;} + top_func = curr_func = NULL; + top_frame_size = -1; + } + break; + + case S_COMPILE2: + TRACE("S-Compile-V3 machine:%x language:%x %s\n", + cv_symbol->compile2_v3.machine, cv_symbol->compile2_v3.flags.iLanguage, debugstr_a(cv_symbol->compile2_v3.name)); + break; + case S_COMPILE3: + TRACE("S-Compile3-V3 machine:%x language:%x %s\n", + cv_symbol->compile3_v3.machine, cv_symbol->compile3_v3.flags.iLanguage, debugstr_a(cv_symbol->compile3_v3.name)); + break; + + case S_ENVBLOCK: + break; + + case S_OBJNAME: + TRACE("S-ObjName-V3 %s\n", debugstr_a(cv_symbol->objname_v3.name)); + break; + + case S_LABEL32: + if ((result = pdb_reader_get_segment_address(pdb, cv_symbol->label_v3.segment, cv_symbol->label_v3.offset, &address))) goto failure; + if (curr_func) + { + loc.kind = loc_absolute; + loc.offset = address - curr_func->ranges[0].low; + symt_add_function_point(pdb->module, curr_func, SymTagLabel, &loc, cv_symbol->label_v3.name); + } + else symt_new_label(pdb->module, compiland, cv_symbol->label_v3.name, address); + break; + + case S_LOCAL: + /* FIXME: don't store global/static variables accessed through registers... we don't support that + * in locals... anyway, global data record should be present as well (so the variable will be available + * through the global definition, but potentially not updated) + */ + if (!cv_symbol->local_v3.varflags.enreg_global && !cv_symbol->local_v3.varflags.enreg_static) + { + loc.kind = loc_cv_defrange; + loc.reg = walker->stream_id; + loc.offset = symbol_start_offset; + + if ((result = pdb_reader_symref_from_cv_typeid(pdb, cv_symbol->local_v3.symtype, &type_symref))) goto failure; + symt_add_func_local(pdb->module, curr_func, + cv_symbol->local_v3.varflags.is_param ? DataIsParam : DataIsLocal, + &loc, block, type_symref, cv_symbol->local_v3.name); + } + if ((result = pdb_reader_symbol_skip_defranges(pdb, walker))) goto failure; + break; + case S_INLINESITE: + { + struct pdb_reader_walker annotation_walker; + struct symt_function* inlined; + + annotation_walker.stream_id = walker->stream_id; + annotation_walker.offset = symbol_start_offset + offsetof(union codeview_symbol, inline_site_v3.binaryAnnotations); + annotation_walker.last = symbol_start_offset + sizeof(cv_symbol->generic.len) + cv_symbol->generic.len; + + if ((result = pdb_reader_create_inline_site(pdb, top_func, block ? &block->symt : &curr_func->symt, + cv_symbol->inline_site_v3.inlinee, symbol_start_offset, &annotation_walker, &inlined))) + { + /* skip whole inlined block */ + walker->offset = cv_symbol->inline_site_v3.pEnd; + if ((result = pdb_reader_symbol_skip_if(pdb, walker, S_INLINESITE_END))) goto failure; + } + else + { + curr_func = inlined; + block = NULL; + } + } + break; + case S_INLINESITE2: + { + struct pdb_reader_walker annotation_walker = *walker; + struct symt_function* inlined; + + annotation_walker.stream_id = walker->stream_id; + annotation_walker.offset = symbol_start_offset + offsetof(union codeview_symbol, inline_site2_v3.binaryAnnotations); + annotation_walker.last = symbol_start_offset + sizeof(cv_symbol->generic.len) + cv_symbol->generic.len; + + if ((result = pdb_reader_create_inline_site(pdb, top_func, block ? &block->symt : &curr_func->symt, + cv_symbol->inline_site2_v3.inlinee, symbol_start_offset, &annotation_walker, &inlined))) + { + /* skip whole inlined block */ + walker->offset = cv_symbol->inline_site2_v3.pEnd; + if ((result = pdb_reader_symbol_skip_if(pdb, walker, S_INLINESITE_END))) goto failure; + } + else + { + curr_func = inlined; + block = NULL; + } + } + break; + + case S_INLINESITE_END: + block = symt_is_symref_ptr(curr_func->container) && symt_check_tag(SYMT_SYMREF_TO_PTR(curr_func->container), SymTagBlock) ? + (struct symt_block*)curr_func->container : NULL; + curr_func = (struct symt_function*)symt_get_upper_inlined(curr_func); + break; + + case S_SSEARCH: + TRACE("Start search: seg=0x%x at offset 0x%08x\n", + cv_symbol->ssearch_v1.segment, cv_symbol->ssearch_v1.offset); + break; + + case S_ALIGN: + TRACE("S-Align V1\n"); + break; + case S_HEAPALLOCSITE: + TRACE("S-heap site V3: offset=0x%08x at sect_idx 0x%04x, inst_len 0x%08x, index 0x%08x\n", + cv_symbol->heap_alloc_site_v3.offset, cv_symbol->heap_alloc_site_v3.sect_idx, + cv_symbol->heap_alloc_site_v3.inst_len, cv_symbol->heap_alloc_site_v3.index); + break; + + case S_SEPCODE: + if (!top_func) + { + struct symt_ht* parent; + if ((result = pdb_reader_get_segment_address(pdb, cv_symbol->sepcode_v3.sectParent, cv_symbol->sepcode_v3.offParent, &address))) goto failure; + parent = symt_find_symbol_at(pdb->module, address); + if (symt_check_tag(&parent->symt, SymTagFunction)) + { + struct symt_function* pfunc = (struct symt_function*)parent; + if ((result = pdb_reader_get_segment_address(pdb, cv_symbol->sepcode_v3.sect, cv_symbol->sepcode_v3.off, &address))) goto failure; + top_func = symt_new_function(pdb->module, compiland, pfunc->hash_elt.name, + address, cv_symbol->sepcode_v3.length, pfunc->type, symbol_start_offset); + curr_func = top_func; + } + else + WARN("Couldn't find function referenced by S_SEPCODE at %04x:%08x\n", + cv_symbol->sepcode_v3.sectParent, cv_symbol->sepcode_v3.offParent); + } + else + { + FIXME("S_SEPCODE inside top-level function %s\n", debugstr_a(top_func->hash_elt.name)); + result = R_PDB_INVALID_PDB_FILE; + goto failure; + } + break; + + case S_FRAMEPROC: + /* expecting only S_FRAMEPROC once for top level functions */ + if (top_frame_size == -1 && curr_func && curr_func == top_func) + top_frame_size = cv_symbol->frame_info_v2.sz_frame; + else + { + FIXME("Unexpected S_FRAMEPROC %d (%p %p) %x\n", top_frame_size, top_func, curr_func, walker->offset); + result = R_PDB_INVALID_PDB_FILE; + goto failure; + } + break; + + case S_GMANPROC: + case S_LMANPROC: + walker->offset = cv_symbol->managed_proc_v3.pend; + if ((result = pdb_reader_symbol_skip_if(pdb, walker, S_END))) goto failure; + break; + + /* symbols only expected in globals' DBI stream */ + case S_PUB32: + case S_PROCREF: + case S_LPROCREF: + case S_TOKENREF: + case S_GDATA32: + case S_UDT: + PDB_REPORT_UNEXPECTED("(compiland stream) symbol id", cv_symbol->generic.id); + break; + + /* the symbols we can safely ignore for now */ + case S_SKIP: + case S_TRAMPOLINE: + case S_FRAMECOOKIE: + case S_SECTION: + case S_COFFGROUP: + case S_EXPORT: + case S_CALLSITEINFO: + case S_ARMSWITCHTABLE: + case S_CONSTANT: + case S_MANSLOT: + case S_OEM: + /* even if S_LOCAL groks all the S_DEFRANGE* records following itself, + * those kinds of records can also be present after a S_FILESTATIC record + * so silence them until (at least) S_FILESTATIC is supported + */ + case S_DEFRANGE_REGISTER: + case S_DEFRANGE_FRAMEPOINTER_REL: + case S_DEFRANGE_SUBFIELD_REGISTER: + case S_DEFRANGE_FRAMEPOINTER_REL_FULL_SCOPE: + case S_DEFRANGE_REGISTER_REL: + case S_BUILDINFO: + case S_FILESTATIC: + case S_CALLEES: + case S_CALLERS: + case S_UNAMESPACE: + case S_INLINEES: + case S_POGODATA: + TRACE("Unsupported symbol id %x\n", cv_symbol->generic.id); + break; + + default: + PDB_REPORT_UNEXPECTED("symbol id", cv_symbol->generic.id); + break; + } + pdb_reader_free(pdb, cv_symbol); + } + return R_PDB_SUCCESS; + +failure: + pdb_reader_free(pdb, cv_symbol); + return result; +} + +static enum pdb_result pdb_reader_ensure_symbols_loaded_from_compiland(struct pdb_reader *pdb, unsigned compiland_index) +{ + enum pdb_result result; + struct pdb_compiland *compiland = &pdb->compilands[compiland_index]; + + if (compiland_index >= pdb->num_compilands) return R_PDB_INVALID_ARGUMENT; + if (!compiland->are_symbols_loaded) + { + struct pdb_reader_walker walker; + char *obj_name; + + if ((result = pdb_reader_walker_init(pdb, PDB_STREAM_DBI, &walker))) return result; + walker.offset = compiland->stream_offset; + if ((result = pdb_reader_alloc_and_fetch_string(pdb, &walker, &obj_name))) return result; + compiland->compiland = symt_new_compiland(pdb->module, obj_name); + pdb_reader_free(pdb, obj_name); + + if ((result = pdb_reader_walker_init(pdb, compiland->stream_id, &walker))) return result; + walker.offset = sizeof(UINT32); + if ((result = pdb_reader_load_compiland_symbols(pdb, (struct symt_compiland *)compiland->compiland, &walker))) return result; + compiland->are_symbols_loaded = TRUE; + } + return R_PDB_SUCCESS; +} + static struct module_format_vtable pdb_module_format_vtable = { NULL,/*pdb_module_remove*/ From e6a4475eaeb48ef04142ec6db9fd17de62816f82 Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Thu, 15 May 2025 09:13:26 +0200 Subject: [PATCH 282/454] dbghelp: Workaround SAST false positive. Signed-off-by: Eric Pouech (cherry picked from commit 2f41ab32cd011d01868810bf023c7f9ec49ed9b6) --- dlls/dbghelp/pdb.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/dlls/dbghelp/pdb.c b/dlls/dbghelp/pdb.c index 96ee28585027..188e6791fc9e 100644 --- a/dlls/dbghelp/pdb.c +++ b/dlls/dbghelp/pdb.c @@ -2703,12 +2703,10 @@ static enum method_result pdb_reader_request_symref_t(struct pdb_reader *pdb, sy static enum method_result pdb_reader_request_cv_typeid(struct pdb_reader *pdb, cv_typ_t cv_typeid, IMAGEHLP_SYMBOL_TYPE_INFO req, void *data) { - enum pdb_result result; struct symref_code code; symref_t target_symref; - if ((result = pdb_reader_encode_symref(pdb, symref_code_init_from_cv_typeid(&code, cv_typeid), &target_symref)) != R_PDB_SUCCESS) - return pdb_method_result(result); + if (pdb_reader_encode_symref(pdb, symref_code_init_from_cv_typeid(&code, cv_typeid), &target_symref)) return MR_FAILURE; return pdb_reader_request_symref_t(pdb, target_symref, req, data); } From c42c02cb54bc50f75c90a0ae2fe3523b3bc7bf9e Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Thu, 15 May 2025 10:10:21 +0200 Subject: [PATCH 283/454] dbghelp: Skip compilands without MSF stream (new PDB). Signed-off-by: Eric Pouech (cherry picked from commit 025ac67566b22dd61a850ba1cdd4afdefab13a77) --- dlls/dbghelp/pdb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/dbghelp/pdb.c b/dlls/dbghelp/pdb.c index 188e6791fc9e..7b8ce182c7e4 100644 --- a/dlls/dbghelp/pdb.c +++ b/dlls/dbghelp/pdb.c @@ -1806,9 +1806,9 @@ static enum pdb_result pdb_reader_init_DBI(struct pdb_reader *pdb) for (i = 0; i < pdb->num_compilands; i++) { pdb->compilands[i].stream_offset = compiland_iter.dbi_walker.offset; - pdb->compilands[i].are_symbols_loaded = FALSE; pdb->compilands[i].compiland = NULL; pdb->compilands[i].stream_id = compiland_iter.dbi_cu_header.stream; + pdb->compilands[i].are_symbols_loaded = pdb->compilands[i].stream_id == 0xffff; result = pdb_reader_compiland_iterator_next(pdb, &compiland_iter); if ((result == R_PDB_SUCCESS) != (i + 1 < pdb->num_compilands)) return result ? result : R_PDB_INVALID_PDB_FILE; } From d3f2be6a75ac841913cd36acec072bdf524ac90c Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Fri, 9 May 2025 09:05:33 +0200 Subject: [PATCH 284/454] dbghelp: Add method to query backend for symbol by address. Implement it for PDB backend. Note that we'll merely use this method to ensure that containing compiland is fully loaded, and still return a symt ptr. Signed-off-by: Eric Pouech (cherry picked from commit 069d3f15a4c7666319dfc177798b971135942964) --- dlls/dbghelp/dbghelp_private.h | 1 + dlls/dbghelp/dwarf.c | 1 + dlls/dbghelp/msc.c | 1 + dlls/dbghelp/pdb.c | 35 ++++++++++++++++++++++++++++++++++ dlls/dbghelp/symbol.c | 29 +++++++++++++++++++++++++++- 5 files changed, 66 insertions(+), 1 deletion(-) diff --git a/dlls/dbghelp/dbghelp_private.h b/dlls/dbghelp/dbghelp_private.h index f4545612a555..bb176fb38f52 100644 --- a/dlls/dbghelp/dbghelp_private.h +++ b/dlls/dbghelp/dbghelp_private.h @@ -440,6 +440,7 @@ struct module_format_vtable /* index management */ enum method_result (*request_symref_t)(struct module_format *modfmt, symref_t ref, IMAGEHLP_SYMBOL_TYPE_INFO req, void *data); + enum method_result (*lookup_by_address)(struct module_format *modfmt, DWORD_PTR address, symref_t *symref); /* types management */ enum method_result (*find_type)(struct module_format *modfmt, const char *name, symref_t *ref); diff --git a/dlls/dbghelp/dwarf.c b/dlls/dbghelp/dwarf.c index 62e08eceb41b..7617786ebc44 100644 --- a/dlls/dbghelp/dwarf.c +++ b/dlls/dbghelp/dwarf.c @@ -4302,6 +4302,7 @@ static const struct module_format_vtable dwarf2_module_format_vtable = NULL, NULL, NULL, + NULL, dwarf2_location_compute, }; diff --git a/dlls/dbghelp/msc.c b/dlls/dbghelp/msc.c index b5ccddadb67e..10b1277bac24 100644 --- a/dlls/dbghelp/msc.c +++ b/dlls/dbghelp/msc.c @@ -4019,6 +4019,7 @@ static const struct module_format_vtable old_pdb_module_format_vtable = NULL, NULL, NULL, + NULL, pdb_location_compute, }; diff --git a/dlls/dbghelp/pdb.c b/dlls/dbghelp/pdb.c index 7b8ce182c7e4..457a7e63ab7b 100644 --- a/dlls/dbghelp/pdb.c +++ b/dlls/dbghelp/pdb.c @@ -4545,10 +4545,45 @@ static enum pdb_result pdb_reader_ensure_symbols_loaded_from_compiland(struct pd return R_PDB_SUCCESS; } +static enum pdb_result pdb_reader_lookup_top_symbol_by_segment_offset(struct pdb_reader *pdb, unsigned segment, unsigned offset, symref_t *symref) +{ + enum pdb_result result; + unsigned compiland_index; + DWORD64 in_address; + struct symt_ht *symbol; + + if ((result = pdb_reader_get_segment_address(pdb, segment, offset, &in_address))) return result; + result = pdb_reader_lookup_compiland_by_segment_offset(pdb, segment, offset, &compiland_index); + if (result == R_PDB_SUCCESS) + { + if ((result = pdb_reader_ensure_symbols_loaded_from_compiland(pdb, compiland_index))) + return result; + } + /* don't fail if not found as some symbols are only present in DBI, but not in compiland */ + else if (result != R_PDB_NOT_FOUND) return result; + /* fallback to ptr symbols lookup (as ptr should be loaded by now) */ + symbol = symt_find_symbol_at(pdb->module, in_address); + if (!symbol) return R_PDB_NOT_FOUND; + *symref = symt_ptr_to_symref(&symbol->symt); + return R_PDB_SUCCESS; +} + +static enum method_result pdb_method_lookup_symbol_by_address(struct module_format *modfmt, DWORD_PTR address, symref_t *symref) +{ + enum pdb_result result; + struct pdb_reader *pdb; + unsigned segment, offset; + + if (!pdb_hack_get_main_info(modfmt, &pdb, NULL)) return MR_FAILURE; + if ((result = pdb_reader_get_segment_offset_from_address(pdb, address, &segment, &offset))) return MR_FAILURE; + return pdb_method_result(pdb_reader_lookup_top_symbol_by_segment_offset(pdb, segment, offset, symref)); +} + static struct module_format_vtable pdb_module_format_vtable = { NULL,/*pdb_module_remove*/ pdb_method_request_symref_t, + pdb_method_lookup_symbol_by_address, pdb_method_find_type, pdb_method_enumerate_types, pdb_method_location_compute, diff --git a/dlls/dbghelp/symbol.c b/dlls/dbghelp/symbol.c index 9f8049e3330a..85eb76af15e4 100644 --- a/dlls/dbghelp/symbol.c +++ b/dlls/dbghelp/symbol.c @@ -1034,7 +1034,7 @@ static int symt_get_best_at(struct module* module, int idx_sorttab) } /* assume addr is in module */ -struct symt_ht* symt_find_nearest(struct module* module, DWORD_PTR addr) +static struct symt_ht* symt_find_nearest_internal(struct module* module, DWORD_PTR addr) { int mid, high, low; ULONG64 ref_addr, ref_size; @@ -1080,6 +1080,33 @@ struct symt_ht* symt_find_nearest(struct module* module, DWORD_PTR addr) return module->addr_sorttab[low]; } +struct symt_ht *symt_find_nearest(struct module *module, DWORD_PTR addr) +{ + static int recursive; + struct module_format_vtable_iterator iter = {}; + + /* prevent recursive lookup inside backend */ + if (!recursive++) + { + while ((module_format_vtable_iterator_next(module, &iter, + MODULE_FORMAT_VTABLE_INDEX(lookup_by_address)))) + { + symref_t symref; + enum method_result result = iter.modfmt->vtable->lookup_by_address(iter.modfmt, addr, &symref); + if (result == MR_SUCCESS) + { + recursive--; + if (symt_is_symref_ptr(symref)) return (struct symt_ht*)SYMT_SYMREF_TO_PTR(symref); + FIXME("No support for this case yet\n"); + return NULL; + } + /* fall back in all the other cases */ + } + } + recursive--; + return symt_find_nearest_internal(module, addr); +} + struct symt_ht* symt_find_symbol_at(struct module* module, DWORD_PTR addr) { struct symt_ht* nearest = symt_find_nearest(module, addr); From 7c26be624e18e005ada34cd69c87ad6d9c2d9397 Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Fri, 9 May 2025 09:23:28 +0200 Subject: [PATCH 285/454] dbghelp: Add method to query symbols by name. Implement it for PDB. It uses DBI hash for lookup, but still returns a symt ptr. It however forces the full containing compiland load. Signed-off-by: Eric Pouech (cherry picked from commit 15b920fd741f42476dee6507032b4d45badca204) --- dlls/dbghelp/dbghelp_private.h | 1 + dlls/dbghelp/dwarf.c | 1 + dlls/dbghelp/msc.c | 1 + dlls/dbghelp/pdb.c | 82 +++++++++++++++++++++++++++++++++- dlls/dbghelp/symbol.c | 19 ++++++++ 5 files changed, 103 insertions(+), 1 deletion(-) diff --git a/dlls/dbghelp/dbghelp_private.h b/dlls/dbghelp/dbghelp_private.h index bb176fb38f52..4f97048fdf3e 100644 --- a/dlls/dbghelp/dbghelp_private.h +++ b/dlls/dbghelp/dbghelp_private.h @@ -441,6 +441,7 @@ struct module_format_vtable /* index management */ enum method_result (*request_symref_t)(struct module_format *modfmt, symref_t ref, IMAGEHLP_SYMBOL_TYPE_INFO req, void *data); enum method_result (*lookup_by_address)(struct module_format *modfmt, DWORD_PTR address, symref_t *symref); + enum method_result (*lookup_by_name)(struct module_format *modfmt, const char *name, symref_t *symref); /* types management */ enum method_result (*find_type)(struct module_format *modfmt, const char *name, symref_t *ref); diff --git a/dlls/dbghelp/dwarf.c b/dlls/dbghelp/dwarf.c index 7617786ebc44..7044ee46eb92 100644 --- a/dlls/dbghelp/dwarf.c +++ b/dlls/dbghelp/dwarf.c @@ -4303,6 +4303,7 @@ static const struct module_format_vtable dwarf2_module_format_vtable = NULL, NULL, NULL, + NULL, dwarf2_location_compute, }; diff --git a/dlls/dbghelp/msc.c b/dlls/dbghelp/msc.c index 10b1277bac24..2b1ed69ecb56 100644 --- a/dlls/dbghelp/msc.c +++ b/dlls/dbghelp/msc.c @@ -4020,6 +4020,7 @@ static const struct module_format_vtable old_pdb_module_format_vtable = NULL, NULL, NULL, + NULL, pdb_location_compute, }; diff --git a/dlls/dbghelp/pdb.c b/dlls/dbghelp/pdb.c index 457a7e63ab7b..eb6da132c378 100644 --- a/dlls/dbghelp/pdb.c +++ b/dlls/dbghelp/pdb.c @@ -3570,7 +3570,7 @@ static enum pdb_result pdb_reader_init_IPI(struct pdb_reader *pdb) goto invalid_file; if (pdb->ipi_header.hash_value_size > sizeof(unsigned)) { - FIXME("Unexpected hash value size %u\n", pdb->ipi_header.hash_value_size); + PDB_REPORT_UNEXPECTED("IPI hash value size", pdb->ipi_header.hash_value_size); goto invalid_file; } pdb->ipi_walker.offset = pdb->ipi_header.type_offset; @@ -4579,11 +4579,91 @@ static enum method_result pdb_method_lookup_symbol_by_address(struct module_form return pdb_method_result(pdb_reader_lookup_top_symbol_by_segment_offset(pdb, segment, offset, symref)); } +static enum pdb_result pdb_reader_dereference_procedure(struct pdb_reader *pdb, unsigned compiland_id, pdbsize_t stream_offset, + unsigned *segment, unsigned *offset) +{ + enum pdb_result result; + struct pdb_reader_walker walker; + union codeview_symbol cv_symbol; + unsigned stream_id; + + if (!compiland_id || compiland_id > pdb->num_compilands) return R_PDB_INVALID_ARGUMENT; + compiland_id--; + stream_id = pdb->compilands[compiland_id].stream_id; + + if ((result = pdb_reader_walker_init(pdb, stream_id, &walker))) return result; + walker.offset = stream_offset; + if ((result = pdb_reader_read_partial_codeview_symbol(pdb, &walker, &cv_symbol))) return result; + switch (cv_symbol.generic.id) + { + case S_GPROC32: + case S_LPROC32: + *segment = cv_symbol.proc_v3.segment; + *offset = cv_symbol.proc_v3.offset; + break; + + default: + PDB_REPORT_UNEXPECTED("codeview symbol-id", cv_symbol.generic.id); + /* fall through */ + case S_OBJNAME: + case S_COMPILE: + case S_COMPILE2: + case S_COMPILE3: + case S_BUILDINFO: + case S_UDT: + case S_UNAMESPACE: + case S_GMANPROC: + case S_LMANPROC: + return R_PDB_NOT_FOUND; + } + + return result; +} + +static enum method_result pdb_method_lookup_symbol_by_name(struct module_format *modfmt, const char *name, symref_t *symref) +{ + enum pdb_result result; + struct pdb_reader *pdb; + union codeview_symbol cv_symbol; + pdbsize_t globals_offset; + unsigned segment; + unsigned offset; + + if (!pdb_hack_get_main_info(modfmt, &pdb, NULL)) return MR_FAILURE; + + if ((result = pdb_reader_read_DBI_codeview_symbol_by_name(pdb, name, &globals_offset, &cv_symbol))) + return pdb_method_result(result); + + switch (cv_symbol.generic.id) + { + case S_GDATA32: + case S_LDATA32: + segment = cv_symbol.data_v3.segment; + offset = cv_symbol.data_v3.offset; + break; + case S_PROCREF: + case S_LPROCREF: + if ((result = pdb_reader_dereference_procedure(pdb, cv_symbol.refsym2_v3.imod, cv_symbol.refsym2_v3.ibSym, + &segment, &offset))) + { + return MR_FAILURE; + } + break; + default: + return MR_FAILURE; + } + result = pdb_reader_lookup_top_symbol_by_segment_offset(pdb, segment, offset, symref); + if (result == R_PDB_SUCCESS) return MR_SUCCESS; + TRACE("No symbol %s found...\n", name); + return MR_NOT_FOUND; +} + static struct module_format_vtable pdb_module_format_vtable = { NULL,/*pdb_module_remove*/ pdb_method_request_symref_t, pdb_method_lookup_symbol_by_address, + pdb_method_lookup_symbol_by_name, pdb_method_find_type, pdb_method_enumerate_types, pdb_method_location_compute, diff --git a/dlls/dbghelp/symbol.c b/dlls/dbghelp/symbol.c index 85eb76af15e4..9c900344bd7d 100644 --- a/dlls/dbghelp/symbol.c +++ b/dlls/dbghelp/symbol.c @@ -1610,6 +1610,7 @@ BOOL WINAPI SymGetSymFromAddr64(HANDLE hProcess, DWORD64 Address, static BOOL find_name(struct process* pcs, struct module* module, const char* name, SYMBOL_INFO* symbol) { + struct module_format_vtable_iterator iter = {}; struct hash_table_iter hti; void* ptr; struct symt_ht* sym = NULL; @@ -1619,6 +1620,24 @@ static BOOL find_name(struct process* pcs, struct module* module, const char* na if (!(pair.requested = module)) return FALSE; if (!module_get_debug(&pair)) return FALSE; + while ((module_format_vtable_iterator_next(pair.effective, &iter, + MODULE_FORMAT_VTABLE_INDEX(lookup_by_name)))) + { + symref_t symref; + enum method_result result = iter.modfmt->vtable->lookup_by_name(iter.modfmt, name, &symref); + if (result == MR_SUCCESS) + { + if (symt_is_symref_ptr(symref)) + { + symt_fill_sym_info(&pair, NULL, SYMT_SYMREF_TO_PTR(symref), symbol); + return TRUE; + } + FIXME("Not expected case\n"); + return FALSE; + } + if (result != MR_NOT_FOUND) return FALSE; + } + hash_table_iter_init(&pair.effective->ht_symbols, &hti, name); while ((ptr = hash_table_iter_up(&hti))) { From e9074f30418b47bdbd184911f26e60d0d2713678 Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Fri, 9 May 2025 09:33:01 +0200 Subject: [PATCH 286/454] dbghelp: Add method to enumerate symbols. Implement it for PDB backend. It ensures containing compiland of every matched symbol is actually loaded. Signed-off-by: Eric Pouech (cherry picked from commit 80278c34bd7d93dcfcb59c321bfb7f271cf1eda5) --- dlls/dbghelp/dbghelp_private.h | 1 + dlls/dbghelp/dwarf.c | 1 + dlls/dbghelp/msc.c | 1 + dlls/dbghelp/pdb.c | 68 ++++++++++++++++++++++++++++++++++ dlls/dbghelp/symbol.c | 33 +++++++++++++++++ 5 files changed, 104 insertions(+) diff --git a/dlls/dbghelp/dbghelp_private.h b/dlls/dbghelp/dbghelp_private.h index 4f97048fdf3e..e6df0721fdaa 100644 --- a/dlls/dbghelp/dbghelp_private.h +++ b/dlls/dbghelp/dbghelp_private.h @@ -442,6 +442,7 @@ struct module_format_vtable enum method_result (*request_symref_t)(struct module_format *modfmt, symref_t ref, IMAGEHLP_SYMBOL_TYPE_INFO req, void *data); enum method_result (*lookup_by_address)(struct module_format *modfmt, DWORD_PTR address, symref_t *symref); enum method_result (*lookup_by_name)(struct module_format *modfmt, const char *name, symref_t *symref); + enum method_result (*enumerate_symbols)(struct module_format *modfmt, const WCHAR *match, BOOL (*cb)(symref_t, const char *, void *), void *user); /* types management */ enum method_result (*find_type)(struct module_format *modfmt, const char *name, symref_t *ref); diff --git a/dlls/dbghelp/dwarf.c b/dlls/dbghelp/dwarf.c index 7044ee46eb92..12b7f28a22bd 100644 --- a/dlls/dbghelp/dwarf.c +++ b/dlls/dbghelp/dwarf.c @@ -4304,6 +4304,7 @@ static const struct module_format_vtable dwarf2_module_format_vtable = NULL, NULL, NULL, + NULL, dwarf2_location_compute, }; diff --git a/dlls/dbghelp/msc.c b/dlls/dbghelp/msc.c index 2b1ed69ecb56..c57217205378 100644 --- a/dlls/dbghelp/msc.c +++ b/dlls/dbghelp/msc.c @@ -4021,6 +4021,7 @@ static const struct module_format_vtable old_pdb_module_format_vtable = NULL, NULL, NULL, + NULL, pdb_location_compute, }; diff --git a/dlls/dbghelp/pdb.c b/dlls/dbghelp/pdb.c index eb6da132c378..eb4e32025415 100644 --- a/dlls/dbghelp/pdb.c +++ b/dlls/dbghelp/pdb.c @@ -4658,12 +4658,80 @@ static enum method_result pdb_method_lookup_symbol_by_name(struct module_format return MR_NOT_FOUND; } +static enum method_result pdb_method_enumerate_symbols(struct module_format *modfmt, const WCHAR *match, BOOL (*cb)(symref_t, const char *, void *), void *user) +{ + enum pdb_result result; + struct pdb_reader *pdb; + struct pdb_reader_walker walker, symbol_walker; + union codeview_symbol cv_symbol; + unsigned segment; + unsigned offset; + char *symbol_name; + + if (!pdb_hack_get_main_info(modfmt, &pdb, NULL)) return MR_FAILURE; + + /* FIXME could be optimized if match doesn't contain wild cards */ + /* this is currently ugly, but basically we just ensure that all the compilands which contain matching symbols + * are actually loaded, and fall back to generic mode... + */ + if ((result = pdb_reader_walker_init(pdb, pdb->dbi_header.gsym_stream, &walker))) return pdb_method_result(result); + while (pdb_reader_read_partial_codeview_symbol(pdb, &walker, &cv_symbol) == R_PDB_SUCCESS) + { + symbol_name = NULL; + symbol_walker = walker; + symbol_walker.offset -= sizeof(cv_symbol.generic.len); + switch (cv_symbol.generic.id) + { + case S_GDATA32: + case S_LDATA32: + segment = cv_symbol.data_v3.segment; + offset = cv_symbol.data_v3.offset; + symbol_walker.offset += offsetof(union codeview_symbol, data_v3.name); + if ((result = pdb_reader_alloc_and_fetch_string(pdb, &symbol_walker, &symbol_name))) return pdb_method_result(result); + break; + case S_PROCREF: + case S_LPROCREF: + if ((result = pdb_reader_dereference_procedure(pdb, cv_symbol.refsym2_v3.imod, cv_symbol.refsym2_v3.ibSym, + &segment, &offset))) + { + return pdb_method_result(result); + } + symbol_walker.offset += offsetof(union codeview_symbol, refsym2_v3.name); + if ((result = pdb_reader_alloc_and_fetch_string(pdb, &symbol_walker, &symbol_name))) return pdb_method_result(result); + break; + case S_UDT: + case S_CONSTANT: + case S_PUB32: + break; + default: + PDB_REPORT_UNEXPECTED("codeview symbol-id", cv_symbol.generic.id); + break; + } + if (symbol_name) + { + BOOL do_continue = TRUE; + symref_t symref; + + if (symt_match_stringAW(symbol_name, match, TRUE) && + pdb_reader_lookup_top_symbol_by_segment_offset(pdb, segment, offset, &symref) == R_PDB_SUCCESS) + { + do_continue = cb(symref, symbol_name, user); + } + pdb_reader_free(pdb, symbol_name); + if (!do_continue) return MR_SUCCESS; + } + walker.offset += cv_symbol.generic.len; + } + return MR_FAILURE; +} + static struct module_format_vtable pdb_module_format_vtable = { NULL,/*pdb_module_remove*/ pdb_method_request_symref_t, pdb_method_lookup_symbol_by_address, pdb_method_lookup_symbol_by_name, + pdb_method_enumerate_symbols, pdb_method_find_type, pdb_method_enumerate_types, pdb_method_location_compute, diff --git a/dlls/dbghelp/symbol.c b/dlls/dbghelp/symbol.c index 9c900344bd7d..7ab11e2f4eb8 100644 --- a/dlls/dbghelp/symbol.c +++ b/dlls/dbghelp/symbol.c @@ -892,15 +892,48 @@ static BOOL send_symbol(const struct sym_enum* se, struct module_pair* pair, return !se->cb(se->sym_info, se->sym_info->Size, se->user); } +struct symbol_enum_method +{ + struct module_pair *pair; + const struct sym_enum *se; +}; + +static BOOL symbol_enum_method_cb(symref_t symref, const char *name, void *user) +{ + struct symbol_enum_method *sem = user; + + sem->se->sym_info->SizeOfStruct = sizeof(SYMBOL_INFO); + sem->se->sym_info->MaxNameLen = sizeof(sem->se->buffer) - sizeof(SYMBOL_INFO); + + if (symt_is_symref_ptr(symref)) + { + if (send_symbol(sem->se, sem->pair, NULL, (struct symt*)symref)) return TRUE; + } + else FIXME("No support for this case yet %Ix\n", symref); + return TRUE; +} + static BOOL symt_enum_module(struct module_pair* pair, const WCHAR* match, const struct sym_enum* se) { + struct module_format_vtable_iterator iter = {}; void* ptr; struct symt_ht* sym = NULL; struct hash_table_iter hti; WCHAR* nameW; BOOL ret; + while ((module_format_vtable_iterator_next(pair->effective, &iter, + MODULE_FORMAT_VTABLE_INDEX(enumerate_symbols)))) + { + struct symbol_enum_method sem = {pair, se}; + enum method_result result = iter.modfmt->vtable->enumerate_symbols(iter.modfmt, match, symbol_enum_method_cb, &sem); + + if (result == MR_SUCCESS) return TRUE; + if (result == MR_FAILURE) return FALSE; + /* fall back in all the other cases */ + } + hash_table_iter_init(&pair->effective->ht_symbols, &hti, NULL); while ((ptr = hash_table_iter_up(&hti))) { From 03bf072826a60bd154ce02d5b124fa070f36ece2 Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Fri, 9 May 2025 09:42:38 +0200 Subject: [PATCH 287/454] dbghelp: Load compilands on demand (new PDB). (instead of fully loading all compilands at startup). Signed-off-by: Eric Pouech (cherry picked from commit 0ac1033f04074339de5cae9ffd9f4f45baaef92e) --- dlls/dbghelp/pdb.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/dlls/dbghelp/pdb.c b/dlls/dbghelp/pdb.c index eb4e32025415..c0dd06e411cd 100644 --- a/dlls/dbghelp/pdb.c +++ b/dlls/dbghelp/pdb.c @@ -1779,9 +1779,6 @@ static enum pdb_result pdb_reader_read_DBI_codeview_symbol_by_name(struct pdb_re return R_PDB_NOT_FOUND; } -/* FIXME temp forward */ -static enum pdb_result pdb_reader_ensure_symbols_loaded_from_compiland(struct pdb_reader *pdb, unsigned compiland_index); - static enum pdb_result pdb_reader_init_DBI(struct pdb_reader *pdb) { enum pdb_result result; @@ -1812,11 +1809,6 @@ static enum pdb_result pdb_reader_init_DBI(struct pdb_reader *pdb) result = pdb_reader_compiland_iterator_next(pdb, &compiland_iter); if ((result == R_PDB_SUCCESS) != (i + 1 < pdb->num_compilands)) return result ? result : R_PDB_INVALID_PDB_FILE; } - /* TEMP force loading of all compilands */ - for (i = 0; i < pdb->num_compilands; i++) - { - if ((result = pdb_reader_ensure_symbols_loaded_from_compiland(pdb, i))) return result; - } if ((result = pdb_reader_load_DBI_hash_table(pdb))) return result; /* register the globals entries not bound to a compiland */ From dec72391e25d861143377d4c5aa6d083c31554e7 Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Fri, 30 May 2025 14:56:49 +0200 Subject: [PATCH 288/454] dbghelp: Speed up global symbols at startup. Also introducting helpers to handle a stream as a whole chunk of memory. Signed-off-by: Eric Pouech (cherry picked from commit 7b8a89e5e804fadcc7601d1eee6d59ea77e2a9ce) --- dlls/dbghelp/pdb.c | 140 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 106 insertions(+), 34 deletions(-) diff --git a/dlls/dbghelp/pdb.c b/dlls/dbghelp/pdb.c index c0dd06e411cd..12baa0f86832 100644 --- a/dlls/dbghelp/pdb.c +++ b/dlls/dbghelp/pdb.c @@ -1779,12 +1779,79 @@ static enum pdb_result pdb_reader_read_DBI_codeview_symbol_by_name(struct pdb_re return R_PDB_NOT_FOUND; } +struct pdb_reader_whole_stream +{ + unsigned short stream_id; + const BYTE *data; +}; + +static enum pdb_result pdb_reader_alloc_and_load_whole_stream(struct pdb_reader *pdb, unsigned short stream_id, struct pdb_reader_whole_stream *whole) +{ + enum pdb_result result; + const uint32_t *blocks; + unsigned num_blocks, i, j; + BYTE *buffer; + + memset(whole, 0, sizeof(*whole)); + if (stream_id >= pdb->toc->num_streams) return R_PDB_INVALID_ARGUMENT; + if (pdb->toc->stream_size[stream_id] == 0 || pdb->toc->stream_size[stream_id] == 0xFFFFFFFF) return R_PDB_NOT_FOUND; + + blocks = pdb->streams[stream_id].blocks; + num_blocks = ((pdboff_t)pdb->toc->stream_size[stream_id] + pdb->block_size - 1) / pdb->block_size; + buffer = HeapAlloc(GetProcessHeap(), 0, num_blocks * pdb->block_size); + if (!buffer) return R_PDB_OUT_OF_MEMORY; + + for (i = 0; i < num_blocks; i = j) + { + /* find all contiguous blocks to read them at once */ + for (j = i + 1; j < num_blocks && blocks[j] == blocks[j - 1] + 1; j++) {} + if ((result = pdb_reader_fetch_file_no_cache(pdb, buffer + i * pdb->block_size, + (pdboff_t)blocks[i] * pdb->block_size, (j - i) * pdb->block_size))) + { + HeapFree(GetProcessHeap(), 0, buffer); + return result; + } + } + whole->stream_id = stream_id; + whole->data = buffer; + return R_PDB_SUCCESS; +} + +static enum pdb_result pdb_reader_dispose_whole_stream(struct pdb_reader *pdb, struct pdb_reader_whole_stream *whole) +{ + HeapFree(GetProcessHeap(), 0, (void *)whole->data); + return R_PDB_SUCCESS; +} + +static enum pdb_result pdb_reader_whole_stream_access_codeview_symbol(struct pdb_reader *pdb, struct pdb_reader_whole_stream *whole, + unsigned offset, const union codeview_symbol **cv_symbol) +{ + const union codeview_symbol *cv = (const void *)(whole->data + offset); + if (!whole->data || + offset + sizeof(cv->generic) > pdb->toc->stream_size[whole->stream_id] || + offset + sizeof(cv->generic.len) + cv->generic.len > pdb->toc->stream_size[whole->stream_id]) return R_PDB_INVALID_ARGUMENT; + *cv_symbol = cv; + return R_PDB_SUCCESS; +} + +static int my_action_global_obj_cmp(const void *p1, const void *p2) +{ + pdbsize_t o1 = ((const struct pdb_action_entry *)p1)->stream_offset; + pdbsize_t o2 = ((const struct pdb_action_entry *)p2)->stream_offset; + + if (o1 < o2) return -1; + if (o1 > o2) return +1; + return 0; +} + static enum pdb_result pdb_reader_init_DBI(struct pdb_reader *pdb) { enum pdb_result result; struct pdb_reader_compiland_iterator compiland_iter; struct pdb_reader_walker walker; - union codeview_symbol cv_symbol; + struct pdb_reader_whole_stream whole; + const union codeview_symbol *cv_global_symbol, *cv_global_symbol2; + unsigned hash; symref_t symref; unsigned i; @@ -1811,40 +1878,43 @@ static enum pdb_result pdb_reader_init_DBI(struct pdb_reader *pdb) } if ((result = pdb_reader_load_DBI_hash_table(pdb))) return result; - /* register the globals entries not bound to a compiland */ - if ((result = pdb_reader_walker_init(pdb, pdb->dbi_header.gsym_stream, &walker))) return result; - while (pdb_reader_READ(pdb, &walker, &cv_symbol.generic) == R_PDB_SUCCESS) + if ((result = pdb_reader_alloc_and_load_whole_stream(pdb, pdb->dbi_header.gsym_stream, &whole))) return result; + + for (hash = 0; hash < DBI_MAX_HASH; hash++) { - switch (cv_symbol.generic.id) + struct pdb_dbi_hash_entry *entry, *entry2; + DWORD64 address; + symref_t type_symref; + BOOL found; + + if (pdb->dbi_symbols_hash[hash].next == &pdb->dbi_symbols_hash[hash]) continue; + for (entry = &pdb->dbi_symbols_hash[hash]; entry; entry = entry->next) { - case S_UDT: - if ((result = pdb_reader_push_action(pdb, action_type_globals, walker.offset - sizeof(cv_symbol.generic), - cv_symbol.generic.len + sizeof(cv_symbol.generic.len), 0, &symref))) return result; - break; - case S_GDATA32: + if (!pdb_reader_whole_stream_access_codeview_symbol(pdb, &whole, entry->dbi_stream_offset, &cv_global_symbol)) { - DWORD64 address; - symref_t type_symref; - union codeview_symbol *cv_global_symbol; - union codeview_symbol zzcv; - struct pdb_reader_walker global_walker = walker; - pdbsize_t global_hash_offset, global_offset; - - /* There are cases (incremental linking) where we have several entries of same name, but - * only one is valid. - * We discriminate valid with: - * - the hash for that name points to this global entry - * - the address is valid - * - the typeid is valid - * Note: checking address map doesn't bring nothing as the invalid entries are also listed - * there. - */ - global_walker.offset -= sizeof(cv_symbol.generic); - global_offset = global_walker.offset; - if (!pdb_reader_alloc_and_read_full_codeview_symbol(pdb, &global_walker, &cv_global_symbol)) + switch (cv_global_symbol->generic.id) { - if (!pdb_reader_read_DBI_codeview_symbol_by_name(pdb, cv_global_symbol->data_v3.name, &global_hash_offset, &zzcv) && - global_hash_offset == global_offset && + case S_UDT: + if ((result = pdb_reader_push_action(pdb, action_type_globals, entry->dbi_stream_offset, + cv_global_symbol->generic.len + sizeof(cv_global_symbol->generic.len), 0, &symref))) return result; + break; + case S_GDATA32: + /* There are cases (incremental linking) where we have several entries of same name, but + * only one is valid. + * We discriminate valid with: + * - there's no other entry with same name before this entry in hash bucket, + * - the address is valid + * - the typeid is valid + * Note: checking address map doesn't bring nothing as the invalid entries are also listed + * there. + */ + found = FALSE; + for (entry2 = &pdb->dbi_symbols_hash[hash]; !found && entry2 && entry2 != entry; entry2 = entry2->next) + { + if (!pdb_reader_whole_stream_access_codeview_symbol(pdb, &whole, entry2->dbi_stream_offset, &cv_global_symbol2)) + found = !strcmp(cv_global_symbol->data_v3.name, cv_global_symbol2->data_v3.name); + } + if (!found && !pdb_reader_get_segment_address(pdb, cv_global_symbol->data_v3.segment, cv_global_symbol->data_v3.offset, &address) && !pdb_reader_symref_from_cv_typeid(pdb, cv_global_symbol->data_v3.symtype, &type_symref)) { @@ -1852,14 +1922,16 @@ static enum pdb_result pdb_reader_init_DBI(struct pdb_reader *pdb) symt_new_global_variable(pdb->module, 0, cv_global_symbol->data_v3.name, FALSE, loc, 0, type_symref); } - pdb_reader_free(pdb, cv_global_symbol); + break; } } - break; } - walker.offset += cv_symbol.generic.len - sizeof(cv_symbol.generic.id); } + if ((result = pdb_reader_dispose_whole_stream(pdb, &whole))) return result; pdb->num_action_globals = pdb->num_action_entries; + /* as we walked the DBI stream according to hash order, resort by stream_offset */ + qsort(pdb->action_store, pdb->num_action_globals, sizeof(pdb->action_store[0]), + &my_action_global_obj_cmp); return R_PDB_SUCCESS; } From dd46c24afc7049b1e506d503960405040ebfb213 Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Fri, 6 Jun 2025 10:49:44 +0200 Subject: [PATCH 289/454] dbghelp: Get fpo stream information directly in new PDB reader. Signed-off-by: Eric Pouech (cherry picked from commit df9e1b85ec00c77a38198cbed5cc74a248e85149) --- dlls/dbghelp/pdb.c | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/dlls/dbghelp/pdb.c b/dlls/dbghelp/pdb.c index 12baa0f86832..c371cc73fd8c 100644 --- a/dlls/dbghelp/pdb.c +++ b/dlls/dbghelp/pdb.c @@ -150,6 +150,7 @@ struct pdb_reader unsigned num_action_entries; struct pdb_action_entry *action_store; struct pdb_dbi_hash_entry *dbi_symbols_hash; + unsigned short dbi_substreams[16]; /* 0 means non existing stream */ /* compilands */ unsigned num_compilands; @@ -1844,6 +1845,27 @@ static int my_action_global_obj_cmp(const void *p1, const void *p2) return 0; } +static enum pdb_result pdb_reader_init_DBI_substreams(struct pdb_reader *pdb) +{ + enum pdb_result result; + struct pdb_reader_walker walker; + PDB_SYMBOLS dbi_header; + unsigned i; + unsigned short streamid; + + if ((result = pdb_reader_read_DBI_header(pdb, &dbi_header, &walker))) return result; + walker.offset += dbi_header.module_size + dbi_header.sectcontrib_size + + dbi_header.segmap_size + dbi_header.srcmodule_size + + dbi_header.pdbimport_size + dbi_header.unknown2_size; + walker.last = walker.offset + dbi_header.stream_index_size; + for (i = 0; i < ARRAY_SIZE(pdb->dbi_substreams); i++) + { + if (pdb_reader_READ(pdb, &walker, &streamid)) streamid = 0xffff; + pdb->dbi_substreams[i] = streamid; + } + return R_PDB_SUCCESS; +} + static enum pdb_result pdb_reader_init_DBI(struct pdb_reader *pdb) { enum pdb_result result; @@ -1933,6 +1955,8 @@ static enum pdb_result pdb_reader_init_DBI(struct pdb_reader *pdb) qsort(pdb->action_store, pdb->num_action_globals, sizeof(pdb->action_store[0]), &my_action_global_obj_cmp); + if ((result = pdb_reader_init_DBI_substreams(pdb))) return result; + return R_PDB_SUCCESS; } @@ -5141,13 +5165,13 @@ BOOL pdb_virtual_unwind(struct cpu_stack_walk *csw, DWORD_PTR ip, BOOL ret = FALSE; if (!module_init_pair(&pair, csw->hProcess, ip)) return FALSE; - if (!pdb_hack_get_main_info(pair.effective->format_info[DFI_PDB], &pdb, &fpoext_stream)) return FALSE; - + if (!pdb_hack_get_main_info(pair.effective->format_info[DFI_PDB], &pdb, NULL)) return FALSE; if (!pdb) return pdb_old_virtual_unwind(csw, ip, context, cpair); TRACE("searching %Ix => %Ix\n", ip, ip - (DWORD_PTR)pair.effective->module.BaseOfImage); ip -= (DWORD_PTR)pair.effective->module.BaseOfImage; + fpoext_stream = pdb->dbi_substreams[PDB_SIDX_FPOEXT]; if (!pdb_reader_walker_init(pdb, fpoext_stream, &walker) && (walker.last % sizeof(fpoext)) == 0) { From ae2461f3069110f4ab6579f55e0d8d57ec29f9dd Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Fri, 6 Jun 2025 13:22:19 +0200 Subject: [PATCH 290/454] dbghelp: Simplify signature of PDB unwinders. Signed-off-by: Eric Pouech (cherry picked from commit db344ff63f8e1fd3ffea98f19709412007a69361) --- dlls/dbghelp/cpu_i386.c | 10 ++---- dlls/dbghelp/dbghelp_private.h | 22 ++++++------- dlls/dbghelp/msc.c | 5 ++- dlls/dbghelp/pdb.c | 58 +++++++++++++++++++++------------- 4 files changed, 50 insertions(+), 45 deletions(-) diff --git a/dlls/dbghelp/cpu_i386.c b/dlls/dbghelp/cpu_i386.c index a322dfcdda32..42e4c8820094 100644 --- a/dlls/dbghelp/cpu_i386.c +++ b/dlls/dbghelp/cpu_i386.c @@ -95,8 +95,7 @@ static BOOL fetch_next_frame32(struct cpu_stack_walk* csw, union ctx *pcontext, DWORD_PTR curr_pc) { DWORD64 xframe; - struct pdb_cmd_pair cpair[4]; - DWORD val32; + DWORD val32; WOW64_CONTEXT *context = &pcontext->x86; if (dwarf2_virtual_unwind(csw, curr_pc, pcontext, &xframe)) @@ -104,12 +103,9 @@ static BOOL fetch_next_frame32(struct cpu_stack_walk* csw, context->Esp = xframe; return TRUE; } - cpair[0].name = "$ebp"; cpair[0].pvalue = &context->Ebp; - cpair[1].name = "$esp"; cpair[1].pvalue = &context->Esp; - cpair[2].name = "$eip"; cpair[2].pvalue = &context->Eip; - cpair[3].name = NULL; cpair[3].pvalue = NULL; - if (!pdb_virtual_unwind(csw, curr_pc, pcontext, cpair)) + if (!pdb_virtual_unwind(csw, curr_pc, pcontext) && + !old_pdb_virtual_unwind(csw, curr_pc, pcontext)) { /* do a simple unwind using ebp * we assume a "regular" prologue in the function has been used diff --git a/dlls/dbghelp/dbghelp_private.h b/dlls/dbghelp/dbghelp_private.h index e6df0721fdaa..f4486166d442 100644 --- a/dlls/dbghelp/dbghelp_private.h +++ b/dlls/dbghelp/dbghelp_private.h @@ -827,14 +827,9 @@ extern BOOL pe_load_debug_directory(const struct process* pcs, const IMAGE_DEBUG_DIRECTORY* dbg, int nDbg); extern DWORD msc_get_file_indexinfo(void* image, const IMAGE_DEBUG_DIRECTORY* dbgdir, DWORD size, SYMSRV_INDEX_INFOW* info); -struct pdb_cmd_pair { - const char* name; - DWORD* pvalue; -}; -extern BOOL pdb_virtual_unwind(struct cpu_stack_walk *csw, DWORD_PTR ip, - union ctx *context, struct pdb_cmd_pair *cpair); -extern DWORD pdb_get_file_indexinfo(void* image, DWORD size, SYMSRV_INDEX_INFOW* info); -extern DWORD dbg_get_file_indexinfo(void* image, DWORD size, SYMSRV_INDEX_INFOW* info); +extern DWORD pdb_get_file_indexinfo(void* image, DWORD size, SYMSRV_INDEX_INFOW* info); +extern DWORD dbg_get_file_indexinfo(void* image, DWORD size, SYMSRV_INDEX_INFOW* info); +extern BOOL old_pdb_virtual_unwind(struct cpu_stack_walk *csw, DWORD_PTR ip, union ctx *context); /* path.c */ extern BOOL path_find_symbol_file(const struct process *pcs, const struct module *module, @@ -847,6 +842,12 @@ extern BOOL search_unix_path(const WCHAR *name, const WCHAR *path, BOOL (*match) extern const WCHAR* file_name(const WCHAR* str); extern const char* file_nameA(const char* str); +/* pdb.c */ +extern BOOL pdb_virtual_unwind(struct cpu_stack_walk *csw, DWORD_PTR ip, union ctx *context); +struct _PDB_FPO_DATA; +extern BOOL pdb_fpo_unwind_parse_cmd_string(struct cpu_stack_walk* csw, struct _PDB_FPO_DATA* fpoext, + const char* cmd, WOW64_CONTEXT *context); + /* pe_module.c */ extern BOOL pe_load_nt_header(HANDLE hProc, DWORD64 base, IMAGE_NT_HEADERS* nth, BOOL* is_builtin); extern struct module* @@ -1087,11 +1088,6 @@ extern struct symt_function* #define IFC_DEPTH(x) ((x) & IFC_DEPTH_MASK) /* temporary helpers for PDB rewriting */ -struct _PDB_FPO_DATA; -extern BOOL pdb_fpo_unwind_parse_cmd_string(struct cpu_stack_walk* csw, struct _PDB_FPO_DATA* fpoext, - const char* cmd, struct pdb_cmd_pair* cpair); -extern BOOL pdb_old_virtual_unwind(struct cpu_stack_walk *csw, DWORD_PTR ip, - union ctx *context, struct pdb_cmd_pair *cpair); struct pdb_reader; extern BOOL pdb_hack_get_main_info(struct module_format *modfmt, struct pdb_reader **pdb, unsigned *fpoext_stream); extern void pdb_reader_dispose(struct pdb_reader *pdb); diff --git a/dlls/dbghelp/msc.c b/dlls/dbghelp/msc.c index c57217205378..4fa1134b20ce 100644 --- a/dlls/dbghelp/msc.c +++ b/dlls/dbghelp/msc.c @@ -4409,8 +4409,7 @@ DWORD dbg_get_file_indexinfo(void* image, DWORD size, SYMSRV_INDEX_INFOW* info) return msc_get_file_indexinfo(image, dbg, num_directories, info); } -BOOL pdb_old_virtual_unwind(struct cpu_stack_walk *csw, DWORD_PTR ip, - union ctx *context, struct pdb_cmd_pair *cpair) +BOOL old_pdb_virtual_unwind(struct cpu_stack_walk *csw, DWORD_PTR ip, union ctx *context) { struct module_pair pair; struct pdb_module_info* pdb_info; @@ -4443,7 +4442,7 @@ BOOL pdb_old_virtual_unwind(struct cpu_stack_walk *csw, DWORD_PTR ip, debugstr_a(pdb_get_string_table_entry(strbase, fpoext[i].str_offset))); ret = pdb_fpo_unwind_parse_cmd_string(csw, &fpoext[i], pdb_get_string_table_entry(strbase, fpoext[i].str_offset), - cpair); + &context->x86); break; } } diff --git a/dlls/dbghelp/pdb.c b/dlls/dbghelp/pdb.c index c371cc73fd8c..a8420d1c6986 100644 --- a/dlls/dbghelp/pdb.c +++ b/dlls/dbghelp/pdb.c @@ -5070,8 +5070,7 @@ static BOOL pev_assign(struct pevaluator* pev) } /* initializes the postfix evaluator */ -static void pev_init(struct pevaluator* pev, struct cpu_stack_walk* csw, - const PDB_FPO_DATA* fpoext, struct pdb_cmd_pair* cpair) +static void pev_init(struct pevaluator* pev, struct cpu_stack_walk* csw) { pev->csw = csw; pool_init(&pev->pool, 512); @@ -5079,29 +5078,41 @@ static void pev_init(struct pevaluator* pev, struct cpu_stack_walk* csw, pev->stk_index = 0; hash_table_init(&pev->pool, &pev->values, 8); pev->error[0] = '\0'; - for (; cpair->name; cpair++) - pev_set_value(pev, cpair->name, *cpair->pvalue); +} + +static void pev_push_context(struct pevaluator *pev, const WOW64_CONTEXT *context) +{ + pev_set_value(pev, "$ebp", context->Ebp); + pev_set_value(pev, "$esp", context->Esp); + pev_set_value(pev, "$eip", context->Eip); +} + +static void pev_pop_context(struct pevaluator *pev, WOW64_CONTEXT *context) +{ + DWORD_PTR val; + + if (pev_get_val(pev, "$ebp", &val)) context->Ebp = val; + if (pev_get_val(pev, "$esp", &val)) context->Esp = val; + if (pev_get_val(pev, "$eip", &val)) context->Eip = val; +} + +static void pev_push_fpodata(struct pevaluator *pev, const PDB_FPO_DATA* fpoext) +{ + pev_set_value(pev, ".raSearchStart", fpoext->start); pev_set_value(pev, ".cbLocals", fpoext->locals_size); pev_set_value(pev, ".cbParams", fpoext->params_size); pev_set_value(pev, ".cbSavedRegs", fpoext->savedregs_size); } -static BOOL pev_free(struct pevaluator* pev, struct pdb_cmd_pair* cpair) +static BOOL pev_free(struct pevaluator* pev) { - DWORD_PTR val; - - if (cpair) for (; cpair->name; cpair++) - { - if (pev_get_val(pev, cpair->name, &val)) - *cpair->pvalue = val; - } pool_destroy(&pev->pool); return TRUE; } -BOOL pdb_fpo_unwind_parse_cmd_string(struct cpu_stack_walk* csw, PDB_FPO_DATA* fpoext, - const char* cmd, struct pdb_cmd_pair* cpair) +BOOL pdb_fpo_unwind_parse_cmd_string(struct cpu_stack_walk* csw, PDB_FPO_DATA* fpoext, + const char* cmd, WOW64_CONTEXT *context) { char token[PEV_MAX_LEN]; char* ptok = token; @@ -5110,7 +5121,9 @@ BOOL pdb_fpo_unwind_parse_cmd_string(struct cpu_stack_walk* csw, PDB_FPO_DATA* struct pevaluator pev; if (!cmd) return FALSE; - pev_init(&pev, csw, fpoext, cpair); + pev_init(&pev, csw); + pev_push_context(&pev, context); + pev_push_fpodata(&pev, fpoext); for (ptr = cmd; !over; ptr++) { if (*ptr == ' ' || (over = *ptr == '\0')) @@ -5146,16 +5159,16 @@ BOOL pdb_fpo_unwind_parse_cmd_string(struct cpu_stack_walk* csw, PDB_FPO_DATA* *ptok++ = *ptr; } } - pev_free(&pev, cpair); + pev_pop_context(&pev, context); + pev_free(&pev); return TRUE; done: FIXME("Couldn't evaluate %s => %s\n", debugstr_a(cmd), pev.error); - pev_free(&pev, NULL); + pev_free(&pev); return FALSE; } -BOOL pdb_virtual_unwind(struct cpu_stack_walk *csw, DWORD_PTR ip, - union ctx *context, struct pdb_cmd_pair *cpair) +BOOL pdb_virtual_unwind(struct cpu_stack_walk *csw, DWORD_PTR ip, union ctx *context) { struct pdb_reader *pdb; struct pdb_reader_walker walker; @@ -5165,9 +5178,10 @@ BOOL pdb_virtual_unwind(struct cpu_stack_walk *csw, DWORD_PTR ip, BOOL ret = FALSE; if (!module_init_pair(&pair, csw->hProcess, ip)) return FALSE; + if (!pair.effective->format_info[DFI_PDB]) return FALSE; if (!pdb_hack_get_main_info(pair.effective->format_info[DFI_PDB], &pdb, NULL)) return FALSE; - if (!pdb) - return pdb_old_virtual_unwind(csw, ip, context, cpair); + if (!pdb) return FALSE; + TRACE("searching %Ix => %Ix\n", ip, ip - (DWORD_PTR)pair.effective->module.BaseOfImage); ip -= (DWORD_PTR)pair.effective->module.BaseOfImage; @@ -5189,7 +5203,7 @@ BOOL pdb_virtual_unwind(struct cpu_stack_walk *csw, DWORD_PTR ip, fpoext.savedregs_size, fpoext.flags, debugstr_a(cmd)); - ret = pdb_fpo_unwind_parse_cmd_string(csw, &fpoext, cmd, cpair); + ret = pdb_fpo_unwind_parse_cmd_string(csw, &fpoext, cmd, &context->x86); pdb_reader_free(pdb, cmd); break; } From 30a6357ea2d18ee1352b6c46ed10fb2c5b712167 Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Fri, 6 Jun 2025 11:42:43 +0200 Subject: [PATCH 291/454] dbghelp: Rename declarations for old PBD backend. Signed-off-by: Eric Pouech (cherry picked from commit 3d9db800bc4265164f00a69cd6f102009fca05c9) --- dlls/dbghelp/dbghelp_private.h | 6 ++--- dlls/dbghelp/module.c | 4 +-- dlls/dbghelp/msc.c | 48 +++++++++++++++++----------------- dlls/dbghelp/pdb.c | 10 +++---- 4 files changed, 34 insertions(+), 34 deletions(-) diff --git a/dlls/dbghelp/dbghelp_private.h b/dlls/dbghelp/dbghelp_private.h index f4486166d442..9957c0b0aaa6 100644 --- a/dlls/dbghelp/dbghelp_private.h +++ b/dlls/dbghelp/dbghelp_private.h @@ -414,7 +414,7 @@ enum format_info DFI_PE, DFI_MACHO, DFI_DWARF, - DFI_PDB, + DFI_OLD_PDB, DFI_LAST }; @@ -477,8 +477,8 @@ struct module_format struct elf_module_info* elf_info; struct dwarf2_module_info_s* dwarf2_info; struct pe_module_info* pe_info; - struct macho_module_info* macho_info; - struct pdb_module_info* pdb_info; + struct macho_module_info* macho_info; + struct old_pdb_module_info* old_pdb_info; } u; }; diff --git a/dlls/dbghelp/module.c b/dlls/dbghelp/module.c index 3e0e567b2287..47e5ec8902f0 100644 --- a/dlls/dbghelp/module.c +++ b/dlls/dbghelp/module.c @@ -407,8 +407,8 @@ BOOL module_load_debug(struct module* module) * we could always use the 3 non-zero lower bits of symref_t to match a * debug backend. */ - if (module->format_info[DFI_PDB] && module->format_info[DFI_PDB]->vtable) - module->ops_symref_modfmt = module->format_info[DFI_PDB]; + if (module->format_info[DFI_OLD_PDB] && module->format_info[DFI_OLD_PDB]->vtable) + module->ops_symref_modfmt = module->format_info[DFI_OLD_PDB]; if (!ret) module->module.SymType = SymNone; assert(module->module.SymType != SymDeferred); diff --git a/dlls/dbghelp/msc.c b/dlls/dbghelp/msc.c index 4fa1134b20ce..296286d5807e 100644 --- a/dlls/dbghelp/msc.c +++ b/dlls/dbghelp/msc.c @@ -80,7 +80,7 @@ struct pdb_file_info /* FIXME: don't make it static */ #define CV_MAX_MODULES 32 -struct pdb_module_info +struct old_pdb_module_info { unsigned used_subfiles; struct pdb_file_info pdb_files[CV_MAX_MODULES]; @@ -98,10 +98,10 @@ struct cv_module_snarf BOOL pdb_hack_get_main_info(struct module_format *modfmt, struct pdb_reader **pdb, unsigned *fpoext_stream) { - struct pdb_module_info* pdb_info; + struct old_pdb_module_info* pdb_info; if (!modfmt) return FALSE; - pdb_info = modfmt->u.pdb_info; + pdb_info = modfmt->u.old_pdb_info; *pdb = pdb_info->pdb_files[0].pdb_reader; if (fpoext_stream) *fpoext_stream = pdb_info->pdb_files[0].fpoext_stream; @@ -668,7 +668,7 @@ static symref_t codeview_fetch_symref(struct codeview_type_parse* ctp, unsigned { symref_t symref; - if (!cv_hack_ptr_to_symref(ctp->module->format_info[DFI_PDB]->u.pdb_info->pdb_files[0].pdb_reader, typeno, &symref)) + if (!cv_hack_ptr_to_symref(ctp->module->format_info[DFI_OLD_PDB]->u.old_pdb_info->pdb_files[0].pdb_reader, typeno, &symref)) { symref = symt_ptr_to_symref(codeview_fetch_type(ctp, typeno)); } @@ -680,7 +680,7 @@ static symref_t codeview_get_symref(struct module *module, unsigned typeno) { symref_t symref; - if (!cv_hack_ptr_to_symref(module->format_info[DFI_PDB]->u.pdb_info->pdb_files[0].pdb_reader, typeno, &symref)) + if (!cv_hack_ptr_to_symref(module->format_info[DFI_OLD_PDB]->u.old_pdb_info->pdb_files[0].pdb_reader, typeno, &symref)) { symref = symt_ptr_to_symref(codeview_get_type(typeno, TRUE)); } @@ -1548,7 +1548,7 @@ static BOOL codeview_parse_type_table(struct codeview_type_parse* ctp) unsigned int i, curr_type; const union codeview_type* type; - cv_current_module->pdb = ctp->module->format_info[DFI_PDB]->u.pdb_info->pdb_files[0].pdb_reader; + cv_current_module->pdb = ctp->module->format_info[DFI_OLD_PDB]->u.old_pdb_info->pdb_files[0].pdb_reader; cv_current_module->first_type_index = ctp->header.first_index; cv_current_module->last_type_index = ctp->header.last_index; cv_current_module->defined_types = calloc(ctp->header.last_index - ctp->header.first_index, @@ -3306,13 +3306,13 @@ static void pdb_module_remove(struct module_format* modfmt) { unsigned i; - for (i = 0; i < modfmt->u.pdb_info->used_subfiles; i++) + for (i = 0; i < modfmt->u.old_pdb_info->used_subfiles; i++) { - pdb_free_file(&modfmt->u.pdb_info->pdb_files[i], TRUE); - if (modfmt->u.pdb_info->pdb_files[i].image) - UnmapViewOfFile(modfmt->u.pdb_info->pdb_files[i].image); - if (modfmt->u.pdb_info->pdb_files[i].pdb_reader) - pdb_reader_dispose(modfmt->u.pdb_info->pdb_files[i].pdb_reader); + pdb_free_file(&modfmt->u.old_pdb_info->pdb_files[i], TRUE); + if (modfmt->u.old_pdb_info->pdb_files[i].image) + UnmapViewOfFile(modfmt->u.old_pdb_info->pdb_files[i].image); + if (modfmt->u.old_pdb_info->pdb_files[i].pdb_reader) + pdb_reader_dispose(modfmt->u.old_pdb_info->pdb_files[i].pdb_reader); } HeapFree(GetProcessHeap(), 0, modfmt); } @@ -3649,7 +3649,7 @@ static BOOL pdb_init(struct pdb_file_info* pdb_file, const char* image) static BOOL pdb_process_internal(const struct process *pcs, const struct msc_debug_info *msc_dbg, const WCHAR *filename, - struct pdb_module_info *pdb_module_info, + struct old_pdb_module_info *pdb_module_info, unsigned module_index, BOOL *has_linenumber_info); @@ -3767,7 +3767,7 @@ static void pdb_process_symbol_imports(const struct process *pcs, const PDB_SYMBOLS *symbols, const void *symbols_image, const char *image, - struct pdb_module_info *pdb_module_info, + struct old_pdb_module_info *pdb_module_info, unsigned module_index) { if (module_index == -1 && symbols && symbols->pdbimport_size) @@ -3813,7 +3813,7 @@ static void pdb_process_symbol_imports(const struct process *pcs, static BOOL pdb_process_internal(const struct process *pcs, const struct msc_debug_info *msc_dbg, const WCHAR *filename, - struct pdb_module_info *pdb_module_info, + struct old_pdb_module_info *pdb_module_info, unsigned module_index, BOOL *has_linenumber_info) { @@ -4030,27 +4030,27 @@ static BOOL pdb_process_file(const struct process *pcs, const char *filename, const GUID *guid, DWORD timestamp, DWORD age) { struct module_format* modfmt; - struct pdb_module_info* pdb_module_info; + struct old_pdb_module_info* pdb_module_info; SYMSRV_INDEX_INFOW info; BOOL unmatched; if (!msc_dbg->module->dont_load_symbols && path_find_symbol_file(pcs, msc_dbg->module, filename, TRUE, guid, timestamp, age, &info, &unmatched) && (modfmt = HeapAlloc(GetProcessHeap(), 0, - sizeof(struct module_format) + sizeof(struct pdb_module_info)))) + sizeof(struct module_format) + sizeof(struct old_pdb_module_info)))) { BOOL ret, has_linenumber_info; pdb_module_info = (void*)(modfmt + 1); - msc_dbg->module->format_info[DFI_PDB] = modfmt; + msc_dbg->module->format_info[DFI_OLD_PDB] = modfmt; modfmt->module = msc_dbg->module; modfmt->vtable = &old_pdb_module_format_vtable; - modfmt->u.pdb_info = pdb_module_info; + modfmt->u.old_pdb_info = pdb_module_info; memset(cv_zmodules, 0, sizeof(cv_zmodules)); codeview_init_basic_types(msc_dbg->module); ret = pdb_process_internal(pcs, msc_dbg, info.pdbfile, - msc_dbg->module->format_info[DFI_PDB]->u.pdb_info, -1, &has_linenumber_info); + msc_dbg->module->format_info[DFI_OLD_PDB]->u.old_pdb_info, -1, &has_linenumber_info); codeview_clear_type_table(); if (ret) { @@ -4070,7 +4070,7 @@ static BOOL pdb_process_file(const struct process *pcs, return TRUE; } - msc_dbg->module->format_info[DFI_PDB] = NULL; + msc_dbg->module->format_info[DFI_OLD_PDB] = NULL; HeapFree(GetProcessHeap(), 0, modfmt); } msc_dbg->module->module.SymType = SymNone; @@ -4412,15 +4412,15 @@ DWORD dbg_get_file_indexinfo(void* image, DWORD size, SYMSRV_INDEX_INFOW* info) BOOL old_pdb_virtual_unwind(struct cpu_stack_walk *csw, DWORD_PTR ip, union ctx *context) { struct module_pair pair; - struct pdb_module_info* pdb_info; + struct old_pdb_module_info* pdb_info; PDB_FPO_DATA* fpoext; unsigned i, size; PDB_STRING_TABLE* strbase; BOOL ret = TRUE; if (!module_init_pair(&pair, csw->hProcess, ip)) return FALSE; - if (!pair.effective->format_info[DFI_PDB]) return FALSE; - pdb_info = pair.effective->format_info[DFI_PDB]->u.pdb_info; + if (!pair.effective->format_info[DFI_OLD_PDB]) return FALSE; + pdb_info = pair.effective->format_info[DFI_OLD_PDB]->u.old_pdb_info; TRACE("searching %Ix => %Ix\n", ip, ip - (DWORD_PTR)pair.effective->module.BaseOfImage); ip -= (DWORD_PTR)pair.effective->module.BaseOfImage; diff --git a/dlls/dbghelp/pdb.c b/dlls/dbghelp/pdb.c index a8420d1c6986..d046effb9bb5 100644 --- a/dlls/dbghelp/pdb.c +++ b/dlls/dbghelp/pdb.c @@ -510,7 +510,7 @@ static enum pdb_result pdb_reader_init(struct pdb_reader *pdb, struct module *mo pdb->streams[i].name = NULL; } /* hack (must be set before loading debug info so it can be used therein) */ - pdb->module->ops_symref_modfmt = module->format_info[DFI_PDB]; + pdb->module->ops_symref_modfmt = module->format_info[DFI_OLD_PDB]; pdb_reader_init_DBI(pdb); return R_PDB_SUCCESS; @@ -4840,9 +4840,9 @@ struct pdb_reader *pdb_hack_reader_init(struct module *module, HANDLE file, cons if (pdb_reader_init(pdb, module, file, new_sections, num_sections) == R_PDB_SUCCESS) { /* hack (copy old pdb methods until they are moved here) */ - pdb_module_format_vtable.remove = module->format_info[DFI_PDB]->vtable->remove; + pdb_module_format_vtable.remove = module->format_info[DFI_OLD_PDB]->vtable->remove; - module->format_info[DFI_PDB]->vtable = &pdb_module_format_vtable; + module->format_info[DFI_OLD_PDB]->vtable = &pdb_module_format_vtable; return pdb; } } @@ -5178,8 +5178,8 @@ BOOL pdb_virtual_unwind(struct cpu_stack_walk *csw, DWORD_PTR ip, union ctx *con BOOL ret = FALSE; if (!module_init_pair(&pair, csw->hProcess, ip)) return FALSE; - if (!pair.effective->format_info[DFI_PDB]) return FALSE; - if (!pdb_hack_get_main_info(pair.effective->format_info[DFI_PDB], &pdb, NULL)) return FALSE; + if (!pair.effective->format_info[DFI_OLD_PDB]) return FALSE; + if (!pdb_hack_get_main_info(pair.effective->format_info[DFI_OLD_PDB], &pdb, NULL)) return FALSE; if (!pdb) return FALSE; TRACE("searching %Ix => %Ix\n", ip, ip - (DWORD_PTR)pair.effective->module.BaseOfImage); From e549a0915d6e4befc2be360ef41d118e4533e81a Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Fri, 6 Jun 2025 09:45:16 +0200 Subject: [PATCH 292/454] dbghelp: Let new PDB reader exist independently of the old one. Signed-off-by: Eric Pouech (cherry picked from commit a24e54c7c9ed406b9a70a5159577a5182bf70cfe) --- dlls/dbghelp/dbghelp_private.h | 18 ++-- dlls/dbghelp/module.c | 8 -- dlls/dbghelp/msc.c | 149 ++++++++++++--------------------- dlls/dbghelp/pdb.c | 101 ++++++++++++++-------- 4 files changed, 130 insertions(+), 146 deletions(-) diff --git a/dlls/dbghelp/dbghelp_private.h b/dlls/dbghelp/dbghelp_private.h index 9957c0b0aaa6..d16468dc07ad 100644 --- a/dlls/dbghelp/dbghelp_private.h +++ b/dlls/dbghelp/dbghelp_private.h @@ -414,6 +414,7 @@ enum format_info DFI_PE, DFI_MACHO, DFI_DWARF, + DFI_PDB, DFI_OLD_PDB, DFI_LAST }; @@ -479,6 +480,7 @@ struct module_format struct pe_module_info* pe_info; struct macho_module_info* macho_info; struct old_pdb_module_info* old_pdb_info; + struct pdb_module_info* pdb_info; } u; }; @@ -503,7 +505,12 @@ struct module /* specific information for debug types */ struct module_format* format_info[DFI_LAST]; unsigned debug_format_bitmask; - struct module_format *ops_symref_modfmt; /* HACK for fast access to the ops table */ + /* Hack for fast symdef deref... + * Note: if ever we need another backend with dedicated symref_t support, + * we could always use the 3 non-zero lower bits of symref_t to match a + * debug backend. + */ + struct module_format *ops_symref_modfmt; /* memory allocation pool */ struct pool pool; @@ -843,6 +850,8 @@ extern const WCHAR* file_name(const WCHAR* str); extern const char* file_nameA(const char* str); /* pdb.c */ +extern BOOL pdb_init_modfmt(const struct process *pcs, const struct msc_debug_info *msc_dbg, + const WCHAR *filename, BOOL *has_linenumber_info); extern BOOL pdb_virtual_unwind(struct cpu_stack_walk *csw, DWORD_PTR ip, union ctx *context); struct _PDB_FPO_DATA; extern BOOL pdb_fpo_unwind_parse_cmd_string(struct cpu_stack_walk* csw, struct _PDB_FPO_DATA* fpoext, @@ -1086,10 +1095,3 @@ extern struct symt_function* #define IFC_DEPTH_MASK 0x3FFFFFFF #define IFC_MODE(x) ((x) & ~IFC_DEPTH_MASK) #define IFC_DEPTH(x) ((x) & IFC_DEPTH_MASK) - -/* temporary helpers for PDB rewriting */ -struct pdb_reader; -extern BOOL pdb_hack_get_main_info(struct module_format *modfmt, struct pdb_reader **pdb, unsigned *fpoext_stream); -extern void pdb_reader_dispose(struct pdb_reader *pdb); -extern struct pdb_reader *pdb_hack_reader_init(struct module *module, HANDLE file, const IMAGE_SECTION_HEADER *sections, unsigned num_sections); -extern BOOL cv_hack_ptr_to_symref(struct pdb_reader *pdb, unsigned typeno, symref_t *symref); diff --git a/dlls/dbghelp/module.c b/dlls/dbghelp/module.c index 47e5ec8902f0..02bd9b3a0a0d 100644 --- a/dlls/dbghelp/module.c +++ b/dlls/dbghelp/module.c @@ -402,14 +402,6 @@ BOOL module_load_debug(struct module* module) } else ret = module->process->loader->load_debug_info(module->process, module); - /* Hack for fast symdef deref... - * Note: if ever we need another backend with dedicated symref_t support, - * we could always use the 3 non-zero lower bits of symref_t to match a - * debug backend. - */ - if (module->format_info[DFI_OLD_PDB] && module->format_info[DFI_OLD_PDB]->vtable) - module->ops_symref_modfmt = module->format_info[DFI_OLD_PDB]; - if (!ret) module->module.SymType = SymNone; assert(module->module.SymType != SymDeferred); module->module.NumSyms = module->ht_symbols.num_elts; diff --git a/dlls/dbghelp/msc.c b/dlls/dbghelp/msc.c index 296286d5807e..43ee8509a0dd 100644 --- a/dlls/dbghelp/msc.c +++ b/dlls/dbghelp/msc.c @@ -61,7 +61,6 @@ enum pdb_kind {PDB_JG, PDB_DS}; struct pdb_file_info { enum pdb_kind kind; - struct pdb_reader *pdb_reader; /* new pdb reader */ const char* image; struct pdb_stream_name* stream_dict; unsigned fpoext_stream; @@ -96,18 +95,6 @@ struct cv_module_snarf const PDB_STRING_TABLE* strimage; }; -BOOL pdb_hack_get_main_info(struct module_format *modfmt, struct pdb_reader **pdb, unsigned *fpoext_stream) -{ - struct old_pdb_module_info* pdb_info; - - if (!modfmt) return FALSE; - pdb_info = modfmt->u.old_pdb_info; - *pdb = pdb_info->pdb_files[0].pdb_reader; - if (fpoext_stream) - *fpoext_stream = pdb_info->pdb_files[0].fpoext_stream; - return TRUE; -} - /*======================================================================== * Debug file access helper routines */ @@ -149,7 +136,6 @@ struct cv_defined_module unsigned int first_type_index; unsigned int last_type_index; struct symt** defined_types; /* when old reader */ - struct pdb_reader *pdb; /* new reader: hack */ }; /* FIXME: don't make it static */ #define CV_MAX_MODULES 32 @@ -666,25 +652,12 @@ static struct symt* codeview_fetch_type(struct codeview_type_parse* ctp, static symref_t codeview_fetch_symref(struct codeview_type_parse* ctp, unsigned typeno) { - symref_t symref; - - if (!cv_hack_ptr_to_symref(ctp->module->format_info[DFI_OLD_PDB]->u.old_pdb_info->pdb_files[0].pdb_reader, typeno, &symref)) - { - symref = symt_ptr_to_symref(codeview_fetch_type(ctp, typeno)); - } - - return symref; + return symt_ptr_to_symref(codeview_fetch_type(ctp, typeno)); } static symref_t codeview_get_symref(struct module *module, unsigned typeno) { - symref_t symref; - - if (!cv_hack_ptr_to_symref(module->format_info[DFI_OLD_PDB]->u.old_pdb_info->pdb_files[0].pdb_reader, typeno, &symref)) - { - symref = symt_ptr_to_symref(codeview_get_type(typeno, TRUE)); - } - return symref; + return symt_ptr_to_symref(codeview_get_type(typeno, TRUE)); } static UINT32 codeview_compute_hash(const char* ptr, unsigned len) @@ -1548,7 +1521,6 @@ static BOOL codeview_parse_type_table(struct codeview_type_parse* ctp) unsigned int i, curr_type; const union codeview_type* type; - cv_current_module->pdb = ctp->module->format_info[DFI_OLD_PDB]->u.old_pdb_info->pdb_files[0].pdb_reader; cv_current_module->first_type_index = ctp->header.first_index; cv_current_module->last_type_index = ctp->header.last_index; cv_current_module->defined_types = calloc(ctp->header.last_index - ctp->header.first_index, @@ -2329,7 +2301,6 @@ static struct symt_compiland* codeview_new_compiland(const struct msc_debug_info } static BOOL codeview_snarf(const struct msc_debug_info* msc_dbg, - unsigned stream_id, const BYTE* root, unsigned offset, unsigned size, const struct cv_module_snarf* cvmod, const char* objname) @@ -2720,15 +2691,7 @@ static BOOL codeview_snarf(const struct msc_debug_info* msc_dbg, */ if (!sym->local_v3.varflags.enreg_global && !sym->local_v3.varflags.enreg_static) { - if (stream_id) - { - loc.kind = loc_user + 1 /* loc_cv_defrange for new PDB reader (see pdb.c) */; - loc.reg = stream_id; - loc.offset = (const BYTE*)sym - root; - length += codeview_defrange_length(sym); - } - else - length += codeview_transform_defrange(msc_dbg, curr_func, sym, &loc); + length += codeview_transform_defrange(msc_dbg, curr_func, sym, &loc); symt_add_func_local(msc_dbg->module, curr_func, sym->local_v3.varflags.is_param ? DataIsParam : DataIsLocal, &loc, block, @@ -3209,7 +3172,7 @@ static void pdb_free(void* buffer) HeapFree(GetProcessHeap(), 0, buffer); } -static void pdb_free_file(struct pdb_file_info* pdb_file, BOOL unmap) +static void pdb_free_file(struct pdb_file_info* pdb_file) { switch (pdb_file->kind) { @@ -3224,11 +3187,8 @@ static void pdb_free_file(struct pdb_file_info* pdb_file, BOOL unmap) } HeapFree(GetProcessHeap(), 0, pdb_file->stream_dict); pdb_file->stream_dict = NULL; - if (unmap) - { - UnmapViewOfFile(pdb_file->image); - pdb_file->image = NULL; - } + UnmapViewOfFile(pdb_file->image); + pdb_file->image = NULL; } static struct pdb_stream_name* pdb_load_stream_name_table(const char* str, unsigned cb) @@ -3308,11 +3268,9 @@ static void pdb_module_remove(struct module_format* modfmt) for (i = 0; i < modfmt->u.old_pdb_info->used_subfiles; i++) { - pdb_free_file(&modfmt->u.old_pdb_info->pdb_files[i], TRUE); + pdb_free_file(&modfmt->u.old_pdb_info->pdb_files[i]); if (modfmt->u.old_pdb_info->pdb_files[i].image) UnmapViewOfFile(modfmt->u.old_pdb_info->pdb_files[i].image); - if (modfmt->u.old_pdb_info->pdb_files[i].pdb_reader) - pdb_reader_dispose(modfmt->u.old_pdb_info->pdb_files[i].pdb_reader); } HeapFree(GetProcessHeap(), 0, modfmt); } @@ -3839,22 +3797,11 @@ static BOOL pdb_process_internal(const struct process *pcs, return FALSE; } + CloseHandle(hFile); CloseHandle(hMap); /* old pdb reader */ if (!pdb_init(pdb_file, image)) { - CloseHandle(hFile); - UnmapViewOfFile(image); - return FALSE; - } - if (getenv("WINE_DBGHELP_OLD_PDB")) /* keep using old pdb reader */ - { - pdb_file->pdb_reader = NULL; - CloseHandle(hFile); - } - else if (!(pdb_file->pdb_reader = pdb_hack_reader_init(msc_dbg->module, hFile, msc_dbg->sectp, msc_dbg->nsect))) - { - CloseHandle(hFile); UnmapViewOfFile(image); return FALSE; } @@ -3899,10 +3846,7 @@ static BOOL pdb_process_internal(const struct process *pcs, pdb_process_symbol_imports(pcs, msc_dbg, &symbols, symbols_image, image, pdb_module_info, module_index); - if (!pdb_file->pdb_reader) - { - pdb_process_types(msc_dbg, pdb_file); - } + pdb_process_types(msc_dbg, pdb_file); ipi_image = pdb_read_stream(pdb_file, 4); ipi_ok = pdb_init_type_parse(msc_dbg, pdb_file, &ipi_ctp, ipi_image); @@ -3911,7 +3855,7 @@ static BOOL pdb_process_internal(const struct process *pcs, * streams' loading can succeed them. */ globalimage = pdb_read_stream(pdb_file, symbols.gsym_stream); - if (globalimage && !pdb_file->pdb_reader) + if (globalimage) { const BYTE* data; unsigned global_size = pdb_get_stream_size(pdb_file, symbols.gsym_stream); @@ -3929,7 +3873,6 @@ static BOOL pdb_process_internal(const struct process *pcs, /* Read per-module symbols' tables */ file = symbols_image + header_size; - if (pdb_file->pdb_reader) file += symbols.module_size; /* skip it */ while (file - symbols_image < header_size + symbols.module_size) { PDB_SYMBOL_FILE_EX sfile; @@ -3945,22 +3888,19 @@ static BOOL pdb_process_internal(const struct process *pcs, { struct cv_module_snarf cvmod = {ipi_ok ? &ipi_ctp : NULL, (const void*)(modimage + sfile.symbol_size), sfile.lineno2_size, files_image}; - codeview_snarf(msc_dbg, pdb_file->pdb_reader ? sfile.stream : 0, modimage, sizeof(DWORD), sfile.symbol_size, + codeview_snarf(msc_dbg, modimage, sizeof(DWORD), sfile.symbol_size, &cvmod, file_name); if (sfile.lineno_size && sfile.lineno2_size) FIXME("Both line info present... preferring second\n"); if (sfile.lineno2_size) { - if (pdb_file->pdb_reader || - ((SymGetOptions() & SYMOPT_LOAD_LINES) && codeview_snarf_linetab2(msc_dbg, &cvmod))) + if (((SymGetOptions() & SYMOPT_LOAD_LINES) && codeview_snarf_linetab2(msc_dbg, &cvmod))) *has_linenumber_info = TRUE; } else if (sfile.lineno_size) { - if (pdb_file->pdb_reader) - FIXME("New PDB reader doesn't support old line format\n"); - else if ((SymGetOptions() & SYMOPT_LOAD_LINES) && + if ((SymGetOptions() & SYMOPT_LOAD_LINES) && codeview_snarf_linetab(msc_dbg, modimage + sfile.symbol_size, sfile.lineno_size, @@ -3974,7 +3914,7 @@ static BOOL pdb_process_internal(const struct process *pcs, file = (BYTE*)((DWORD_PTR)(file_name + strlen(file_name) + 1 + 3) & ~3); } /* Load the global variables and constants (if not yet loaded) and public information */ - if (globalimage && !pdb_file->pdb_reader) + if (globalimage) { const BYTE* data; unsigned global_size = pdb_get_stream_size(pdb_file, symbols.gsym_stream); @@ -4008,7 +3948,7 @@ static BOOL pdb_process_internal(const struct process *pcs, pdb_free(symbols_image); pdb_free(files_image); - pdb_free_file(pdb_file, pdb_file->pdb_reader != NULL); + pdb_free_file(pdb_file); return TRUE; } @@ -4025,33 +3965,52 @@ static const struct module_format_vtable old_pdb_module_format_vtable = pdb_location_compute, }; +static BOOL old_pdb_process_file(const struct process *pcs, + const struct msc_debug_info *msc_dbg, + const WCHAR *filename, BOOL *has_linenumber_info) +{ + struct module_format* modfmt; + struct old_pdb_module_info* pdb_module_info; + BOOL ret; + + if (!(modfmt = HeapAlloc(GetProcessHeap(), 0, + sizeof(struct module_format) + sizeof(struct old_pdb_module_info)))) + return FALSE; + + pdb_module_info = (void*)(modfmt + 1); + msc_dbg->module->format_info[DFI_OLD_PDB] = modfmt; + modfmt->module = msc_dbg->module; + modfmt->vtable = &old_pdb_module_format_vtable; + modfmt->u.old_pdb_info = pdb_module_info; + + memset(cv_zmodules, 0, sizeof(cv_zmodules)); + codeview_init_basic_types(msc_dbg->module); + ret = pdb_process_internal(pcs, msc_dbg, filename, + msc_dbg->module->format_info[DFI_OLD_PDB]->u.old_pdb_info, -1, has_linenumber_info); + codeview_clear_type_table(); + if (!ret) + { + msc_dbg->module->format_info[DFI_OLD_PDB] = NULL; + HeapFree(GetProcessHeap(), 0, modfmt); + } + return ret; +} + static BOOL pdb_process_file(const struct process *pcs, const struct msc_debug_info *msc_dbg, const char *filename, const GUID *guid, DWORD timestamp, DWORD age) { - struct module_format* modfmt; - struct old_pdb_module_info* pdb_module_info; SYMSRV_INDEX_INFOW info; - BOOL unmatched; + BOOL unmatched, has_linenumber_info, ret; if (!msc_dbg->module->dont_load_symbols && - path_find_symbol_file(pcs, msc_dbg->module, filename, TRUE, guid, timestamp, age, &info, &unmatched) && - (modfmt = HeapAlloc(GetProcessHeap(), 0, - sizeof(struct module_format) + sizeof(struct old_pdb_module_info)))) + path_find_symbol_file(pcs, msc_dbg->module, filename, TRUE, guid, timestamp, age, &info, &unmatched)) { - BOOL ret, has_linenumber_info; - - pdb_module_info = (void*)(modfmt + 1); - msc_dbg->module->format_info[DFI_OLD_PDB] = modfmt; - modfmt->module = msc_dbg->module; - modfmt->vtable = &old_pdb_module_format_vtable; - modfmt->u.old_pdb_info = pdb_module_info; + if (getenv("WINE_DBGHELP_OLD_PDB")) /* keep using old pdb reader */ + ret = old_pdb_process_file(pcs, msc_dbg, info.pdbfile, &has_linenumber_info); + else + ret = pdb_init_modfmt(pcs, msc_dbg, info.pdbfile, &has_linenumber_info); - memset(cv_zmodules, 0, sizeof(cv_zmodules)); - codeview_init_basic_types(msc_dbg->module); - ret = pdb_process_internal(pcs, msc_dbg, info.pdbfile, - msc_dbg->module->format_info[DFI_OLD_PDB]->u.old_pdb_info, -1, &has_linenumber_info); - codeview_clear_type_table(); if (ret) { msc_dbg->module->module.SymType = SymPdb; @@ -4070,8 +4029,6 @@ static BOOL pdb_process_file(const struct process *pcs, return TRUE; } - msc_dbg->module->format_info[DFI_OLD_PDB] = NULL; - HeapFree(GetProcessHeap(), 0, modfmt); } msc_dbg->module->module.SymType = SymNone; if (guid) @@ -4151,7 +4108,7 @@ static BOOL codeview_process_info(const struct process *pcs, if (ent->SubSection == sstAlignSym) { - codeview_snarf(msc_dbg, 0, msc_dbg->root + ent->lfo, sizeof(DWORD), ent->cb, NULL, NULL); + codeview_snarf(msc_dbg, msc_dbg->root + ent->lfo, sizeof(DWORD), ent->cb, NULL, NULL); if (SymGetOptions() & SYMOPT_LOAD_LINES) { diff --git a/dlls/dbghelp/pdb.c b/dlls/dbghelp/pdb.c index d046effb9bb5..c4f4319de43b 100644 --- a/dlls/dbghelp/pdb.c +++ b/dlls/dbghelp/pdb.c @@ -510,7 +510,7 @@ static enum pdb_result pdb_reader_init(struct pdb_reader *pdb, struct module *mo pdb->streams[i].name = NULL; } /* hack (must be set before loading debug info so it can be used therein) */ - pdb->module->ops_symref_modfmt = module->format_info[DFI_OLD_PDB]; + pdb->module->ops_symref_modfmt = module->format_info[DFI_PDB]; pdb_reader_init_DBI(pdb); return R_PDB_SUCCESS; @@ -522,7 +522,7 @@ static enum pdb_result pdb_reader_init(struct pdb_reader *pdb, struct module *mo return result; } -void pdb_reader_dispose(struct pdb_reader *pdb) +static void pdb_reader_dispose(struct pdb_reader *pdb) { CloseHandle(pdb->file); /* note: pdb is allocated inside its pool, so this must be last line */ @@ -1051,6 +1051,16 @@ static enum pdb_result pdb_reader_get_line_from_address_internal(struct pdb_read return R_PDB_NOT_FOUND; } +struct pdb_module_info +{ + struct pdb_reader pdb_reader; +}; + +static inline struct pdb_reader *pdb_get_current_reader(const struct module_format *modfmt) +{ + return &modfmt->u.pdb_info->pdb_reader; +} + static enum method_result pdb_method_result(enum pdb_result result) { switch (result) @@ -1068,7 +1078,7 @@ static enum method_result pdb_method_get_line_from_address(struct module_format struct pdb_reader *pdb; pdbsize_t compiland_offset; - if (!pdb_hack_get_main_info(modfmt, &pdb, NULL)) return MR_FAILURE; + pdb = pdb_get_current_reader(modfmt); result = pdb_reader_get_line_from_address_internal(pdb, address, line_info, &compiland_offset); return pdb_method_result(result); } @@ -1142,7 +1152,7 @@ static enum method_result pdb_method_advance_line_info(struct module_format *mod { struct pdb_reader *pdb; - if (!pdb_hack_get_main_info(modfmt, &pdb, NULL)) return MR_FAILURE; + pdb = pdb_get_current_reader(modfmt); return pdb_reader_advance_line_info(pdb, line_info, forward) == R_PDB_SUCCESS ? MR_SUCCESS : MR_FAILURE; } @@ -1261,8 +1271,7 @@ static enum method_result pdb_method_enumerate_lines(struct module_format *modfm { struct pdb_reader *pdb; - if (!pdb_hack_get_main_info(modfmt, &pdb, NULL)) return MR_FAILURE; - + pdb = pdb_get_current_reader(modfmt); return pdb_method_result(pdb_method_enumerate_lines_internal(pdb, compiland_regex, source_file_regex, cb, user)); } @@ -1308,7 +1317,7 @@ static enum method_result pdb_method_enumerate_sources(struct module_format *mod { struct pdb_reader *pdb; - if (!pdb_hack_get_main_info(modfmt, &pdb, NULL)) return MR_FAILURE; + pdb = pdb_get_current_reader(modfmt); /* Note: in PDB, each compiland lists its used files, which are all in global string table, * but there's no global source files table AFAICT. @@ -1975,7 +1984,7 @@ static void pdb_method_location_compute(const struct module_format* modfmt, loc->kind = loc_error; loc->reg = loc_err_internal; - if (!pdb_hack_get_main_info((struct module_format *)modfmt, &pdb, NULL)) return; + pdb = pdb_get_current_reader(modfmt); if (in_loc.kind != loc_cv_defrange || pdb_reader_walker_init(pdb, in_loc.reg, &walker)) return; walker.offset = in_loc.offset; @@ -2582,7 +2591,7 @@ static enum method_result pdb_method_find_type(struct module_format *modfmt, con struct pdb_type_details *type_details; pdbsize_t stream_offset; - if (!pdb_hack_get_main_info(modfmt, &pdb, NULL)) return MR_FAILURE; + pdb = pdb_get_current_reader(modfmt); if ((result = pdb_reader_init_TPI(pdb))) return pdb_method_result(result); /* search in TPI hash table */ if ((result = pdb_reader_read_codeview_type_by_name(pdb, name, &walker, &cv_type, &cv_typeid)) == R_PDB_SUCCESS) @@ -2706,7 +2715,7 @@ static enum method_result pdb_method_enumerate_types(struct module_format *modfm VARIANT v; BOOL ret; - if (!pdb_hack_get_main_info(modfmt, &pdb, NULL)) return MR_FAILURE; + pdb = pdb_get_current_reader(modfmt); if ((result = pdb_reader_init_TPI(pdb))) return pdb_method_result(result); walker = pdb->tpi_types_walker; /* Note: walking the types through the hash table may not be the most efficient */ @@ -3598,7 +3607,7 @@ static enum method_result pdb_method_request_symref_t(struct module_format *modf { struct pdb_reader *pdb; - if (!pdb_hack_get_main_info(modfmt, &pdb, NULL)) return MR_FAILURE; + pdb = pdb_get_current_reader(modfmt); if (pdb_reader_init_TPI(pdb)) return MR_FAILURE; return pdb_reader_request_symref_t(pdb, symref, req, data); } @@ -4047,8 +4056,7 @@ static enum method_result pdb_method_get_line_from_inlined_address(struct module { struct pdb_reader *pdb; - if (!pdb_hack_get_main_info(modfmt, &pdb, NULL)) return MR_FAILURE; - + pdb = pdb_get_current_reader(modfmt); return pdb_method_result(pdb_method_get_line_from_inlined_address_internal(pdb, inlined, address, line_info)); } @@ -4662,7 +4670,7 @@ static enum method_result pdb_method_lookup_symbol_by_address(struct module_form struct pdb_reader *pdb; unsigned segment, offset; - if (!pdb_hack_get_main_info(modfmt, &pdb, NULL)) return MR_FAILURE; + pdb = pdb_get_current_reader(modfmt); if ((result = pdb_reader_get_segment_offset_from_address(pdb, address, &segment, &offset))) return MR_FAILURE; return pdb_method_result(pdb_reader_lookup_top_symbol_by_segment_offset(pdb, segment, offset, symref)); } @@ -4717,7 +4725,7 @@ static enum method_result pdb_method_lookup_symbol_by_name(struct module_format unsigned segment; unsigned offset; - if (!pdb_hack_get_main_info(modfmt, &pdb, NULL)) return MR_FAILURE; + pdb = pdb_get_current_reader(modfmt); if ((result = pdb_reader_read_DBI_codeview_symbol_by_name(pdb, name, &globals_offset, &cv_symbol))) return pdb_method_result(result); @@ -4756,7 +4764,7 @@ static enum method_result pdb_method_enumerate_symbols(struct module_format *mod unsigned offset; char *symbol_name; - if (!pdb_hack_get_main_info(modfmt, &pdb, NULL)) return MR_FAILURE; + pdb = pdb_get_current_reader(modfmt); /* FIXME could be optimized if match doesn't contain wild cards */ /* this is currently ugly, but basically we just ensure that all the compilands which contain matching symbols @@ -4813,9 +4821,15 @@ static enum method_result pdb_method_enumerate_symbols(struct module_format *mod return MR_FAILURE; } +static void pdb_module_remove(struct module_format* modfmt) +{ + pdb_reader_dispose(&modfmt->u.pdb_info->pdb_reader); + HeapFree(GetProcessHeap(), 0, modfmt); +} + static struct module_format_vtable pdb_module_format_vtable = { - NULL,/*pdb_module_remove*/ + pdb_module_remove, pdb_method_request_symref_t, pdb_method_lookup_symbol_by_address, pdb_method_lookup_symbol_by_name, @@ -4830,24 +4844,44 @@ static struct module_format_vtable pdb_module_format_vtable = pdb_method_enumerate_sources, }; -struct pdb_reader *pdb_hack_reader_init(struct module *module, HANDLE file, const IMAGE_SECTION_HEADER *sections, unsigned num_sections) +BOOL pdb_init_modfmt(const struct process *pcs, + const struct msc_debug_info *msc_dbg, + const WCHAR *filename, BOOL *has_linenumber_info) { - struct pdb_reader *pdb = pool_alloc(&module->pool, sizeof(*pdb) + num_sections * sizeof(*sections)); - if (pdb) + struct module_format *modfmt; + struct pdb_module_info *pdb_module_info; + IMAGE_SECTION_HEADER *new_sections; + HANDLE file; + + if (!(modfmt = HeapAlloc(GetProcessHeap(), 0, + sizeof(struct module_format) + sizeof(struct pdb_module_info) + msc_dbg->nsect * sizeof(msc_dbg->sectp[0])))) + return FALSE; + + if ((file = CreateFileW(filename, GENERIC_READ, FILE_SHARE_READ, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE) { - IMAGE_SECTION_HEADER *new_sections = (void*)(pdb + 1); - memcpy(new_sections, sections, num_sections * sizeof(*sections)); - if (pdb_reader_init(pdb, module, file, new_sections, num_sections) == R_PDB_SUCCESS) - { - /* hack (copy old pdb methods until they are moved here) */ - pdb_module_format_vtable.remove = module->format_info[DFI_OLD_PDB]->vtable->remove; + HeapFree(GetProcessHeap(), 0, modfmt); + return FALSE; + } - module->format_info[DFI_OLD_PDB]->vtable = &pdb_module_format_vtable; - return pdb; - } + pdb_module_info = (void*)(modfmt + 1); + msc_dbg->module->format_info[DFI_PDB] = modfmt; + modfmt->module = msc_dbg->module; + modfmt->vtable = &pdb_module_format_vtable; + modfmt->u.pdb_info = pdb_module_info; + + new_sections = (void*)(pdb_module_info + 1); + memcpy(new_sections, msc_dbg->sectp, msc_dbg->nsect * sizeof(*new_sections)); + if (pdb_reader_init(&pdb_module_info->pdb_reader, msc_dbg->module, file, new_sections, msc_dbg->nsect) == R_PDB_SUCCESS) + { + /* FIXME */ + *has_linenumber_info = TRUE; + return TRUE; } - pool_free(&module->pool, pdb); - return NULL; + msc_dbg->module->format_info[DFI_PDB] = NULL; + HeapFree(GetProcessHeap(), 0, modfmt); + CloseHandle(file); + return FALSE; } /*======================================================================== @@ -5178,9 +5212,8 @@ BOOL pdb_virtual_unwind(struct cpu_stack_walk *csw, DWORD_PTR ip, union ctx *con BOOL ret = FALSE; if (!module_init_pair(&pair, csw->hProcess, ip)) return FALSE; - if (!pair.effective->format_info[DFI_OLD_PDB]) return FALSE; - if (!pdb_hack_get_main_info(pair.effective->format_info[DFI_OLD_PDB], &pdb, NULL)) return FALSE; - if (!pdb) return FALSE; + if (!pair.effective->format_info[DFI_PDB]) return FALSE; + pdb = pdb_get_current_reader(pair.effective->format_info[DFI_PDB]); TRACE("searching %Ix => %Ix\n", ip, ip - (DWORD_PTR)pair.effective->module.BaseOfImage); ip -= (DWORD_PTR)pair.effective->module.BaseOfImage; From cd8d0b87d9a3cb3381693c9799b414be908ed1fb Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Sun, 18 May 2025 10:10:16 +0200 Subject: [PATCH 293/454] dbghelp: Don't dupe some exports on 64bit compilation. For a 32bit DLL, dbghelp exports both the 32bit and the 64bit variant of some APIs (eg. StackWalk, SymLoadModule...). For a 64bit DLL, only the 64bit variant is implemented and exported (the two names point to the same address in the export table). This patch: - uses the same function for both names (as native does) in .spec file for 64bit architecture - removes the 32bit variant from 64bit compilation, - adapts also the corresponding import:s in imagehlp from dbghelp. This mostly fixes 64bit apps, getting eg "StackWalk" address with GetProcAddress() and expecting a 64bit code path. (reported & tested by Stefan). Signed-off-by: Eric Pouech (cherry picked from commit 88defefcbe071f26c3d84d7bd520e87fa13ae153) --- dlls/dbghelp/dbghelp.c | 8 +++-- dlls/dbghelp/dbghelp.spec | 64 +++++++++++++++++++++++++------------ dlls/dbghelp/module.c | 14 ++++++++ dlls/dbghelp/stack.c | 8 +++++ dlls/dbghelp/symbol.c | 16 ++++++++++ dlls/imagehlp/imagehlp.spec | 18 +++++------ 6 files changed, 96 insertions(+), 32 deletions(-) diff --git a/dlls/dbghelp/dbghelp.c b/dlls/dbghelp/dbghelp.c index 75881473c2cc..a46d052b8c78 100644 --- a/dlls/dbghelp/dbghelp.c +++ b/dlls/dbghelp/dbghelp.c @@ -764,6 +764,7 @@ BOOL WINAPI SymSetScopeFromInlineContext(HANDLE hProcess, ULONG64 addr, DWORD in } } +#ifndef _WIN64 /****************************************************************** * reg_cb64to32 (internal) * @@ -809,6 +810,7 @@ static BOOL CALLBACK reg_cb64to32(HANDLE hProcess, ULONG action, ULONG64 data, U } return pcs->reg_cb32(hProcess, action, data32, (PVOID)(DWORD_PTR)user); } +#endif /****************************************************************** * pcs_callback (internal) @@ -877,17 +879,19 @@ static BOOL sym_register_cb(HANDLE hProcess, return TRUE; } +#ifndef _WIN64 /*********************************************************************** * SymRegisterCallback (DBGHELP.@) */ -BOOL WINAPI SymRegisterCallback(HANDLE hProcess, +BOOL WINAPI SymRegisterCallback(HANDLE hProcess, PSYMBOL_REGISTERED_CALLBACK CallbackFunction, PVOID UserContext) { - TRACE("(%p, %p, %p)\n", + TRACE("(%p, %p, %p)\n", hProcess, CallbackFunction, UserContext); return sym_register_cb(hProcess, reg_cb64to32, CallbackFunction, (DWORD_PTR)UserContext, FALSE); } +#endif /*********************************************************************** * SymRegisterCallback64 (DBGHELP.@) diff --git a/dlls/dbghelp/dbghelp.spec b/dlls/dbghelp/dbghelp.spec index a89c2c1d86b7..c9ae3bbb73c3 100644 --- a/dlls/dbghelp/dbghelp.spec +++ b/dlls/dbghelp/dbghelp.spec @@ -2,7 +2,8 @@ @ stub DbgHelpCreateUserDumpW @ stdcall EnumDirTree(long str str ptr ptr ptr) @ stdcall EnumDirTreeW(long wstr wstr ptr ptr ptr) -@ stdcall EnumerateLoadedModules(long ptr ptr) +@ stdcall -arch=win32 EnumerateLoadedModules(long ptr ptr) +@ stdcall -arch=win64 EnumerateLoadedModules(long ptr ptr) EnumerateLoadedModules64 @ stdcall EnumerateLoadedModules64(long ptr ptr) @ stdcall EnumerateLoadedModulesEx(long ptr ptr) EnumerateLoadedModules64 @ stdcall EnumerateLoadedModulesExW(long ptr ptr) EnumerateLoadedModulesW64 @@ -30,7 +31,8 @@ @ stdcall MiniDumpWriteDump(ptr long ptr long ptr ptr ptr) @ stdcall SearchTreeForFile(str str ptr) @ stdcall SearchTreeForFileW(wstr wstr ptr) -@ stdcall StackWalk(long long long ptr ptr ptr ptr ptr ptr) +@ stdcall -arch=win32 StackWalk(long long long ptr ptr ptr ptr ptr ptr) +@ stdcall -arch=win64 StackWalk(long long long ptr ptr ptr ptr ptr ptr) StackWalk64 @ stdcall StackWalk64(long long long ptr ptr ptr ptr ptr ptr) @ stdcall StackWalkEx(long long long ptr ptr ptr ptr ptr ptr long) @ stub SymAddSourceStream @@ -60,10 +62,12 @@ @ stdcall SymEnumTypesByName(ptr int64 str ptr ptr) @ stdcall SymEnumTypesByNameW(ptr int64 wstr ptr ptr) @ stdcall SymEnumTypesW(ptr int64 ptr ptr) -@ stdcall SymEnumerateModules(long ptr ptr) +@ stdcall -arch=win32 SymEnumerateModules(long ptr ptr) +@ stdcall -arch=win64 SymEnumerateModules(long ptr ptr) SymEnumerateModules64 @ stdcall SymEnumerateModules64(long ptr ptr) @ stdcall SymEnumerateModulesW64(long ptr ptr) -@ stdcall SymEnumerateSymbols(long long ptr ptr) +@ stdcall -arch=win32 SymEnumerateSymbols(long long ptr ptr) +@ stdcall -arch=win64 SymEnumerateSymbols(long int64 ptr ptr) SymEnumerateSymbols64 @ stdcall SymEnumerateSymbols64(long int64 ptr ptr) @ stub SymEnumerateSymbolsW @ stub SymEnumerateSymbolsW64 @@ -83,31 +87,40 @@ @ stdcall SymFromNameW(long wstr ptr) @ stub SymFromToken @ stub SymFromTokenW -@ stdcall SymFunctionTableAccess(long long) +@ stdcall -arch=win32 SymFunctionTableAccess(long long) +@ stdcall -arch=win64 SymFunctionTableAccess(long int64) SymFunctionTableAccess64 @ stdcall SymFunctionTableAccess64(long int64) @ stub SymGetFileLineOffsets64 @ stub SymGetHomeDirectory @ stub SymGetHomeDirectoryW @ stdcall SymGetExtendedOption(long) -@ stdcall SymGetLineFromAddr(long long ptr ptr) +@ stdcall -arch=win32 SymGetLineFromAddr(long long ptr ptr) +@ stdcall -arch=win64 SymGetLineFromAddr(long int64 ptr ptr) SymGetLineFromAddr64 @ stdcall SymGetLineFromAddr64(long int64 ptr ptr) +@ stub SymGetLineFromAddrW @ stdcall SymGetLineFromAddrW64(long int64 ptr ptr) @ stdcall SymGetLineFromInlineContext(long int64 long int64 ptr ptr) @ stdcall SymGetLineFromInlineContextW(long int64 long int64 ptr ptr) -@ stdcall SymGetLineFromName(long str str long ptr ptr) +@ stdcall -arch=win32 SymGetLineFromName(long str str long ptr ptr) +@ stdcall -arch=win64 SymGetLineFromName(long str str long ptr ptr) SymGetLineFromName64 @ stdcall SymGetLineFromName64(long str str long ptr ptr) @ stdcall SymGetLineFromNameW64(long wstr wstr long ptr ptr) -@ stdcall SymGetLineNext(long ptr) +@ stdcall -arch=win32 SymGetLineNext(long ptr) +@ stdcall -arch=win64 SymGetLineNext(long ptr) SymGetLineNext64 @ stdcall SymGetLineNext64(long ptr) @ stdcall SymGetLineNextW64(long ptr) -@ stdcall SymGetLinePrev(long ptr) +@ stdcall -arch=win32 SymGetLinePrev(long ptr) +@ stdcall -arch=win64 SymGetLinePrev(long ptr) SymGetLinePrev64 @ stdcall SymGetLinePrev64(long ptr) @ stdcall SymGetLinePrevW64(long ptr) -@ stdcall SymGetModuleBase(long long) +@ stdcall -arch=win32 SymGetModuleBase(long long) +@ stdcall -arch=win64 SymGetModuleBase(long int64) SymGetModuleBase64 @ stdcall SymGetModuleBase64(long int64) -@ stdcall SymGetModuleInfo(long long ptr) +@ stdcall -arch=win32 SymGetModuleInfo(long ptr ptr) +@ stdcall -arch=win64 SymGetModuleInfo(long int64 ptr) SymGetModuleInfo64 @ stdcall SymGetModuleInfo64(long int64 ptr) -@ stdcall SymGetModuleInfoW(long long ptr) +@ stdcall -arch=win32 SymGetModuleInfoW(long long ptr) +@ stdcall -arch=win64 SymGetModuleInfoW(long int64 ptr) SymGetModuleInfoW64 @ stdcall SymGetModuleInfoW64(long int64 ptr) @ stub SymGetOmapBlockBase @ stdcall SymGetOptions() @@ -123,13 +136,17 @@ @ stub SymGetSourceFileW @ stub SymGetSourceVarFromToken @ stub SymGetSourceVarFromTokenW -@ stdcall SymGetSymFromAddr(long long ptr ptr) +@ stdcall -arch=win32 SymGetSymFromAddr(long long ptr ptr) +@ stdcall -arch=win64 SymGetSymFromAddr(long int64 ptr ptr) SymGetSymFromAddr64 @ stdcall SymGetSymFromAddr64(long int64 ptr ptr) -@ stdcall SymGetSymFromName(long str ptr) +@ stdcall -arch=win32 SymGetSymFromName(long str ptr) +@ stdcall -arch=win64 SymGetSymFromName(long str ptr) SymGetSymFromName64 @ stdcall SymGetSymFromName64(long str ptr) -@ stdcall SymGetSymNext(long ptr) +@ stdcall -arch=win32 SymGetSymNext(long ptr) +@ stdcall -arch=win64 SymGetSymNext(long ptr) SymGetSymNext64 @ stdcall SymGetSymNext64(long ptr) -@ stdcall SymGetSymPrev(long ptr) +@ stdcall -arch=win32 SymGetSymPrev(long ptr) +@ stdcall -arch=win64 SymGetSymPrev(long ptr) SymGetSymPrev64 @ stdcall SymGetSymPrev64(long ptr) @ stub SymGetSymbolFile @ stub SymGetSymbolFileW @@ -140,7 +157,8 @@ @ stub SymGetUnwindInfo @ stdcall SymInitialize(long str long) @ stdcall SymInitializeW(long wstr long) -@ stdcall SymLoadModule(long long str str long long) +@ stdcall -arch=win32 SymLoadModule(long long str str long long) +@ stdcall -arch=win64 SymLoadModule(long long str str int64 long) SymLoadModule64 @ stdcall SymLoadModule64(long long str str int64 long) @ stdcall SymLoadModuleEx(long long str str int64 long ptr long) @ stdcall SymLoadModuleExW(long long wstr wstr int64 long ptr long) @@ -155,10 +173,12 @@ @ stub SymPrevW @ stdcall SymQueryInlineTrace(long int64 long int64 int64 ptr ptr) @ stdcall SymRefreshModuleList(long) -@ stdcall SymRegisterCallback(long ptr ptr) +@ stdcall -arch=win32 SymRegisterCallback(long ptr ptr) +@ stdcall -arch=win64 SymRegisterCallback(long ptr ptr) SymRegisterCallback64 @ stdcall SymRegisterCallback64(long ptr int64) @ stdcall SymRegisterCallbackW64(long ptr int64) -@ stdcall SymRegisterFunctionEntryCallback(ptr ptr ptr) +@ stdcall -arch=win32 SymRegisterFunctionEntryCallback(long ptr ptr) +@ stdcall -arch=win64 SymRegisterFunctionEntryCallback(long ptr ptr) SymRegisterFunctionEntryCallback64 @ stdcall SymRegisterFunctionEntryCallback64(ptr ptr int64) @ stdcall SymSearch(long int64 long long str int64 ptr ptr long) @ stdcall SymSearchW(long int64 long long wstr int64 ptr ptr long) @@ -191,9 +211,11 @@ @ stub SymSrvStoreSupplementW # @ stub SymSetSymWithAddr64 no longer present ?? @ stub SymSetSymWithAddr64 -@ stdcall SymUnDName(ptr str long) +@ stdcall -arch=win32 SymUnDName(ptr str long) +@ stdcall -arch=win64 SymUnDName(ptr str long) SymUnDName64 @ stdcall SymUnDName64(ptr str long) -@ stdcall SymUnloadModule(long long) +@ stdcall -arch=win32 SymUnloadModule(long long) +@ stdcall -arch=win64 SymUnloadModule(long int64) SymUnloadModule64 @ stdcall SymUnloadModule64(long int64) @ stdcall UnDecorateSymbolName(str ptr long long) @ stdcall UnDecorateSymbolNameW(wstr ptr long long) diff --git a/dlls/dbghelp/module.c b/dlls/dbghelp/module.c index 02bd9b3a0a0d..79a0de42a817 100644 --- a/dlls/dbghelp/module.c +++ b/dlls/dbghelp/module.c @@ -902,6 +902,7 @@ BOOL image_check_alternate(struct image_file_map* fmap, const struct module* mod return FALSE; } +#ifndef _WIN64 /*********************************************************************** * SymLoadModule (DBGHELP.@) */ @@ -911,6 +912,7 @@ DWORD WINAPI SymLoadModule(HANDLE hProcess, HANDLE hFile, PCSTR ImageName, return SymLoadModuleEx(hProcess, hFile, ImageName, ModuleName, BaseOfDll, SizeOfDll, NULL, 0); } +#endif /*********************************************************************** * SymLoadModuleEx (DBGHELP.@) @@ -1127,6 +1129,7 @@ BOOL module_remove(struct process* pcs, struct module* module) return FALSE; } +#ifndef _WIN64 /****************************************************************** * SymUnloadModule (DBGHELP.@) * @@ -1135,6 +1138,7 @@ BOOL WINAPI SymUnloadModule(HANDLE hProcess, DWORD BaseOfDll) { return SymUnloadModule64(hProcess, BaseOfDll); } +#endif /****************************************************************** * SymUnloadModule64 (DBGHELP.@) @@ -1153,6 +1157,7 @@ BOOL WINAPI SymUnloadModule64(HANDLE hProcess, DWORD64 BaseOfDll) return TRUE; } +#ifndef _WIN64 /****************************************************************** * SymEnumerateModules (DBGHELP.@) * @@ -1183,6 +1188,7 @@ BOOL WINAPI SymEnumerateModules(HANDLE hProcess, return SymEnumerateModulesW64(hProcess, enum_modW64_32, &x); } +#endif /****************************************************************** * SymEnumerateModules64 (DBGHELP.@) @@ -1272,6 +1278,7 @@ BOOL WINAPI EnumerateLoadedModules64(HANDLE hProcess, return EnumerateLoadedModulesW64(hProcess, enum_load_modW64_64, &x); } +#ifndef _WIN64 /****************************************************************** * EnumerateLoadedModules (DBGHELP.@) * @@ -1302,6 +1309,7 @@ BOOL WINAPI EnumerateLoadedModules(HANDLE hProcess, return EnumerateLoadedModulesW64(hProcess, enum_load_modW64_32, &x); } +#endif static unsigned int load_and_grow_modules(HANDLE process, HMODULE** hmods, unsigned start, unsigned* alloc, DWORD filter) { @@ -1427,6 +1435,7 @@ static void dbghelp_str_WtoA(const WCHAR *src, char *dst, int dst_len) dst[dst_len - 1] = 0; } +#ifndef _WIN64 /****************************************************************** * SymGetModuleInfo (DBGHELP.@) * @@ -1487,6 +1496,7 @@ BOOL WINAPI SymGetModuleInfoW(HANDLE hProcess, DWORD dwAddr, return TRUE; } +#endif /****************************************************************** * SymGetModuleInfo64 (DBGHELP.@) @@ -1582,6 +1592,7 @@ BOOL WINAPI SymGetModuleInfoW64(HANDLE hProcess, DWORD64 dwAddr, return TRUE; } +#ifndef _WIN64 /*********************************************************************** * SymGetModuleBase (DBGHELP.@) */ @@ -1589,6 +1600,7 @@ DWORD WINAPI SymGetModuleBase(HANDLE hProcess, DWORD dwAddr) { return (DWORD)SymGetModuleBase64(hProcess, dwAddr); } +#endif /*********************************************************************** * SymGetModuleBase64 (DBGHELP.@) @@ -1662,6 +1674,7 @@ BOOL WINAPI SymRefreshModuleList(HANDLE hProcess) return module_refresh_list(pcs); } +#ifndef _WIN64 /*********************************************************************** * SymFunctionTableAccess (DBGHELP.@) */ @@ -1669,6 +1682,7 @@ PVOID WINAPI SymFunctionTableAccess(HANDLE hProcess, DWORD AddrBase) { return SymFunctionTableAccess64(hProcess, AddrBase); } +#endif /*********************************************************************** * SymFunctionTableAccess64 (DBGHELP.@) diff --git a/dlls/dbghelp/stack.c b/dlls/dbghelp/stack.c index 8b573feee77a..a0fc03206685 100644 --- a/dlls/dbghelp/stack.c +++ b/dlls/dbghelp/stack.c @@ -60,6 +60,7 @@ static DWORD64 WINAPI addr_to_linear(HANDLE hProcess, HANDLE hThread, ADDRESS64* return 0; } +#ifndef _WIN64 static BOOL CALLBACK read_mem(HANDLE hProcess, DWORD addr, void* buffer, DWORD size, LPDWORD nread) { @@ -68,6 +69,7 @@ static BOOL CALLBACK read_mem(HANDLE hProcess, DWORD addr, void* buffer, if (nread) *nread = r; return TRUE; } +#endif static BOOL CALLBACK read_mem64(HANDLE hProcess, DWORD64 addr, void* buffer, DWORD size, LPDWORD nread) @@ -78,12 +80,14 @@ static BOOL CALLBACK read_mem64(HANDLE hProcess, DWORD64 addr, void* buffer, return TRUE; } +#ifndef _WIN64 static inline void addr_32to64(const ADDRESS* addr32, ADDRESS64* addr64) { addr64->Offset = (ULONG64)addr32->Offset; addr64->Segment = addr32->Segment; addr64->Mode = addr32->Mode; } +#endif static inline void addr_64to32(const ADDRESS64* addr64, ADDRESS* addr32) { @@ -132,6 +136,7 @@ DWORD64 sw_module_base(struct cpu_stack_walk* csw, DWORD64 addr) return csw->u.s64.f_modl_bas(csw->hProcess, addr); } +#ifndef _WIN64 /*********************************************************************** * StackWalk (DBGHELP.@) */ @@ -202,6 +207,7 @@ BOOL WINAPI StackWalk(DWORD MachineType, HANDLE hProcess, HANDLE hThread, return ret; } +#endif /*********************************************************************** @@ -340,6 +346,7 @@ BOOL WINAPI StackWalkEx(DWORD MachineType, HANDLE hProcess, HANDLE hThread, return TRUE; } +#ifndef _WIN64 /****************************************************************** * SymRegisterFunctionEntryCallback (DBGHELP.@) * @@ -352,6 +359,7 @@ BOOL WINAPI SymRegisterFunctionEntryCallback(HANDLE hProc, SetLastError(ERROR_CALL_NOT_IMPLEMENTED); return FALSE; } +#endif /****************************************************************** * SymRegisterFunctionEntryCallback64 (DBGHELP.@) diff --git a/dlls/dbghelp/symbol.c b/dlls/dbghelp/symbol.c index 7ab11e2f4eb8..f051fa793ff6 100644 --- a/dlls/dbghelp/symbol.c +++ b/dlls/dbghelp/symbol.c @@ -1486,6 +1486,7 @@ BOOL WINAPI SymEnumSymbolsW(HANDLE hProcess, ULONG64 BaseOfDll, PCWSTR Mask, return doSymEnumSymbols(hProcess, BaseOfDll, Mask, sym_enumW, &sew); } +#ifndef _WIN64 struct sym_enumerate { void* ctx; @@ -1512,6 +1513,7 @@ BOOL WINAPI SymEnumerateSymbols(HANDLE hProcess, DWORD BaseOfDll, return SymEnumSymbols(hProcess, BaseOfDll, NULL, sym_enumerate_cb, &se); } +#endif struct sym_enumerate64 { @@ -1856,6 +1858,7 @@ static void init_lineinfo(struct lineinfo_t* line_info, BOOL unicode) line_info->address = 0; } +#ifndef _WIN64 static BOOL lineinfo_copy_toA32(const struct lineinfo_t* line_info, IMAGEHLP_LINE* l32) { if (line_info->unicode) return FALSE; @@ -1865,6 +1868,7 @@ static BOOL lineinfo_copy_toA32(const struct lineinfo_t* line_info, IMAGEHLP_LIN l32->Address = line_info->address; return TRUE; } +#endif static BOOL lineinfo_copy_toA64(const struct lineinfo_t* line_info, IMAGEHLP_LINE64* l64) { @@ -2011,6 +2015,7 @@ BOOL WINAPI SymGetSymNext64(HANDLE hProcess, PIMAGEHLP_SYMBOL64 Symbol) return FALSE; } +#ifndef _WIN64 /*********************************************************************** * SymGetSymNext (DBGHELP.@) */ @@ -2020,6 +2025,7 @@ BOOL WINAPI SymGetSymNext(HANDLE hProcess, PIMAGEHLP_SYMBOL Symbol) SetLastError(ERROR_CALL_NOT_IMPLEMENTED); return FALSE; } +#endif /*********************************************************************** * SymGetSymPrev64 (DBGHELP.@) @@ -2031,6 +2037,7 @@ BOOL WINAPI SymGetSymPrev64(HANDLE hProcess, PIMAGEHLP_SYMBOL64 Symbol) return FALSE; } +#ifndef _WIN64 /*********************************************************************** * SymGetSymPrev (DBGHELP.@) */ @@ -2040,7 +2047,9 @@ BOOL WINAPI SymGetSymPrev(HANDLE hProcess, PIMAGEHLP_SYMBOL Symbol) SetLastError(ERROR_CALL_NOT_IMPLEMENTED); return FALSE; } +#endif +#ifndef _WIN64 /****************************************************************** * SymGetLineFromAddr (DBGHELP.@) * @@ -2057,6 +2066,7 @@ BOOL WINAPI SymGetLineFromAddr(HANDLE hProcess, DWORD dwAddr, if (!get_line_from_addr(hProcess, dwAddr, pdwDisplacement, &line_info)) return FALSE; return lineinfo_copy_toA32(&line_info, Line); } +#endif /****************************************************************** * SymGetLineFromAddr64 (DBGHELP.@) @@ -2154,6 +2164,7 @@ BOOL WINAPI SymGetLinePrev64(HANDLE hProcess, PIMAGEHLP_LINE64 Line) return lineinfo_copy_toA64(&line_info, Line); } +#ifndef _WIN64 /****************************************************************** * SymGetLinePrev (DBGHELP.@) * @@ -2169,6 +2180,7 @@ BOOL WINAPI SymGetLinePrev(HANDLE hProcess, PIMAGEHLP_LINE Line) if (!symt_get_func_line_prev(hProcess, &line_info, Line->Key, Line->Address)) return FALSE; return lineinfo_copy_toA32(&line_info, Line); } +#endif /****************************************************************** * SymGetLinePrevW64 (DBGHELP.@) @@ -2244,6 +2256,7 @@ BOOL WINAPI SymGetLineNext64(HANDLE hProcess, PIMAGEHLP_LINE64 Line) return lineinfo_copy_toA64(&line_info, Line); } +#ifndef _WIN64 /****************************************************************** * SymGetLineNext (DBGHELP.@) * @@ -2259,6 +2272,7 @@ BOOL WINAPI SymGetLineNext(HANDLE hProcess, PIMAGEHLP_LINE Line) if (!symt_get_func_line_next(hProcess, &line_info, Line->Key, Line->Address)) return FALSE; return lineinfo_copy_toA32(&line_info, Line); } +#endif /****************************************************************** * SymGetLineNextW64 (DBGHELP.@) @@ -2276,6 +2290,7 @@ BOOL WINAPI SymGetLineNextW64(HANDLE hProcess, PIMAGEHLP_LINEW64 Line) return lineinfo_copy_toW64(&line_info, Line); } +#ifndef _WIN64 /*********************************************************************** * SymUnDName (DBGHELP.@) */ @@ -2284,6 +2299,7 @@ BOOL WINAPI SymUnDName(PIMAGEHLP_SYMBOL sym, PSTR UnDecName, DWORD UnDecNameLeng return UnDecorateSymbolName(sym->Name, UnDecName, UnDecNameLength, UNDNAME_COMPLETE) != 0; } +#endif /*********************************************************************** * SymUnDName64 (DBGHELP.@) diff --git a/dlls/imagehlp/imagehlp.spec b/dlls/imagehlp/imagehlp.spec index 7d934dcc96e5..4e784f8bf222 100644 --- a/dlls/imagehlp/imagehlp.spec +++ b/dlls/imagehlp/imagehlp.spec @@ -51,16 +51,16 @@ @ stdcall -import SymEnumerateModules64(long ptr ptr) @ stdcall -import SymEnumerateModules(long ptr ptr) @ stdcall -import SymEnumerateSymbols64(long int64 ptr ptr) -@ stdcall -import SymEnumerateSymbols(long long ptr ptr) +@ stdcall -import SymEnumerateSymbols(long ptr ptr ptr) @ stub SymEnumerateSymbolsW64 @ stub SymEnumerateSymbolsW @ stdcall -import SymFindFileInPath(long str str ptr long long long ptr ptr ptr) @ stdcall -import SymFromAddr(ptr int64 ptr ptr) @ stdcall -import SymFromName(long str ptr) @ stdcall -import SymFunctionTableAccess64(long int64) -@ stdcall -import SymFunctionTableAccess(long long) +@ stdcall -import SymFunctionTableAccess(long ptr) @ stdcall -import SymGetLineFromAddr64(long int64 ptr ptr) -@ stdcall -import SymGetLineFromAddr(long long ptr ptr) +@ stdcall -import SymGetLineFromAddr(long ptr ptr ptr) @ stub SymGetLineFromName64 @ stub SymGetLineFromName @ stdcall -import SymGetLineNext64(long ptr) @@ -68,15 +68,15 @@ @ stdcall -import SymGetLinePrev64(long ptr) @ stdcall -import SymGetLinePrev(long ptr) @ stdcall -import SymGetModuleBase64(long int64) -@ stdcall -import SymGetModuleBase(long long) +@ stdcall -import SymGetModuleBase(long ptr) @ stdcall -import SymGetModuleInfo64(long int64 ptr) -@ stdcall -import SymGetModuleInfo(long long ptr) +@ stdcall -import SymGetModuleInfo(long ptr ptr) @ stdcall -import SymGetModuleInfoW64(long int64 ptr) -@ stdcall -import SymGetModuleInfoW(long long ptr) +@ stdcall -import SymGetModuleInfoW(long ptr ptr) @ stdcall -import SymGetOptions() @ stdcall -import SymGetSearchPath(long ptr long) @ stdcall -import SymGetSymFromAddr64(long int64 ptr ptr) -@ stdcall -import SymGetSymFromAddr(long long ptr ptr) +@ stdcall -import SymGetSymFromAddr(long ptr ptr ptr) @ stdcall -import SymGetSymFromName64(long str ptr) @ stdcall -import SymGetSymFromName(long str ptr) @ stdcall -import SymGetSymNext64(long ptr) @@ -87,7 +87,7 @@ @ stdcall -import SymGetTypeInfo(ptr int64 long long ptr) @ stdcall -import SymInitialize(long str long) @ stdcall -import SymLoadModule64(long long str str int64 long) -@ stdcall -import SymLoadModule(long long str str long long) +@ stdcall -import SymLoadModule(long long str str ptr long) @ stdcall -import SymMatchFileName(str str ptr ptr) @ stdcall -import SymMatchString(str str long) @ stdcall -import SymRegisterCallback64(long ptr int64) @@ -100,7 +100,7 @@ @ stdcall -import SymUnDName64(ptr str long) @ stdcall -import SymUnDName(ptr str long) @ stdcall -import SymUnloadModule64(long int64) -@ stdcall -import SymUnloadModule(long long) +@ stdcall -import SymUnloadModule(long ptr) @ stdcall TouchFileTimes(long ptr) @ stdcall -import UnDecorateSymbolName(str str long long) @ stdcall UnMapAndLoad(ptr) From 5ccfee19b2da1c37dd83c9ce0b20239721286251 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20=C3=9Cbelacker?= Date: Sun, 8 Jun 2025 21:15:16 +0200 Subject: [PATCH 294/454] dbghelp: Avoid use after free by moving assignment before vector_add (ASan). Followup to 5c54087c47. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=58340 (cherry picked from commit 8f3bcabbc6af0645ec46d1e18cecca5dc832ebe6) --- dlls/dbghelp/symbol.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dlls/dbghelp/symbol.c b/dlls/dbghelp/symbol.c index f051fa793ff6..29a32ad7dcf3 100644 --- a/dlls/dbghelp/symbol.c +++ b/dlls/dbghelp/symbol.c @@ -432,6 +432,8 @@ void symt_add_func_line(struct module* module, struct symt_function* func, WARN("Duplicate addition of line number in %s\n", debugstr_a(func->hash_elt.name)); return; } + /* clear previous last */ + if (prev) prev->is_last = 0; if (!last_matches) { /* we shouldn't have line changes on first line of function */ @@ -442,8 +444,6 @@ void symt_add_func_line(struct module* module, struct symt_function* func, dli->line_number = 0; dli->u.source_file = source_idx; } - /* clear previous last */ - if (prev) prev->is_last = 0; dli = vector_add(&func->vlines, &module->pool); dli->is_source_file = 0; dli->is_first = 0; /* only a source file can be first */ From e251cb20fb43e8e9e7c08b6d53a123968e6c107c Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Tue, 24 Jun 2025 18:40:41 +0100 Subject: [PATCH 295/454] dsound: Make sure to null-terminate strings for callbacks. WideCharToMultiByte normally null-terminates the output if input length is -1, but it doesn't do so if the output buffer is too small. (cherry picked from commit 81d464bc3db966f72679652221aea6ab4e0b64f8) --- dlls/dsound/dsound_main.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/dlls/dsound/dsound_main.c b/dlls/dsound/dsound_main.c index bb373a4304d3..19e94c1071db 100644 --- a/dlls/dsound/dsound_main.c +++ b/dlls/dsound/dsound_main.c @@ -296,12 +296,22 @@ struct morecontext static BOOL CALLBACK a_to_w_callback(LPGUID guid, LPCWSTR descW, LPCWSTR modW, LPVOID data) { struct morecontext *context = data; - char descA[MAXPNAMELEN], modA[MAXPNAMELEN]; + char *descA, *modA; + DWORD len; + BOOL ret; + + len = WideCharToMultiByte(CP_ACP, 0, descW, -1, NULL, 0, NULL, NULL); + if ((descA = malloc(len))) + WideCharToMultiByte(CP_ACP, 0, descW, -1, descA, len, NULL, NULL); + len = WideCharToMultiByte(CP_ACP, 0, modW, -1, NULL, 0, NULL, NULL); + if ((modA = malloc(len))) + WideCharToMultiByte(CP_ACP, 0, modW, -1, modA, len, NULL, NULL); - WideCharToMultiByte(CP_ACP, 0, descW, -1, descA, sizeof(descA), NULL, NULL); - WideCharToMultiByte(CP_ACP, 0, modW, -1, modA, sizeof(modA), NULL, NULL); + ret = context->callA(guid, descA, modA, context->data); - return context->callA(guid, descA, modA, context->data); + free(descA); + free(modA); + return ret; } /*************************************************************************** From be2173c6d36a6702172881ed619ce6972c397240 Mon Sep 17 00:00:00 2001 From: Ethan Lee Date: Fri, 11 Jul 2025 14:21:06 -0600 Subject: [PATCH 296/454] faudio: Debug: Avoid a use-after-free when logging api exits in DestroyVoice (cherry picked from commit 3b7037e413a4ad72fd5d3af6f2c475251dd4b503) --- libs/faudio/src/FAudio.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/libs/faudio/src/FAudio.c b/libs/faudio/src/FAudio.c index b85db87dd19e..47e6b2d02fe8 100644 --- a/libs/faudio/src/FAudio.c +++ b/libs/faudio/src/FAudio.c @@ -2508,20 +2508,22 @@ uint32_t FAudioVoice_DestroyVoiceSafeEXT(FAudioVoice *voice) { uint32_t ret; - LOG_API_ENTER(voice->audio) + FAudio* audio = voice->audio; + + LOG_API_ENTER(audio) if ((ret = check_for_sends_to_voice(voice))) { LOG_ERROR( - voice->audio, + audio, "Voice %p is an output for other voice(s)", voice ) - LOG_API_EXIT(voice->audio) + LOG_API_EXIT(audio) return ret; } destroy_voice(voice); - LOG_API_EXIT(voice->audio) + LOG_API_EXIT(audio) return 0; } From edadea5f8f37addd3f42a9966218dc364d1df8ad Mon Sep 17 00:00:00 2001 From: Arkadiusz Hiler Date: Mon, 14 Jul 2025 09:59:25 +0300 Subject: [PATCH 297/454] Revert "dmime: Load soundfont from environment variable." This reverts commit d0d4d97c8e88313031d76bd0237985dbdfc5e60b. No longer needed - we use registry key instead. --- dlls/dmloader/loader.c | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/dlls/dmloader/loader.c b/dlls/dmloader/loader.c index 2e663061c25d..3798a0851a0d 100644 --- a/dlls/dmloader/loader.c +++ b/dlls/dmloader/loader.c @@ -888,17 +888,10 @@ static const IDirectMusicLoader8Vtbl loader_vtbl = static HRESULT get_default_gm_path(WCHAR *path, DWORD max_len) { - DWORD ret; - HKEY hkey; - static const WCHAR PROTON_SOUNDFILES_FILES_W[] = {'P','R','O','T','O','N','_','S','O','U','N','D','F','O','N','T','_','F','I','L','E','S',0}; - if (GetEnvironmentVariableW(PROTON_SOUNDFILES_FILES_W, path, max_len)) - { - TRACE("Found soundfont files from environment %s\n", debugstr_w(path)); - if (GetFileAttributesW(path) != INVALID_FILE_ATTRIBUTES) return S_OK; - WARN("Counldn't find %s\n", debugstr_w(path)); - } + DWORD ret; + HKEY hkey; - if (!(ret = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\DirectMusic" , 0, KEY_READ, &hkey))) + if (!(ret = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\DirectMusic" , 0, KEY_READ, &hkey))) { DWORD type, size = max_len * sizeof(WCHAR); ret = RegQueryValueExW(hkey, L"GMFilePath", NULL, &type, (BYTE *)path, &size); From f5223bf6bb16c9fc60e8ab91e87d9a61e4464728 Mon Sep 17 00:00:00 2001 From: Arkadiusz Hiler Date: Mon, 14 Jul 2025 12:46:26 +0300 Subject: [PATCH 298/454] dmloader: Touch soundfont-used file whenever looking up sound fonts. CW-Bug-Id: #23141 --- dlls/dmloader/loader.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/dlls/dmloader/loader.c b/dlls/dmloader/loader.c index 3798a0851a0d..19c1c2ac9917 100644 --- a/dlls/dmloader/loader.c +++ b/dlls/dmloader/loader.c @@ -886,11 +886,40 @@ static const IDirectMusicLoader8Vtbl loader_vtbl = loader_LoadObjectFromFile, }; +static inline void touch_soundfont_used_tag(void) +{ + WCHAR env[MAX_PATH]; + + if (GetEnvironmentVariableW(L"STEAM_COMPAT_TRANSCODED_MEDIA_PATH", env, ARRAY_SIZE(env))) + { + WCHAR buffer[MAX_PATH]; + HANDLE file; + + swprintf(buffer, ARRAY_SIZE(buffer), L"\\??\\unix%s/soundfont-used", env); + + file = CreateFileW(buffer, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + + if (file == INVALID_HANDLE_VALUE) + { + ERR("Failed to open/create %s\n", debugstr_w(buffer)); + return; + } + + CloseHandle(file); + } + else + { + ERR("STEAM_COMPAT_TRANSCODED_MEDIA_PATH not set, cannot create soundfont-used file.\n"); + } +} + static HRESULT get_default_gm_path(WCHAR *path, DWORD max_len) { DWORD ret; HKEY hkey; + touch_soundfont_used_tag(); + if (!(ret = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\DirectMusic" , 0, KEY_READ, &hkey))) { DWORD type, size = max_len * sizeof(WCHAR); From 78782b342419fbd802b6d2c14cc94d61e2c21fc7 Mon Sep 17 00:00:00 2001 From: Arkadiusz Hiler Date: Tue, 15 Jul 2025 09:12:46 +0300 Subject: [PATCH 299/454] Revert "HACK: winex11.drv/opengl: Do not trigger a libX11 bug." This reverts commit 11c7c2b7abaa18c1d2cb8406d18ded5dea0e677e. Looks like most distros that are still supported have enough up to date libx11 to remove this. CW-Bug-Id: #20026 --- dlls/winex11.drv/opengl.c | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/dlls/winex11.drv/opengl.c b/dlls/winex11.drv/opengl.c index c6b3e9fe6223..cf06d27e80b6 100644 --- a/dlls/winex11.drv/opengl.c +++ b/dlls/winex11.drv/opengl.c @@ -3468,21 +3468,6 @@ static struct wgl_context *X11DRV_wglCreateContextAttribsARB( HDC hdc, struct wg case WGL_CONTEXT_LAYER_PLANE_ARB: break; case WGL_CONTEXT_FLAGS_ARB: - /* HACK: The Last Campfire sometimes uses an - * invalid value for WGL_CONTEXT_FLAGS_ARB, which - * triggers - * https://gitlab.freedesktop.org/xorg/lib/libx11/-/issues/152 - * on the Deck. If we see the invalid value we - * directly return an error, so that Wine doesn't - * crash. This hack can be removed once that issue - * is fixed. */ - if (attribList[1] == 0x31b3) - { - WARN("return early to avoid triggering a libX11 bug\n"); - free(ret); - release_gl_drawable(gl); - return NULL; - } pContextAttribList[0] = GLX_CONTEXT_FLAGS_ARB; pContextAttribList[1] = attribList[1]; pContextAttribList += 2; From cb0202c19595d87e6502af733ca04475bba2520c Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 17 Jul 2025 11:17:48 -0600 Subject: [PATCH 300/454] Revert "ntdll: HACK: Limit address space for Wuthering Waves." This reverts commit b5f0e4e3ec423b0c62016bda406623d96a4ed50f. CW-Bug-Id: #25606 --- dlls/ntdll/unix/virtual.c | 1 - 1 file changed, 1 deletion(-) diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c index 370f012de181..d955a6172484 100644 --- a/dlls/ntdll/unix/virtual.c +++ b/dlls/ntdll/unix/virtual.c @@ -2495,7 +2495,6 @@ static void fixup_effective_user_space_limit( const void **effective_user_space_ cached = sgi && ( !strcmp( sgi, "3092660" ) - || !strcmp( sgi, "3513350" ) ); } if (cached) From 981fc5ada11bcf6b5e4321e1246659d5f8b1bf61 Mon Sep 17 00:00:00 2001 From: Arkadiusz Hiler Date: Mon, 21 Jul 2025 17:18:44 +0300 Subject: [PATCH 301/454] fixup! mfsrcsnk: HACK: Append transcoded audio info to user data and return AAC for Warhammer 40,000: Dakka Squadron. Suggested-by: Federico Dossena Link: https://github.com/ValveSoftware/wine/issues/291 --- dlls/mfsrcsnk/media_source.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/mfsrcsnk/media_source.c b/dlls/mfsrcsnk/media_source.c index cf40cb084c7a..40f7ecd10745 100644 --- a/dlls/mfsrcsnk/media_source.c +++ b/dlls/mfsrcsnk/media_source.c @@ -1471,7 +1471,7 @@ static HRESULT media_type_from_winedmo_format( GUID major, union winedmo_format /* Warhammer 40,000: Dakka Squadron depends on the input format belonging to a specific set of formats. * Append transcoded audio info to the user data so it can be restored, and create a fake AAC media * type instead. If decoding support is added, PCM will work without a hack. */ - if (!strcmp(sgi, "1253190") && format->audio.wFormatTag == WAVE_FORMAT_EXTENSIBLE + if (sgi && !strcmp(sgi, "1253190") && format->audio.wFormatTag == WAVE_FORMAT_EXTENSIBLE && IsEqualGUID(&audio->SubFormat, &MFAudioFormat_Vorbis)) { size_t config_data_size = format->audio.cbSize + sizeof(WAVEFORMATEX) - sizeof(WAVEFORMATEXTENSIBLE); From 89ccdd2a8541e06edb487e36d65c4040b2047a41 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 22 Jul 2025 15:14:26 -0600 Subject: [PATCH 302/454] winex11.drv: HACK: Make sure popups are not managed for Rockstar Launcher. CW-Bug-Id: #25698 --- dlls/winex11.drv/window.c | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index c75cf372924e..0adcc76753dd 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -395,6 +395,31 @@ static struct x11drv_win_data *alloc_win_data( Display *display, HWND hwnd ) } +static BOOL thickframe_managed( DWORD style ) +{ + static int cached = -1; + + if (!(style & WS_POPUP)) return TRUE; + + if (cached == -1) + { + static const WCHAR app_name[] = u"\\SocialClubHelper.exe"; + UNICODE_STRING *name; + DWORD len, name_len; + + cached = 1; + + name = &NtCurrentTeb()->Peb->ProcessParameters->ImagePathName; + len = name->Length / sizeof(WCHAR); + name_len = ARRAY_SIZE(app_name) - 1; + if (len >= name_len) + cached = !!memcmp( name->Buffer + len - name_len, app_name, name_len * sizeof(*app_name) ); + if (!cached) FIXME( "HACK: making popups with WS_THICKFRAME not managed.\n" ); + } + return cached; +} + + /*********************************************************************** * is_window_managed * @@ -415,7 +440,7 @@ static BOOL is_window_managed( HWND hwnd, UINT swp_flags, BOOL fullscreen ) /* windows with caption are managed */ if ((style & WS_CAPTION) == WS_CAPTION) return TRUE; /* windows with thick frame are managed */ - if (style & WS_THICKFRAME) return TRUE; + if (style & WS_THICKFRAME && thickframe_managed( style )) return TRUE; if (style & WS_POPUP) { /* popup with sysmenu == caption are managed */ From a309e71890ac48579f1d869e1a84c24766b47f45 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 23 Jul 2025 11:14:23 -0600 Subject: [PATCH 303/454] winepulse.drv: Move pulse_release_stream() below. CW-Bug-Id: #25600 --- dlls/winepulse.drv/pulse.c | 72 +++++++++++++++++++------------------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/dlls/winepulse.drv/pulse.c b/dlls/winepulse.drv/pulse.c index 462fb20071e5..e9ea52fcfc09 100644 --- a/dlls/winepulse.drv/pulse.c +++ b/dlls/winepulse.drv/pulse.c @@ -1244,42 +1244,6 @@ static NTSTATUS pulse_create_stream(void *args) return STATUS_SUCCESS; } -static NTSTATUS pulse_release_stream(void *args) -{ - struct release_stream_params *params = args; - struct pulse_stream *stream = handle_get_stream(params->stream); - SIZE_T size; - - if(params->timer_thread) { - stream->please_quit = TRUE; - NtWaitForSingleObject(params->timer_thread, FALSE, NULL); - NtClose(params->timer_thread); - } - - pulse_lock(); - if (PA_STREAM_IS_GOOD(pa_stream_get_state(stream->stream))) { - pa_stream_disconnect(stream->stream); - while (pulse_ml && PA_STREAM_IS_GOOD(pa_stream_get_state(stream->stream))) - pulse_cond_wait(); - } - pa_stream_unref(stream->stream); - pulse_unlock(); - - if (stream->tmp_buffer) { - size = 0; - NtFreeVirtualMemory(GetCurrentProcess(), (void **)&stream->tmp_buffer, - &size, MEM_RELEASE); - } - if (stream->local_buffer) { - size = 0; - NtFreeVirtualMemory(GetCurrentProcess(), (void **)&stream->local_buffer, - &size, MEM_RELEASE); - } - free(stream->peek_buffer); - free(stream); - return STATUS_SUCCESS; -} - static int write_buffer(const struct pulse_stream *stream, BYTE *buffer, UINT32 bytes) { const float *vol = stream->vol; @@ -1666,6 +1630,42 @@ static NTSTATUS pulse_timer_loop(void *args) return STATUS_SUCCESS; } +static NTSTATUS pulse_release_stream(void *args) +{ + struct release_stream_params *params = args; + struct pulse_stream *stream = handle_get_stream(params->stream); + SIZE_T size; + + if(params->timer_thread) { + stream->please_quit = TRUE; + NtWaitForSingleObject(params->timer_thread, FALSE, NULL); + NtClose(params->timer_thread); + } + + pulse_lock(); + if (PA_STREAM_IS_GOOD(pa_stream_get_state(stream->stream))) { + pa_stream_disconnect(stream->stream); + while (pulse_ml && PA_STREAM_IS_GOOD(pa_stream_get_state(stream->stream))) + pulse_cond_wait(); + } + pa_stream_unref(stream->stream); + pulse_unlock(); + + if (stream->tmp_buffer) { + size = 0; + NtFreeVirtualMemory(GetCurrentProcess(), (void **)&stream->tmp_buffer, + &size, MEM_RELEASE); + } + if (stream->local_buffer) { + size = 0; + NtFreeVirtualMemory(GetCurrentProcess(), (void **)&stream->local_buffer, + &size, MEM_RELEASE); + } + free(stream->peek_buffer); + free(stream); + return STATUS_SUCCESS; +} + static NTSTATUS pulse_start(void *args) { struct start_params *params = args; From 17da3db0eec9911398764aac264d652d3d2a4c71 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 23 Jun 2025 17:57:56 -0600 Subject: [PATCH 304/454] winepulse.drv: Process streams timer updates from PA main loop. CW-Bug-Id: #25600 --- dlls/winepulse.drv/pulse.c | 183 +++++++++++++++++++++---------------- 1 file changed, 102 insertions(+), 81 deletions(-) diff --git a/dlls/winepulse.drv/pulse.c b/dlls/winepulse.drv/pulse.c index e9ea52fcfc09..07c2024a380e 100644 --- a/dlls/winepulse.drv/pulse.c +++ b/dlls/winepulse.drv/pulse.c @@ -54,6 +54,17 @@ enum phys_device_bus_type { phys_device_bus_usb }; +struct pulse_period +{ + struct list entry; + pa_usec_t timer_last_time; + pa_usec_t period; + struct list streams; + pa_time_event *time_event; +}; + +static struct list active_periods = LIST_INIT(active_periods); + struct pulse_stream { EDataFlow dataflow; @@ -78,13 +89,15 @@ struct pulse_stream SIZE_T tmp_buffer_bytes, held_bytes, peek_len, peek_buffer_len, pa_held_bytes; BYTE *local_buffer, *tmp_buffer, *peek_buffer; void *locked_ptr; - BOOL please_quit, just_started, just_underran; + BOOL just_started, just_underran; pa_usec_t mmdev_period_usec; INT64 clock_lastpos, clock_written; struct list packet_free_head; struct list packet_filled_head; + struct list period_entry; + struct pulse_period *period; }; typedef struct _ACPacket @@ -1531,103 +1544,110 @@ static void pulse_read(struct pulse_stream *stream) static NTSTATUS pulse_timer_loop(void *args) { - struct timer_loop_params *params = args; - struct pulse_stream *stream = handle_get_stream(params->stream); - LARGE_INTEGER delay; - pa_usec_t last_time; - UINT32 adv_bytes; - int success; - - pulse_lock(); - delay.QuadPart = -stream->mmdev_period_usec * 10; - pa_stream_get_time(stream->stream, &last_time); - pulse_unlock(); - - while (!stream->please_quit) - { - pa_usec_t now, adv_usec = 0; - int err; + /* Stream's data are read and written from the main loop timer callback. */ + return STATUS_SUCCESS; +} - NtDelayExecution(FALSE, &delay); +static void pa_streams_timer_cb(pa_mainloop_api *api, pa_time_event *e, const struct timeval *tv, void *userdata) +{ + struct pulse_period *period = userdata; + struct pulse_stream *stream; + UINT32 adv_bytes; - pulse_lock(); + period->timer_last_time += period->period; - delay.QuadPart = -stream->mmdev_period_usec * 10; + TRACE("period %p, now %llu, timer_last_time %llu.\n", period, (long long)pa_rtclock_now(), (long long)period->timer_last_time); - wait_pa_operation_complete(pa_stream_update_timing_info(stream->stream, pulse_op_cb, &success)); - err = pa_stream_get_time(stream->stream, &now); - if (err == 0) + LIST_FOR_EACH_ENTRY(stream, &period->streams, struct pulse_stream, period_entry) + { + if (stream->started) { - TRACE("got now: %s, last time: %s\n", wine_dbgstr_longlong(now), wine_dbgstr_longlong(last_time)); - if (stream->started && (stream->dataflow == eCapture || stream->held_bytes)) + if (stream->dataflow == eRender) { - if(stream->just_underran) - { - last_time = now; - stream->just_started = TRUE; - } + pulse_write(stream); - if (stream->just_started) - { - /* let it play out a period to absorb some latency and get accurate timing */ - pa_usec_t diff = now - last_time; - - if (diff > stream->mmdev_period_usec) - { - stream->just_started = FALSE; - last_time = now; - } - } - else - { - INT32 adjust = last_time + stream->mmdev_period_usec - now; + /* regardless of what PA does, advance one period */ + adv_bytes = min(stream->period_bytes, stream->held_bytes); + stream->lcl_offs_bytes += adv_bytes; + stream->lcl_offs_bytes %= stream->real_bufsize_bytes; + stream->held_bytes -= adv_bytes; + } + else if(stream->dataflow == eCapture) + { + pulse_read(stream); + } + } + if (stream->event) + NtSetEvent(stream->event, NULL); + } + pa_context_rttime_restart(pulse_ctx, e, period->timer_last_time + period->period); +} - adv_usec = now - last_time; +static void pa_streams_timer_cb_destroy(pa_mainloop_api *api, pa_time_event *e, void *userdata) +{ + struct pulse_period *period = userdata; - if(adjust > ((INT32)(stream->mmdev_period_usec / 2))) - adjust = stream->mmdev_period_usec / 2; - else if(adjust < -((INT32)(stream->mmdev_period_usec / 2))) - adjust = -1 * stream->mmdev_period_usec / 2; + TRACE("period %p.\n", period); - delay.QuadPart = -(stream->mmdev_period_usec + adjust) * 10; + list_remove(&period->entry); + free(period); +} - last_time += stream->mmdev_period_usec; - } +static void remove_stream_from_period(struct pulse_stream *stream) +{ + if (!stream->period) + return; - if (stream->dataflow == eRender) - { - pulse_write(stream); + list_remove(&stream->period_entry); + if (list_empty(&stream->period->streams) && pulse_ml) + { + pa_mainloop_api *api = pa_mainloop_get_api(pulse_ml); - /* regardless of what PA does, advance one period */ - adv_bytes = min(stream->period_bytes, stream->held_bytes); - stream->lcl_offs_bytes += adv_bytes; - stream->lcl_offs_bytes %= stream->real_bufsize_bytes; - stream->held_bytes -= adv_bytes; - } - else if(stream->dataflow == eCapture) - { - pulse_read(stream); - } - } - else - { - last_time = now; - delay.QuadPart = -stream->mmdev_period_usec * 10; - } - } + TRACE("freeing time event for period %p.\n", stream->period); + api->time_free(stream->period->time_event); + stream->period->time_event = NULL; + } +} - if (stream->event) - NtSetEvent(stream->event, NULL); +static void pulse_add_stream_to_period(struct pulse_stream *stream) +{ + struct pulse_period *period; + pa_mainloop_api *api; - TRACE("%p after update, adv usec: %d, held: %u, delay usec: %u\n", - stream, (int)adv_usec, - (int)(stream->held_bytes/ pa_frame_size(&stream->ss)), - (unsigned int)(-delay.QuadPart / 10)); + if (stream->period) + { + assert(stream->mmdev_period_usec == stream->period->period); + return; + } - pulse_unlock(); + LIST_FOR_EACH_ENTRY(period, &active_periods, struct pulse_period, entry) + { + if (!period->time_event) + { + /* Period is being removed but pa_streams_timer_cb_destroy was not called yet. */ + continue; + } + if (period->period == stream->mmdev_period_usec) + { + TRACE("Using period %p.\n", period); + stream->period = period; + list_add_tail(&period->streams, &stream->period_entry); + return; + } } - return STATUS_SUCCESS; + period = calloc(1, sizeof(*period)); + period->period = stream->mmdev_period_usec; + list_init(&period->streams); + stream->period = period; + list_add_tail(&period->streams, &stream->period_entry); + list_add_tail(&active_periods, &period->entry); + period->timer_last_time = pa_rtclock_now(); + period->time_event = pa_context_rttime_new(pulse_ctx, period->timer_last_time + period->period, + pa_streams_timer_cb, period); + api = pa_mainloop_get_api(pulse_ml); + api->time_set_destroy(period->time_event, pa_streams_timer_cb_destroy); + TRACE("Created period %p.\n", period); } static NTSTATUS pulse_release_stream(void *args) @@ -1637,12 +1657,12 @@ static NTSTATUS pulse_release_stream(void *args) SIZE_T size; if(params->timer_thread) { - stream->please_quit = TRUE; NtWaitForSingleObject(params->timer_thread, FALSE, NULL); NtClose(params->timer_thread); } pulse_lock(); + remove_stream_from_period(stream); if (PA_STREAM_IS_GOOD(pa_stream_get_state(stream->stream))) { pa_stream_disconnect(stream->stream); while (pulse_ml && PA_STREAM_IS_GOOD(pa_stream_get_state(stream->stream))) @@ -1709,6 +1729,7 @@ static NTSTATUS pulse_start(void *args) { stream->started = TRUE; stream->just_started = TRUE; + pulse_add_stream_to_period(stream); } pulse_unlock(); return STATUS_SUCCESS; From 67b1d6cfb0243a150d32b3dfc81c7da855843be5 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 21 Jul 2025 16:55:27 -0600 Subject: [PATCH 305/454] kernelbase: Duplicate GetOverlappedResult() implementation instead of calling GetOverlappedResultEx(). (cherry picked from commit aad255d890f4d525a3a68644c405e8d06304ed0e) --- dlls/kernelbase/file.c | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c index f0dedfe3b14f..5120c2178e9e 100644 --- a/dlls/kernelbase/file.c +++ b/dlls/kernelbase/file.c @@ -3255,7 +3255,37 @@ DWORD WINAPI DECLSPEC_HOTPATCH GetFileType( HANDLE file ) BOOL WINAPI DECLSPEC_HOTPATCH GetOverlappedResult( HANDLE file, LPOVERLAPPED overlapped, LPDWORD result, BOOL wait ) { - return GetOverlappedResultEx( file, overlapped, result, wait ? INFINITE : 0, FALSE ); + NTSTATUS status; + DWORD ret; + + TRACE( "(%p %p %p %d)\n", file, overlapped, result, wait ); + + /* Paired with the write-release in set_async_iosb() in ntdll; see the + * latter for details. */ + status = ReadAcquire( (LONG *)&overlapped->Internal ); + if (status == STATUS_PENDING) + { + if (!wait) + { + SetLastError( ERROR_IO_INCOMPLETE ); + return FALSE; + } + ret = WaitForSingleObject( overlapped->hEvent ? overlapped->hEvent : file, INFINITE ); + if (ret == WAIT_FAILED) return FALSE; + if (ret) + { + SetLastError( ret ); + return FALSE; + } + + /* We don't need to give this load acquire semantics; the wait above + * already guarantees that the IOSB and output buffer are filled. */ + status = overlapped->Internal; + if (status == STATUS_PENDING) status = STATUS_SUCCESS; + } + + *result = overlapped->InternalHigh; + return set_ntstatus( status ); } From aa07baa35a153c9f5e0d1cf51d4aa434b12d0d49 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 21 Jul 2025 17:00:23 -0600 Subject: [PATCH 306/454] kernelbase: Always set last error in GetOverlappedResult[Ex](). (cherry picked from commit 18e65b5732c7b7313192d610a61678a42fe82d35) --- dlls/kernelbase/file.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c index 5120c2178e9e..0df5d1e9ba65 100644 --- a/dlls/kernelbase/file.c +++ b/dlls/kernelbase/file.c @@ -3281,11 +3281,11 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetOverlappedResult( HANDLE file, LPOVERLAPPED ove /* We don't need to give this load acquire semantics; the wait above * already guarantees that the IOSB and output buffer are filled. */ status = overlapped->Internal; - if (status == STATUS_PENDING) status = STATUS_SUCCESS; } *result = overlapped->InternalHigh; - return set_ntstatus( status ); + SetLastError( RtlNtStatusToDosError( status )); + return !status || status == STATUS_PENDING; } @@ -3322,11 +3322,11 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetOverlappedResultEx( HANDLE file, OVERLAPPED *ov /* We don't need to give this load acquire semantics; the wait above * already guarantees that the IOSB and output buffer are filled. */ status = overlapped->Internal; - if (status == STATUS_PENDING) status = STATUS_SUCCESS; } *result = overlapped->InternalHigh; - return set_ntstatus( status ); + SetLastError( RtlNtStatusToDosError( status )); + return !status || status == STATUS_PENDING; } From d66155dc31d375a3714834ef4a0f8f2e7cbfa2f6 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 14 Jul 2025 21:15:42 -0600 Subject: [PATCH 307/454] kernelbase: Wait in GetOverlappedResultEx() even if IOSB status is not pending. (cherry picked from commit 9ce6826226618cbf8eb6eba35d2d66aa6bb091d9) --- dlls/kernel32/tests/file.c | 240 ++++++++++++++++++++++++++++++++++++- dlls/kernelbase/file.c | 33 ++--- 2 files changed, 257 insertions(+), 16 deletions(-) diff --git a/dlls/kernel32/tests/file.c b/dlls/kernel32/tests/file.c index c57edebb3165..f021e940b8ec 100644 --- a/dlls/kernel32/tests/file.c +++ b/dlls/kernel32/tests/file.c @@ -38,6 +38,7 @@ #undef DeleteFile /* needed for FILE_DISPOSITION_INFO */ static HANDLE (WINAPI *pFindFirstFileExA)(LPCSTR,FINDEX_INFO_LEVELS,LPVOID,FINDEX_SEARCH_OPS,LPVOID,DWORD); +static BOOL (WINAPI *pGetOverlappedResultEx)(HANDLE, OVERLAPPED *, DWORD *, DWORD, BOOL); static BOOL (WINAPI *pReplaceFileW)(LPCWSTR, LPCWSTR, LPCWSTR, DWORD, LPVOID, LPVOID); static UINT (WINAPI *pGetSystemWindowsDirectoryA)(LPSTR, UINT); static BOOL (WINAPI *pGetVolumeNameForVolumeMountPointA)(LPCSTR, LPSTR, DWORD); @@ -93,6 +94,7 @@ static void InitFunctionPointers(void) pRtlFreeUnicodeString = (void *)GetProcAddress(hntdll, "RtlFreeUnicodeString"); pFindFirstFileExA=(void*)GetProcAddress(hkernel32, "FindFirstFileExA"); + pGetOverlappedResultEx =(void*)GetProcAddress(hkernel32, "GetOverlappedResultEx"); pReplaceFileW=(void*)GetProcAddress(hkernel32, "ReplaceFileW"); pGetSystemWindowsDirectoryA=(void*)GetProcAddress(hkernel32, "GetSystemWindowsDirectoryA"); pGetVolumeNameForVolumeMountPointA = (void *) GetProcAddress(hkernel32, "GetVolumeNameForVolumeMountPointA"); @@ -3730,8 +3732,93 @@ static void test_OpenFile(void) static void test_overlapped(void) { + static const struct + { + BOOL ex, alertable, wait, queue_apc; + BOOL pass_file_handle, pass_event_handle, set_event; + } + tests[] = + { + { FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE }, + { TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE }, + { TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE }, + { FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE }, + { TRUE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE }, + { TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE }, + { TRUE, TRUE, FALSE, TRUE, FALSE, FALSE, FALSE }, + { TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE }, + { FALSE, FALSE, FALSE, FALSE, TRUE, FALSE, FALSE }, + { TRUE, FALSE, FALSE, FALSE, TRUE, FALSE, FALSE }, + { TRUE, TRUE, FALSE, FALSE, TRUE, FALSE, FALSE }, + { FALSE, FALSE, TRUE, FALSE, TRUE, FALSE, FALSE }, + { TRUE, FALSE, TRUE, FALSE, TRUE, FALSE, FALSE }, + { TRUE, TRUE, TRUE, FALSE, TRUE, FALSE, FALSE }, + { TRUE, TRUE, FALSE, TRUE, TRUE, FALSE, FALSE }, + { TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE }, + { FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, FALSE }, + { TRUE, FALSE, FALSE, FALSE, FALSE, TRUE, FALSE }, + { TRUE, TRUE, FALSE, FALSE, FALSE, TRUE, FALSE }, + { FALSE, FALSE, TRUE, FALSE, FALSE, TRUE, FALSE }, + { TRUE, FALSE, TRUE, FALSE, FALSE, TRUE, FALSE }, + { TRUE, TRUE, TRUE, FALSE, FALSE, TRUE, FALSE }, + { TRUE, TRUE, FALSE, TRUE, FALSE, TRUE, FALSE }, + { TRUE, TRUE, TRUE, TRUE, FALSE, TRUE, FALSE }, + { FALSE, FALSE, FALSE, FALSE, TRUE, TRUE, FALSE }, + { TRUE, FALSE, FALSE, FALSE, TRUE, TRUE, FALSE }, + { TRUE, TRUE, FALSE, FALSE, TRUE, TRUE, FALSE }, + { FALSE, FALSE, TRUE, FALSE, TRUE, TRUE, FALSE }, + { TRUE, FALSE, TRUE, FALSE, TRUE, TRUE, FALSE }, + { TRUE, TRUE, TRUE, FALSE, TRUE, TRUE, FALSE }, + { TRUE, TRUE, FALSE, TRUE, TRUE, TRUE, FALSE }, + { TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, FALSE }, + { FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE }, + { TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE }, + { TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, TRUE }, + { FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, TRUE }, + { TRUE, FALSE, TRUE, FALSE, FALSE, FALSE, TRUE }, + { TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, TRUE }, + { TRUE, TRUE, FALSE, TRUE, FALSE, FALSE, TRUE }, + { TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, TRUE }, + { FALSE, FALSE, FALSE, FALSE, TRUE, FALSE, TRUE }, + { TRUE, FALSE, FALSE, FALSE, TRUE, FALSE, TRUE }, + { TRUE, TRUE, FALSE, FALSE, TRUE, FALSE, TRUE }, + { FALSE, FALSE, TRUE, FALSE, TRUE, FALSE, TRUE }, + { TRUE, FALSE, TRUE, FALSE, TRUE, FALSE, TRUE }, + { TRUE, TRUE, TRUE, FALSE, TRUE, FALSE, TRUE }, + { TRUE, TRUE, FALSE, TRUE, TRUE, FALSE, TRUE }, + { TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, TRUE }, + { FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, TRUE }, + { TRUE, FALSE, FALSE, FALSE, FALSE, TRUE, TRUE }, + { TRUE, TRUE, FALSE, FALSE, FALSE, TRUE, TRUE }, + { FALSE, FALSE, TRUE, FALSE, FALSE, TRUE, TRUE }, + { TRUE, FALSE, TRUE, FALSE, FALSE, TRUE, TRUE }, + { TRUE, TRUE, TRUE, FALSE, FALSE, TRUE, TRUE }, + { TRUE, TRUE, FALSE, TRUE, FALSE, TRUE, TRUE }, + { TRUE, TRUE, TRUE, TRUE, FALSE, TRUE, TRUE }, + { FALSE, FALSE, FALSE, FALSE, TRUE, TRUE, TRUE }, + { TRUE, FALSE, FALSE, FALSE, TRUE, TRUE, TRUE }, + { TRUE, TRUE, FALSE, FALSE, TRUE, TRUE, TRUE }, + { FALSE, FALSE, TRUE, FALSE, TRUE, TRUE, TRUE }, + { TRUE, FALSE, TRUE, FALSE, TRUE, TRUE, TRUE }, + { TRUE, TRUE, TRUE, FALSE, TRUE, TRUE, TRUE }, + { TRUE, TRUE, FALSE, TRUE, TRUE, TRUE, TRUE }, + { TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE }, + }; + static const NTSTATUS test_status[] = + { + STATUS_SUCCESS, STATUS_PENDING, STATUS_UNEXPECTED_IO_ERROR, + }; + static const ULONG_PTR test_file_bits[] = + { + 0, 1, 2, 3, 0xdeadbeef, + }; + OVERLAPPED ov; - DWORD r, result; + DWORD r, result, err; + HANDLE event; + unsigned int i, status_idx, file_bits_idx; + NTSTATUS iosb_status; + ULONG_PTR file_bits, event_bits; /* GetOverlappedResult crashes if the 2nd or 3rd param are NULL */ if (0) /* tested: WinXP */ @@ -3786,10 +3873,13 @@ static void test_overlapped(void) "wrong error %lu\n", GetLastError() ); ok( r == FALSE, "should return false\n"); + SetLastError( 0xdeadbeef ); r = GetOverlappedResult( 0, &ov, &result, TRUE ); ok( r == TRUE, "should return TRUE\n" ); ok( result == 0xabcd, "wrong result %lu\n", result ); ok( ov.Internal == STATUS_PENDING, "expected STATUS_PENDING, got %08Ix\n", ov.Internal ); + err = GetLastError(); + ok( err == ERROR_IO_PENDING || broken( err == 0xdeadbeef ) /* Before Win10 1809 */, "got %lu.\n", GetLastError() ); ResetEvent( ov.hEvent ); @@ -3803,6 +3893,154 @@ static void test_overlapped(void) r = CloseHandle( ov.hEvent ); ok( r == TRUE, "close handle failed\n"); + + if (!pGetOverlappedResultEx) + { + win_skip( "GetOverlappedResultEx is not available, skipping tests.\n" ); + return; + } + + event = CreateEventW( NULL, FALSE, FALSE, NULL ); + + user_apc_ran = FALSE; + QueueUserAPC( user_apc, GetCurrentThread(), 0 ); + SetEvent( event ); + r = WaitForSingleObjectEx( event, INFINITE, TRUE ); + todo_wine ok( r == WAIT_IO_COMPLETION, "got %lu.\n", r ); + if (!r) SleepEx( 0, TRUE ); + ok( user_apc_ran, "APC was not run.\n" ); + + user_apc_ran = FALSE; + SetEvent( event ); + QueueUserAPC( user_apc, GetCurrentThread(), 0 ); + r = WaitForSingleObjectEx( event, 2, TRUE ); + todo_wine ok( r == WAIT_IO_COMPLETION, "got %lu.\n", r ); + if (!r) SleepEx( 0, TRUE ); + ok( user_apc_ran, "APC was not run.\n" ); + + user_apc_ran = FALSE; + SetEvent( event ); + QueueUserAPC( user_apc, GetCurrentThread(), 0 ); + r = WaitForSingleObjectEx( event, 0, TRUE ); + todo_wine ok( r == WAIT_IO_COMPLETION, "got %lu.\n", r ); + if (!r) SleepEx( 0, TRUE ); + ok( user_apc_ran, "APC was not run.\n" ); + + for (event_bits = 0; event_bits < 2; ++event_bits) + for (file_bits_idx = 0; file_bits_idx < ARRAY_SIZE(test_file_bits); ++file_bits_idx) + { + file_bits = test_file_bits[file_bits_idx]; + for (status_idx = 0; status_idx < ARRAY_SIZE(test_status); ++status_idx) + { + iosb_status = test_status[status_idx]; + for (i = 0; i < ARRAY_SIZE(tests); ++i) + { + BOOL will_wait, wait_fails, wait_alerts, wait_timeouts; + DWORD err; + HANDLE file; + + ov.Internal = iosb_status; + ov.InternalHigh = 0xabcd; + ov.hEvent = (tests[i].pass_event_handle ? event : NULL); + file = (tests[i].pass_file_handle ? event : NULL); + ov.hEvent = (HANDLE)((ULONG_PTR)ov.hEvent | event_bits); + file = (HANDLE)((ULONG_PTR)file | file_bits); + will_wait = tests[i].wait + && (iosb_status == STATUS_PENDING || (tests[i].ex && !(file_bits & 1))); + wait_fails = will_wait && WaitForSingleObject( ov.hEvent ? ov.hEvent : file, 0 ) == WAIT_FAILED; + wait_alerts = will_wait && tests[i].ex && tests[i].alertable && tests[i].queue_apc; + wait_timeouts = will_wait && !wait_fails && !wait_alerts && !tests[i].set_event && !(tests[i].ex && file_bits & 1); + if (will_wait && !tests[i].ex && wait_timeouts) + { + /* This would wait forever. */ + continue; + } + + winetest_push_context( "status %#lx, file_bits %Iu, event_bits %Iu, test %u", + iosb_status, file_bits, event_bits, i ); + + if (tests[i].set_event) + SetEvent( event ); + else + ResetEvent( event ); + + if (tests[i].queue_apc) + QueueUserAPC( user_apc, GetCurrentThread(), 0 ); + + result = 0xdeadbeef; + SetLastError( 0xdeadbeef ); + if (tests[i].ex) + r = pGetOverlappedResultEx( file, &ov, &result, tests[i].wait ? 2 : 0, tests[i].alertable ); + else + r = GetOverlappedResult( file, &ov, &result, tests[i].wait ); + err = GetLastError(); + if (will_wait) + { + if (wait_fails) + { + ok( !r, "got %lu.\n", r ); + ok( err == ERROR_INVALID_HANDLE, "got %lu.\n", err ); + ok( result == 0xdeadbeef, "wrong result %lu\n", result ); + } + else if (wait_alerts) + { + /* todo comes from WaitForSingleObjectEx() with signaled event and queued APC not preferring + * user APC (which is tested above for clarity) */ + todo_wine_if(tests[i].set_event) ok( err == WAIT_IO_COMPLETION, "got %lu.\n", err ); + if (err == WAIT_IO_COMPLETION) + { + ok( !r, "got %lu.\n", r ); + ok( result == 0xdeadbeef, "wrong result %lu\n", result ); + } + } + else if (wait_timeouts) + { + ok( !r, "got %lu.\n", r ); + ok( err == WAIT_TIMEOUT, "got %lu.\n", err ); + ok( result == 0xdeadbeef, "wrong result %lu\n", result ); + } + else + { + ok( r == (iosb_status == STATUS_SUCCESS || iosb_status == STATUS_PENDING), + "got %lu.\n", r ); + ok( err == RtlNtStatusToDosError( iosb_status ) || broken( r && err == 0xdeadbeef ) /* Before Win10 1809 */, + "got %lu.\n", err ); + ok( result == 0xabcd, "wrong result %lu\n", result ); + } + } + else if (iosb_status == STATUS_PENDING) + { + ok( !r, "got %lu.\n", r ); + ok( err == ERROR_IO_INCOMPLETE, "got %lu.\n", err ); + ok( result == 0xdeadbeef, "wrong result %lu\n", result ); + } + else + { + ok( r == (iosb_status == STATUS_SUCCESS || iosb_status == STATUS_PENDING), + "got %lu.\n", r ); + ok( err == RtlNtStatusToDosError( iosb_status ) || broken( r && err == 0xdeadbeef ) /* Before Win10 1809 */, + "got %lu.\n", err ); + ok( result == 0xabcd, "wrong result %lu\n", result ); + } + + r = WaitForSingleObject( event, 0 ); + if (!tests[i].set_event || (will_wait && !wait_fails && !wait_alerts)) + { + ok( r == WAIT_TIMEOUT, "got %#lx.\n", r ); + } + else + { + todo_wine_if(will_wait && !wait_fails && wait_alerts && tests[i].set_event) + ok( r == WAIT_OBJECT_0, "got %#lx.\n", r ); + } + + winetest_pop_context(); + if (tests[i].queue_apc) + SleepEx( 0, TRUE ); + } + } + } + CloseHandle( event ); } static void test_RemoveDirectory(void) diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c index 0df5d1e9ba65..c7d8119baf74 100644 --- a/dlls/kernelbase/file.c +++ b/dlls/kernelbase/file.c @@ -3295,35 +3295,38 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetOverlappedResult( HANDLE file, LPOVERLAPPED ove BOOL WINAPI DECLSPEC_HOTPATCH GetOverlappedResultEx( HANDLE file, OVERLAPPED *overlapped, DWORD *result, DWORD timeout, BOOL alertable ) { - NTSTATUS status; + NTSTATUS status = STATUS_PENDING; + BOOL compat_mode; DWORD ret; TRACE( "(%p %p %p %lu %d)\n", file, overlapped, result, timeout, alertable ); - /* Paired with the write-release in set_async_iosb() in ntdll; see the - * latter for details. */ - status = ReadAcquire( (LONG *)&overlapped->Internal ); - if (status == STATUS_PENDING) + compat_mode = (ULONG_PTR)file & 1; + if (compat_mode || !timeout) + { + /* Paired with the write-release in set_async_iosb() in ntdll; see the + * latter for details. */ + status = ReadAcquire( (LONG *)&overlapped->Internal ); + } + + if (timeout && status == STATUS_PENDING) { - if (!timeout) - { - SetLastError( ERROR_IO_INCOMPLETE ); - return FALSE; - } ret = WaitForSingleObjectEx( overlapped->hEvent ? overlapped->hEvent : file, timeout, alertable ); - if (ret == WAIT_FAILED) - return FALSE; - else if (ret) + if (ret == WAIT_FAILED) return FALSE; + if (ret && !(compat_mode && ret == WAIT_TIMEOUT)) { SetLastError( ret ); return FALSE; } - /* We don't need to give this load acquire semantics; the wait above * already guarantees that the IOSB and output buffer are filled. */ status = overlapped->Internal; } - + else if (status == STATUS_PENDING) + { + SetLastError( ERROR_IO_INCOMPLETE ); + return FALSE; + } *result = overlapped->InternalHigh; SetLastError( RtlNtStatusToDosError( status )); return !status || status == STATUS_PENDING; From fa3ada5ef3e20009440a52fb74aecdba16aaa5da Mon Sep 17 00:00:00 2001 From: Conor McCarthy Date: Fri, 25 Jul 2025 14:13:03 +1000 Subject: [PATCH 308/454] winedmo: Error out if avformat_open_input() fails. This was the case before the call to mediaconv_demuxer_open() was added, but now if mediaconv_demuxer_open() succeeds, failure of avformat_open_input() will be ignored. CW-Bug-Id: #25682 --- dlls/winedmo/unix_demuxer.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/dlls/winedmo/unix_demuxer.c b/dlls/winedmo/unix_demuxer.c index 24681e2d5df5..a9bd0adef9e5 100644 --- a/dlls/winedmo/unix_demuxer.c +++ b/dlls/winedmo/unix_demuxer.c @@ -247,9 +247,8 @@ NTSTATUS demuxer_create( void *arg ) if (!(demuxer->ctx = avformat_alloc_context())) goto failed; if (!(demuxer->ctx->pb = avio_alloc_context( NULL, 0, 0, params->context, unix_read_callback, NULL, unix_seek_callback ))) goto failed; - if ((ret = avformat_open_input( &demuxer->ctx, NULL, NULL, NULL )) < 0) - WARN( "Failed to open input, error %s.\n", debugstr_averr(ret) ); - if ((ret = mediaconv_demuxer_open( &demuxer->ctx, params->context ) < 0)) + ret = avformat_open_input( &demuxer->ctx, NULL, NULL, NULL ); + if (ret < 0 || (ret = mediaconv_demuxer_open( &demuxer->ctx, params->context )) < 0) { ERR( "Failed to open input, error %s.\n", debugstr_averr(ret) ); goto failed; From edbc824284fb43ead521c5859a449bca0bd99f13 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 25 Jul 2025 16:25:42 -0600 Subject: [PATCH 309/454] ntdll: fsync: HACK: Try to help simulated pulse event for God Eater Resurrection. CW-Bug-Id: #25721 --- dlls/ntdll/unix/fsync.c | 7 +++++++ dlls/ntdll/unix/loader.c | 8 ++++++++ dlls/ntdll/unix/unix_private.h | 1 + 3 files changed, 16 insertions(+) diff --git a/dlls/ntdll/unix/fsync.c b/dlls/ntdll/unix/fsync.c index c72f47866705..4d9f5cde7b21 100644 --- a/dlls/ntdll/unix/fsync.c +++ b/dlls/ntdll/unix/fsync.c @@ -720,6 +720,13 @@ NTSTATUS fsync_reset_event( HANDLE handle, LONG *prev ) return STATUS_OBJECT_TYPE_MISMATCH; } + if (fsync_help_simulated_pulse && event->signaled + && __atomic_load_n( &event->last_pid, __ATOMIC_SEQ_CST ) == current_pid) + { + TRACE( "event %p, helping simulated pulse.\n", handle ); + usleep( 0 ); + } + current = __atomic_exchange_n( &event->signaled, 0, __ATOMIC_SEQ_CST ); if (prev) *prev = current; diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c index f339dc64f9b7..e6969571e9e2 100644 --- a/dlls/ntdll/unix/loader.c +++ b/dlls/ntdll/unix/loader.c @@ -2128,6 +2128,7 @@ BOOL ac_odyssey; BOOL fsync_simulate_sched_quantum; BOOL alert_simulate_sched_quantum; BOOL fsync_yield_to_waiters; +BOOL fsync_help_simulated_pulse; BOOL localsystem_sid; BOOL simulate_writecopy; BOOL wine_allocs_2g_limit; @@ -2189,6 +2190,13 @@ static void hacks_init(void) if (fsync_yield_to_waiters) ERR("HACK: fsync: yield to waiters.\n"); + env_str = getenv("WINE_FSYNC_HELP_SIMULATED_PULSE"); + if (env_str) + fsync_help_simulated_pulse = !!atoi(env_str); + else if (sgi) fsync_help_simulated_pulse = !strcmp(sgi, "460870"); + if (fsync_help_simulated_pulse) + ERR("HACK: fsync: helping simulated pulse event.\n"); + switch (sgi ? atoi( sgi ) : -1) { case 25700: /* Madballs in Babo: Invasion */ diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h index cd070e0b98fb..a2163d4e19f3 100644 --- a/dlls/ntdll/unix/unix_private.h +++ b/dlls/ntdll/unix/unix_private.h @@ -196,6 +196,7 @@ extern BOOL ac_odyssey; extern BOOL fsync_simulate_sched_quantum; extern BOOL alert_simulate_sched_quantum; extern BOOL fsync_yield_to_waiters; +extern BOOL fsync_help_simulated_pulse; extern BOOL localsystem_sid; extern BOOL simulate_writecopy; extern long long ram_reporting_bias; From 4af56fc1bacfabc3d085839596bb47be4812e625 Mon Sep 17 00:00:00 2001 From: Conor McCarthy Date: Tue, 29 Jul 2025 17:46:05 +1000 Subject: [PATCH 310/454] Revert "winedmo: Error out if avformat_open_input() fails." This reverts commit 3676ba1f0f1bbf0a9b595cc3c75fc7ab99843483. Some games, for example Resident Evil Village, depend upon execution continuing after this failure. --- dlls/winedmo/unix_demuxer.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/dlls/winedmo/unix_demuxer.c b/dlls/winedmo/unix_demuxer.c index a9bd0adef9e5..24681e2d5df5 100644 --- a/dlls/winedmo/unix_demuxer.c +++ b/dlls/winedmo/unix_demuxer.c @@ -247,8 +247,9 @@ NTSTATUS demuxer_create( void *arg ) if (!(demuxer->ctx = avformat_alloc_context())) goto failed; if (!(demuxer->ctx->pb = avio_alloc_context( NULL, 0, 0, params->context, unix_read_callback, NULL, unix_seek_callback ))) goto failed; - ret = avformat_open_input( &demuxer->ctx, NULL, NULL, NULL ); - if (ret < 0 || (ret = mediaconv_demuxer_open( &demuxer->ctx, params->context )) < 0) + if ((ret = avformat_open_input( &demuxer->ctx, NULL, NULL, NULL )) < 0) + WARN( "Failed to open input, error %s.\n", debugstr_averr(ret) ); + if ((ret = mediaconv_demuxer_open( &demuxer->ctx, params->context ) < 0)) { ERR( "Failed to open input, error %s.\n", debugstr_averr(ret) ); goto failed; From fe5d7b51da0e45e0c012a42271400580e77bf804 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 30 Jul 2025 09:53:31 -0600 Subject: [PATCH 311/454] shell32: Add AccountPictures known folder. CW-Bug-Id: #25738 --- dlls/shell32/shellpath.c | 17 ++++++++++++++++- dlls/shell32/tests/shellpath.c | 9 +++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/dlls/shell32/shellpath.c b/dlls/shell32/shellpath.c index 07295713cb32..c11a1a4c2223 100644 --- a/dlls/shell32/shellpath.c +++ b/dlls/shell32/shellpath.c @@ -2079,7 +2079,17 @@ static const CSIDL_DATA CSIDL_Data[] = .name = L"VideosLibrary", .path = L"Videos.library-ms", .parsing = L"::{031E4825-7B94-4dc3-B131-E946B44C8DD5}\\{491E922F-5643-4af4-A7EB-4E7A138D8174}", - } + }, + { /* 0x73 */ + .id = &FOLDERID_AccountPictures, + .type = CSIDL_Type_User, + .category = KF_CATEGORY_PERUSER, + .name = L"AccountPictures", + .parent = &FOLDERID_RoamingAppData, + .path = L"Microsoft\\Windows\\AccountPictures", + .attributes = FILE_ATTRIBUTE_READONLY, + .flags = KFDF_PRECREATE | KFDF_ROAMABLE, + }, }; static int csidl_from_id( const KNOWNFOLDERID *id ) @@ -3123,6 +3133,11 @@ static HRESULT create_extra_folders(void) hr = SHGetFolderPathAndSubDirW(0, CSIDL_APPDATA | CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_DEFAULT, L"Microsoft\\Windows\\Themes", path); } + if (SUCCEEDED(hr)) + { + hr = SHGetFolderPathAndSubDirW(0, CSIDL_APPDATA | CSIDL_FLAG_CREATE, NULL, + SHGFP_TYPE_DEFAULT, L"Microsoft\\Windows\\AccountPictures", path); + } /* Proton HACK: In older Proton versions, duplicate Stuff directories were diff --git a/dlls/shell32/tests/shellpath.c b/dlls/shell32/tests/shellpath.c index 2d9450652a11..0796e04510cd 100644 --- a/dlls/shell32/tests/shellpath.c +++ b/dlls/shell32/tests/shellpath.c @@ -1314,6 +1314,15 @@ static const struct knownFolderDef known_folders[] = { NULL, 0, 0), + KNOWN_FOLDER(FOLDERID_AccountPictures, + NO_CSIDL, + "AccountPictures", + KF_CATEGORY_PERUSER, + FOLDERID_RoamingAppData, GUID_NULL, + "Microsoft\\Windows\\AccountPictures", + NULL, + FILE_ATTRIBUTE_READONLY, + KFDF_PRECREATE | KFDF_ROAMABLE), }; #undef KNOWN_FOLDER BOOL known_folder_found[ARRAY_SIZE(known_folders)]; From 39911fd55955486636f55e6e5ad8c601135e808b Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 30 Jul 2025 14:24:15 -0600 Subject: [PATCH 312/454] opengl32: Map glCompressedTexImage2DARB to glCompressedTexImage2D if ARB_texture_compression is missing. CW-Bug-Id: #25746 --- dlls/opengl32/unix_wgl.c | 1 + 1 file changed, 1 insertion(+) diff --git a/dlls/opengl32/unix_wgl.c b/dlls/opengl32/unix_wgl.c index 5ebe8ce9a382..725b6a869442 100644 --- a/dlls/opengl32/unix_wgl.c +++ b/dlls/opengl32/unix_wgl.c @@ -624,6 +624,7 @@ static PROC wrap_wglGetProcAddress( TEB *teb, LPCSTR name ) { { "glCopyTexSubImage3DEXT", "glCopyTexSubImage3D" }, /* needed by RuneScape */ { "glVertexAttribDivisor", "glVertexAttribDivisorARB"}, /* needed by Caffeine */ + { "glCompressedTexImage2DARB", "glCompressedTexImage2D" }, /* needed by Grim Fandango Remastered */ }; for (i = 0; i < ARRAY_SIZE(alternatives); i++) From 397988e2088a31cd13c8c6603cf50094fc3e3863 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 30 Jul 2025 11:58:09 -0600 Subject: [PATCH 313/454] fixup! msi: HACK: Report older Windows version for Halo MCC redists. CW-Bug-Id: #25520 --- dlls/msi/package.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/dlls/msi/package.c b/dlls/msi/package.c index f9d9e54447aa..9a65c3e55bb5 100644 --- a/dlls/msi/package.c +++ b/dlls/msi/package.c @@ -628,9 +628,10 @@ static void fixup_winver(DWORD *verval) { const char *s; - cached = (s = getenv("STEAM_COMPAT_APP_ID")) - && !strcmp(s, "976730") - ; + cached = (s = getenv("STEAM_COMPAT_APP_ID")) && + ( + !strcmp(s, "976730") + ); if (cached) ERR("HACK: setting winver 502.\n"); } From d4b12bf9b54d4687558dbdacb255b8f523a0d202 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 30 Jul 2025 12:05:23 -0600 Subject: [PATCH 314/454] msi: HACK: Report older Windows version for Company of Heroes 2 redists. CW-Bug-Id: #25520 --- dlls/msi/package.c | 1 + 1 file changed, 1 insertion(+) diff --git a/dlls/msi/package.c b/dlls/msi/package.c index 9a65c3e55bb5..6c41df2c34f0 100644 --- a/dlls/msi/package.c +++ b/dlls/msi/package.c @@ -631,6 +631,7 @@ static void fixup_winver(DWORD *verval) cached = (s = getenv("STEAM_COMPAT_APP_ID")) && ( !strcmp(s, "976730") + || !strcmp(s, "231430") ); if (cached) ERR("HACK: setting winver 502.\n"); From a64330625be1db253a12d02b3cb3ca4f209bb9f0 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 30 Jul 2025 12:06:03 -0600 Subject: [PATCH 315/454] msi: HACK: Report older Windows version for Age of Empires: Definitive Edition redists. CW-Bug-Id: #25520 --- dlls/msi/package.c | 1 + 1 file changed, 1 insertion(+) diff --git a/dlls/msi/package.c b/dlls/msi/package.c index 6c41df2c34f0..e95fda4c27ff 100644 --- a/dlls/msi/package.c +++ b/dlls/msi/package.c @@ -632,6 +632,7 @@ static void fixup_winver(DWORD *verval) ( !strcmp(s, "976730") || !strcmp(s, "231430") + || !strcmp(s, "1017900") ); if (cached) ERR("HACK: setting winver 502.\n"); From 4fbbbb6f209f5a99463cba8f313d958cd7cf292d Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 30 Jul 2025 12:07:10 -0600 Subject: [PATCH 316/454] msi: HACK: Report older Windows version for Warhammer 40,000: Dawn of War III redists. CW-Bug-Id: #25520 --- dlls/msi/package.c | 1 + 1 file changed, 1 insertion(+) diff --git a/dlls/msi/package.c b/dlls/msi/package.c index e95fda4c27ff..f0302ba9fc47 100644 --- a/dlls/msi/package.c +++ b/dlls/msi/package.c @@ -633,6 +633,7 @@ static void fixup_winver(DWORD *verval) !strcmp(s, "976730") || !strcmp(s, "231430") || !strcmp(s, "1017900") + || !strcmp(s, "285190") ); if (cached) ERR("HACK: setting winver 502.\n"); From 3ab120852a7ce0e2a8fa0ff5b9d778a4a2bbd203 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 30 Jul 2025 12:07:52 -0600 Subject: [PATCH 317/454] msi: HACK: Report older Windows version for Injustice 2 redists. CW-Bug-Id: #25520 --- dlls/msi/package.c | 1 + 1 file changed, 1 insertion(+) diff --git a/dlls/msi/package.c b/dlls/msi/package.c index f0302ba9fc47..b81e6913ddce 100644 --- a/dlls/msi/package.c +++ b/dlls/msi/package.c @@ -634,6 +634,7 @@ static void fixup_winver(DWORD *verval) || !strcmp(s, "231430") || !strcmp(s, "1017900") || !strcmp(s, "285190") + || !strcmp(s, "627270") ); if (cached) ERR("HACK: setting winver 502.\n"); From c96a18b0739e25313771db4358f4b2290bc16c09 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 1 Aug 2025 12:53:48 -0600 Subject: [PATCH 318/454] fixup! HACK: winex11, winevulkan: Support faking GPU PCI IDs. Fake PCI ID on the Unix side so WINE_HIDE_ env vars are there when those got stripped on the PE side. CW-Bug-Id: #25745 --- dlls/winevulkan/loader.c | 42 --------------------------- dlls/winevulkan/make_vulkan | 8 ++++- dlls/winevulkan/vulkan.c | 58 +++++++++++++++++++++++++++++++++++++ 3 files changed, 65 insertions(+), 43 deletions(-) diff --git a/dlls/winevulkan/loader.c b/dlls/winevulkan/loader.c index 9d42ae7539ce..644e0982e7a0 100644 --- a/dlls/winevulkan/loader.c +++ b/dlls/winevulkan/loader.c @@ -531,46 +531,6 @@ static void fill_luid_property(VkPhysicalDeviceProperties2 *properties2) device_node_mask); } -static void fixup_device_id(UINT *vendor_id, UINT *device_id) -{ - const char *sgi; - - if (*vendor_id == 0x10de /* NVIDIA */ && (sgi = getenv("WINE_HIDE_NVIDIA_GPU")) && *sgi != '0') - { - *vendor_id = 0x1002; /* AMD */ - *device_id = 0x73df; /* RX 6700XT */ - } - else if (*vendor_id == 0x1002 /* AMD */ && (sgi = getenv("WINE_HIDE_AMD_GPU")) && *sgi != '0') - { - *vendor_id = 0x10de; /* NVIDIA */ - *device_id = 0x2487; /* RTX 3060 */ - } - else if (*vendor_id == 0x1002 && (*device_id == 0x163f || *device_id == 0x1435) && (sgi = getenv("WINE_HIDE_VANGOGH_GPU")) && *sgi != '0') - { - *device_id = 0x687f; /* Radeon RX Vega 56/64 */ - } - else if (*vendor_id == 0x8086 /* Intel */ && (sgi = getenv("WINE_HIDE_INTEL_GPU")) && *sgi != '0') - { - *vendor_id = 0x1002; /* AMD */ - *device_id = 0x73df; /* RX 6700XT */ - } -} - -void WINAPI vkGetPhysicalDeviceProperties(VkPhysicalDevice physical_device, - VkPhysicalDeviceProperties *properties) -{ - struct vkGetPhysicalDeviceProperties_params params; - NTSTATUS status; - - TRACE("%p, %p\n", physical_device, properties); - - params.physicalDevice = physical_device; - params.pProperties = properties; - status = UNIX_CALL(vkGetPhysicalDeviceProperties, ¶ms); - assert(!status); - fixup_device_id(&properties->vendorID, &properties->deviceID); -} - void WINAPI vkGetPhysicalDeviceProperties2(VkPhysicalDevice phys_dev, VkPhysicalDeviceProperties2 *properties2) { @@ -584,7 +544,6 @@ void WINAPI vkGetPhysicalDeviceProperties2(VkPhysicalDevice phys_dev, status = UNIX_CALL(vkGetPhysicalDeviceProperties2, ¶ms); assert(!status); fill_luid_property(properties2); - fixup_device_id(&properties2->properties.vendorID, &properties2->properties.deviceID); } void WINAPI vkGetPhysicalDeviceProperties2KHR(VkPhysicalDevice phys_dev, @@ -600,7 +559,6 @@ void WINAPI vkGetPhysicalDeviceProperties2KHR(VkPhysicalDevice phys_dev, status = UNIX_CALL(vkGetPhysicalDeviceProperties2KHR, ¶ms); assert(!status); fill_luid_property(properties2); - fixup_device_id(&properties2->properties.vendorID, &properties2->properties.deviceID); } VkResult WINAPI vkCreateDevice(VkPhysicalDevice phys_dev, const VkDeviceCreateInfo *create_info, diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan index d84d6c400534..ca690079130b 100755 --- a/dlls/winevulkan/make_vulkan +++ b/dlls/winevulkan/make_vulkan @@ -174,6 +174,10 @@ FUNCTION_OVERRIDES = { "vkGetPhysicalDeviceExternalFenceProperties" : {"dispatch" : False}, "vkGetPhysicalDeviceExternalSemaphoreProperties" : {"dispatch" : True}, + "vkGetPhysicalDeviceProperties" : {"dispatch" : True}, + "vkGetPhysicalDeviceProperties2" : {"dispatch" : True}, + "vkGetPhysicalDeviceProperties2KHR" : {"dispatch" : True}, + # Device functions "vkCreateCommandPool" : {"extra_param" : "client_ptr"}, "vkGetDeviceProcAddr" : {"dispatch" : False}, @@ -286,6 +290,9 @@ MANUAL_UNIX_THUNKS = { "vkGetPhysicalDeviceExternalSemaphorePropertiesKHR", "vkGetPhysicalDeviceImageFormatProperties2", "vkGetPhysicalDeviceImageFormatProperties2KHR", + "vkGetPhysicalDeviceProperties", + "vkGetPhysicalDeviceProperties2", + "vkGetPhysicalDeviceProperties2KHR", "vkMapMemory", "vkMapMemory2KHR", "vkUnmapMemory", @@ -325,7 +332,6 @@ MANUAL_LOADER_THUNKS = { "vkEnumerateInstanceExtensionProperties", "vkEnumerateInstanceVersion", "vkFreeCommandBuffers", - "vkGetPhysicalDeviceProperties", "vkGetPhysicalDeviceProperties2", "vkGetPhysicalDeviceProperties2KHR", } diff --git a/dlls/winevulkan/vulkan.c b/dlls/winevulkan/vulkan.c index 3ade38428c3b..51b59bfabb77 100644 --- a/dlls/winevulkan/vulkan.c +++ b/dlls/winevulkan/vulkan.c @@ -4710,6 +4710,64 @@ VkResult wine_wine_vkReleaseKeyedMutex(VkDevice device, VkDeviceMemory memory, u return release_keyed_mutex(vulkan_device_from_handle(device), wine_device_memory_from_handle(memory), key, NULL); } +static void fixup_device_id(UINT *vendor_id, UINT *device_id) +{ + const char *sgi; + + if (*vendor_id == 0x10de /* NVIDIA */ && (sgi = getenv("WINE_HIDE_NVIDIA_GPU")) && *sgi != '0') + { + *vendor_id = 0x1002; /* AMD */ + *device_id = 0x73df; /* RX 6700XT */ + } + else if (*vendor_id == 0x1002 /* AMD */ && (sgi = getenv("WINE_HIDE_AMD_GPU")) && *sgi != '0') + { + *vendor_id = 0x10de; /* NVIDIA */ + *device_id = 0x2487; /* RTX 3060 */ + } + else if (*vendor_id == 0x1002 && (*device_id == 0x163f || *device_id == 0x1435) && (sgi = getenv("WINE_HIDE_VANGOGH_GPU")) && *sgi != '0') + { + *device_id = 0x687f; /* Radeon RX Vega 56/64 */ + } + else if (*vendor_id == 0x8086 /* Intel */ && (sgi = getenv("WINE_HIDE_INTEL_GPU")) && *sgi != '0') + { + *vendor_id = 0x1002; /* AMD */ + *device_id = 0x73df; /* RX 6700XT */ + } +} + +void wine_vkGetPhysicalDeviceProperties(VkPhysicalDevice client_physical_device, + VkPhysicalDeviceProperties *properties) +{ + struct wine_phys_dev *phys_dev = wine_phys_dev_from_handle(client_physical_device); + + TRACE("%p, %p\n", phys_dev, properties); + + phys_dev->obj.instance->p_vkGetPhysicalDeviceProperties(phys_dev->obj.host.physical_device, properties); + fixup_device_id(&properties->vendorID, &properties->deviceID); +} + +void wine_vkGetPhysicalDeviceProperties2(VkPhysicalDevice client_physical_device, + VkPhysicalDeviceProperties2 *properties) +{ + struct wine_phys_dev *phys_dev = wine_phys_dev_from_handle(client_physical_device); + + TRACE("%p, %p\n", phys_dev, properties); + + phys_dev->obj.instance->p_vkGetPhysicalDeviceProperties2(phys_dev->obj.host.physical_device, properties); + fixup_device_id(&properties->properties.vendorID, &properties->properties.deviceID); +} + +void wine_vkGetPhysicalDeviceProperties2KHR(VkPhysicalDevice client_physical_device, + VkPhysicalDeviceProperties2 *properties) +{ + struct wine_phys_dev *phys_dev = wine_phys_dev_from_handle(client_physical_device); + + TRACE("%p, %p\n", phys_dev, properties); + + phys_dev->obj.instance->p_vkGetPhysicalDeviceProperties2KHR(phys_dev->obj.host.physical_device, properties); + fixup_device_id(&properties->properties.vendorID, &properties->properties.deviceID); +} + DECLSPEC_EXPORT VkDevice __wine_get_native_VkDevice(VkDevice handle) { struct vulkan_device *device = vulkan_device_from_handle(handle); From ffd82d658fdb600d5d8090f96609ea602ac06e85 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 1 Aug 2025 13:45:53 -0600 Subject: [PATCH 319/454] wineboot: Preserve dxvk-specific NVAPI enablement variables. CW-Bug-Id: #25745 --- programs/wineboot/wineboot.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/programs/wineboot/wineboot.c b/programs/wineboot/wineboot.c index a18bd197b2c8..636b6d738bb9 100644 --- a/programs/wineboot/wineboot.c +++ b/programs/wineboot/wineboot.c @@ -1052,6 +1052,14 @@ static void create_computer_name_keys(void) static void create_volatile_environment_registry_key(void) { + static const WCHAR *preserve[] = + { + L"DXVK_ENABLE_NVAPI", + L"DXVK_NVAPI_ALLOW_OTHER_DRIVERS", + L"DXVK_NVAPI_DRIVER_VERSION", + }; + const WCHAR *str; + unsigned int i; WCHAR path[MAX_PATH]; WCHAR computername[MAX_COMPUTERNAME_LENGTH + 1 + 2]; DWORD size; @@ -1096,6 +1104,12 @@ static void create_volatile_environment_registry_key(void) } set_reg_value( hkey, L"SESSIONNAME", L"Console" ); + + for (i = 0; i < ARRAY_SIZE(preserve); ++i) + { + if ((str = _wgetenv( preserve[i] ))) set_reg_value( hkey, preserve[i], str ); + } + RegCloseKey( hkey ); } From 84b2e4d16d781be7e38ce3a308ebd62a1d3d808f Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 5 Aug 2025 16:22:40 -0600 Subject: [PATCH 320/454] belauncher: Handle install command. CW-Bug-Id: #25758 --- programs/belauncher/main.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/programs/belauncher/main.c b/programs/belauncher/main.c index 850c69f76132..d48169cbe6cb 100644 --- a/programs/belauncher/main.c +++ b/programs/belauncher/main.c @@ -94,6 +94,11 @@ int WINAPI wWinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPWSTR cmdline, int cm WINE_TRACE("uninstall cmd, exiting.\n"); return 0; } + if (argc && iswdigit(argvW[0][0]) && _wtoi(argvW[0]) == 1) + { + WINE_ERR("install cmd, exiting.\n"); + return 0; + } for (i = 0; i < argc; ++i) { From a237cb6221dd7d031f6e5d49a5465176078a4543 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 6 Aug 2025 10:29:23 -0600 Subject: [PATCH 321/454] bcrypt: Factor out len_from_bitlen() function. CW-Bug-Id: #25779 --- dlls/bcrypt/bcrypt_internal.h | 5 +++++ dlls/bcrypt/bcrypt_main.c | 8 ++++---- dlls/bcrypt/gnutls.c | 18 +++++++++--------- 3 files changed, 18 insertions(+), 13 deletions(-) diff --git a/dlls/bcrypt/bcrypt_internal.h b/dlls/bcrypt/bcrypt_internal.h index dd80869b4c12..d2a5b8022971 100644 --- a/dlls/bcrypt/bcrypt_internal.h +++ b/dlls/bcrypt/bcrypt_internal.h @@ -278,4 +278,9 @@ enum key_funcs unix_funcs_count, }; +static inline ULONG len_from_bitlen( ULONG bitlen ) +{ + return bitlen / 8; +} + #endif /* __BCRYPT_INTERNAL_H */ diff --git a/dlls/bcrypt/bcrypt_main.c b/dlls/bcrypt/bcrypt_main.c index 9c7e052196ff..568699c03024 100644 --- a/dlls/bcrypt/bcrypt_main.c +++ b/dlls/bcrypt/bcrypt_main.c @@ -809,7 +809,7 @@ static NTSTATUS set_key_property( struct key *key, const WCHAR *prop, UCHAR *val if (key->u.a.flags & KEY_FLAG_FINALIZED) return STATUS_INVALID_HANDLE; if (key->alg_id != ALG_ID_DH || size < sizeof(*hdr) || hdr->cbLength != size || - hdr->dwMagic != BCRYPT_DH_PARAMETERS_MAGIC || hdr->cbKeyLength != key->u.a.bitlen / 8) + hdr->dwMagic != BCRYPT_DH_PARAMETERS_MAGIC || hdr->cbKeyLength != len_from_bitlen( key->u.a.bitlen )) return STATUS_INVALID_PARAMETER; params.key = key; @@ -1921,7 +1921,7 @@ static NTSTATUS key_import_pair( struct algorithm *alg, const WCHAR *type, BCRYP pubkey = (DSSPUBKEY *)(hdr + 1); if (pubkey->magic != MAGIC_DSS2) return STATUS_NOT_SUPPORTED; - if (input_len < sizeof(*hdr) + sizeof(*pubkey) + (pubkey->bitlen / 8) * 2 + 40 + sizeof(DSSSEED)) + if (input_len < sizeof(*hdr) + sizeof(*pubkey) + len_from_bitlen( pubkey->bitlen ) * 2 + 40 + sizeof(DSSSEED)) return STATUS_INVALID_PARAMETER; if ((status = key_asymmetric_create( alg->id, pubkey->bitlen, &key ))) return status; @@ -1954,7 +1954,7 @@ static NTSTATUS key_import_pair( struct algorithm *alg, const WCHAR *type, BCRYP pubkey = (DSSPUBKEY *)(hdr + 1); if (pubkey->magic != MAGIC_DSS1) return STATUS_NOT_SUPPORTED; - size = sizeof(*hdr) + sizeof(*pubkey) + (pubkey->bitlen / 8) * 3 + 20 + sizeof(DSSSEED); + size = sizeof(*hdr) + sizeof(*pubkey) + len_from_bitlen( pubkey->bitlen ) * 3 + 20 + sizeof(DSSSEED); if (input_len < size) return STATUS_INVALID_PARAMETER; if ((status = key_asymmetric_create( alg->id, pubkey->bitlen, &key ))) return status; @@ -2621,7 +2621,7 @@ static NTSTATUS derive_key_hash( struct secret *secret, BCryptBufferDesc *desc, ULONG *ret_len ) { struct key_asymmetric_derive_key_params params; - ULONG hash_len, derived_key_len = secret->privkey->u.a.bitlen / 8; + ULONG hash_len, derived_key_len = len_from_bitlen( secret->privkey->u.a.bitlen ); UCHAR hash_buf[MAX_HASH_OUTPUT_BYTES]; struct algorithm *alg = NULL; UCHAR *derived_key; diff --git a/dlls/bcrypt/gnutls.c b/dlls/bcrypt/gnutls.c index 486513182890..3ba8ba95643c 100644 --- a/dlls/bcrypt/gnutls.c +++ b/dlls/bcrypt/gnutls.c @@ -997,7 +997,7 @@ static NTSTATUS key_export_rsa_public( struct key *key, UCHAR *buf, ULONG len, U { BCRYPT_RSAKEY_BLOB *rsa_blob = (BCRYPT_RSAKEY_BLOB *)buf; gnutls_datum_t m, e; - ULONG size = key->u.a.bitlen / 8; + ULONG size = len_from_bitlen( key->u.a.bitlen ); UCHAR *dst; int ret; @@ -1104,7 +1104,7 @@ static NTSTATUS key_export_dsa_public( struct key *key, UCHAR *buf, ULONG len, U { BCRYPT_DSA_KEY_BLOB *dsa_blob = (BCRYPT_DSA_KEY_BLOB *)buf; gnutls_datum_t p, q, g, y; - ULONG size = key->u.a.bitlen / 8; + ULONG size = len_from_bitlen( key->u.a.bitlen ); NTSTATUS status = STATUS_SUCCESS; UCHAR *dst; int ret; @@ -1173,7 +1173,7 @@ static NTSTATUS key_export_dsa_capi_public( struct key *key, UCHAR *buf, ULONG l BLOBHEADER *hdr = (BLOBHEADER *)buf; DSSPUBKEY *dsskey; gnutls_datum_t p, q, g, y; - ULONG size = key->u.a.bitlen / 8; + ULONG size = len_from_bitlen( key->u.a.bitlen ); NTSTATUS status = STATUS_SUCCESS; UCHAR *dst; int ret; @@ -1681,7 +1681,7 @@ static NTSTATUS key_export_rsa( struct key *key, ULONG flags, UCHAR *buf, ULONG { BCRYPT_RSAKEY_BLOB *rsa_blob; gnutls_datum_t m, e, d, p, q, u, e1, e2; - ULONG size = key->u.a.bitlen / 8; + ULONG size = len_from_bitlen( key->u.a.bitlen ); BOOL full = (flags & KEY_EXPORT_FLAG_RSA_FULL); UCHAR *dst; int ret; @@ -1778,7 +1778,7 @@ static NTSTATUS key_export_dsa_capi( struct key *key, UCHAR *buf, ULONG len, ULO BLOBHEADER *hdr; DSSPUBKEY *pubkey; gnutls_datum_t p, q, g, y, x; - ULONG size = key->u.a.bitlen / 8; + ULONG size = len_from_bitlen( key->u.a.bitlen ); UCHAR *dst; int ret; @@ -1851,7 +1851,7 @@ static NTSTATUS key_import_dsa_capi( struct key *key, UCHAR *buf, ULONG len ) } pubkey = (DSSPUBKEY *)(hdr + 1); - if ((size = pubkey->bitlen / 8) > sizeof(p_data)) + if ((size = len_from_bitlen( pubkey->bitlen )) > sizeof(p_data)) { FIXME( "size %u not supported\n", size ); pgnutls_privkey_deinit( handle ); @@ -2023,7 +2023,7 @@ static NTSTATUS key_import_dsa_capi_public( struct key *key, UCHAR *buf, ULONG l hdr = (BLOBHEADER *)buf; pubkey = (DSSPUBKEY *)(hdr + 1); - size = pubkey->bitlen / 8; + size = len_from_bitlen( pubkey->bitlen ); data = (unsigned char *)(pubkey + 1); p.data = p_data; @@ -2635,7 +2635,7 @@ static NTSTATUS key_asymmetric_sign( void *args ) if (!params->output) { - *params->ret_len = key->u.a.bitlen / 8; + *params->ret_len = len_from_bitlen( key->u.a.bitlen ); return STATUS_SUCCESS; } if (!key_data(key)->a.privkey) return STATUS_INVALID_PARAMETER; @@ -3512,7 +3512,7 @@ static NTSTATUS key_asymmetric_derive_key( void *args ) return STATUS_INVALID_HANDLE; } - *params->ret_len = EXPORT_SIZE( s, params->privkey->u.a.bitlen / 8, 1 ); + *params->ret_len = EXPORT_SIZE( s, len_from_bitlen( params->privkey->u.a.bitlen ), 1 ); if (params->output) { if (params->output_len < *params->ret_len) status = STATUS_BUFFER_TOO_SMALL; From d92f0d2c6b06208ba87f8562b8d949481b649f6c Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 6 Aug 2025 10:35:50 -0600 Subject: [PATCH 322/454] bcrypt: Use bit length instead of key size in key_import_pair(). CW-Bug-Id: #25779 --- dlls/bcrypt/bcrypt_main.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/dlls/bcrypt/bcrypt_main.c b/dlls/bcrypt/bcrypt_main.c index 568699c03024..5790f82c45d2 100644 --- a/dlls/bcrypt/bcrypt_main.c +++ b/dlls/bcrypt/bcrypt_main.c @@ -1736,29 +1736,29 @@ static NTSTATUS key_import_pair( struct algorithm *alg, const WCHAR *type, BCRYP if (!wcscmp( type, BCRYPT_ECCPUBLIC_BLOB )) { BCRYPT_ECCKEY_BLOB *ecc_blob = (BCRYPT_ECCKEY_BLOB *)input; - DWORD key_size, magic; + DWORD bitlen, magic; if (input_len < sizeof(*ecc_blob)) return STATUS_INVALID_PARAMETER; switch (alg->id) { case ALG_ID_ECDH_P256: - key_size = 32; + bitlen = 256; magic = BCRYPT_ECDH_PUBLIC_P256_MAGIC; break; case ALG_ID_ECDH_P384: - key_size = 48; + bitlen = 384; magic = BCRYPT_ECDH_PUBLIC_P384_MAGIC; break; case ALG_ID_ECDSA_P256: - key_size = 32; + bitlen = 256; magic = BCRYPT_ECDSA_PUBLIC_P256_MAGIC; break; case ALG_ID_ECDSA_P384: - key_size = 48; + bitlen = 384; magic = BCRYPT_ECDSA_PUBLIC_P384_MAGIC; break; @@ -1768,10 +1768,10 @@ static NTSTATUS key_import_pair( struct algorithm *alg, const WCHAR *type, BCRYP } if (ecc_blob->dwMagic != magic) return STATUS_INVALID_PARAMETER; - if (ecc_blob->cbKey != key_size || input_len < sizeof(*ecc_blob) + ecc_blob->cbKey * 2) + if (ecc_blob->cbKey != len_from_bitlen( bitlen ) || input_len < sizeof(*ecc_blob) + ecc_blob->cbKey * 2) return STATUS_INVALID_PARAMETER; - if ((status = key_asymmetric_create( alg->id, key_size * 8, &key ))) return status; + if ((status = key_asymmetric_create( alg->id, bitlen, &key ))) return status; params.key = key; params.flags = KEY_IMPORT_FLAG_PUBLIC; params.buf = input; @@ -1785,24 +1785,24 @@ static NTSTATUS key_import_pair( struct algorithm *alg, const WCHAR *type, BCRYP else if (!wcscmp( type, BCRYPT_ECCPRIVATE_BLOB )) { BCRYPT_ECCKEY_BLOB *ecc_blob = (BCRYPT_ECCKEY_BLOB *)input; - DWORD key_size, magic; + DWORD bitlen, magic; if (input_len < sizeof(*ecc_blob)) return STATUS_INVALID_PARAMETER; switch (alg->id) { case ALG_ID_ECDH_P256: - key_size = 32; + bitlen = 256; magic = BCRYPT_ECDH_PRIVATE_P256_MAGIC; break; case ALG_ID_ECDH_P384: - key_size = 48; + bitlen = 384; magic = BCRYPT_ECDH_PRIVATE_P384_MAGIC; break; case ALG_ID_ECDSA_P256: - key_size = 32; + bitlen = 256; magic = BCRYPT_ECDSA_PRIVATE_P256_MAGIC; break; @@ -1812,10 +1812,10 @@ static NTSTATUS key_import_pair( struct algorithm *alg, const WCHAR *type, BCRYP } if (ecc_blob->dwMagic != magic) return STATUS_INVALID_PARAMETER; - if (ecc_blob->cbKey != key_size || input_len < sizeof(*ecc_blob) + ecc_blob->cbKey * 3) + if (ecc_blob->cbKey != len_from_bitlen( bitlen ) || input_len < sizeof(*ecc_blob) + ecc_blob->cbKey * 3) return STATUS_INVALID_PARAMETER; - if ((status = key_asymmetric_create( alg->id, key_size * 8, &key ))) return status; + if ((status = key_asymmetric_create( alg->id, bitlen, &key ))) return status; params.key = key; params.flags = 0; params.buf = input; From 39c8f6163a9f426d5095737b9ced63e393f3aadc Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 6 Aug 2025 12:25:32 -0600 Subject: [PATCH 323/454] bcrypt: Handle importing ECDSA_P384 private blob. CW-Bug-Id: #25779 --- dlls/bcrypt/bcrypt_main.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/dlls/bcrypt/bcrypt_main.c b/dlls/bcrypt/bcrypt_main.c index 5790f82c45d2..e09c66966b5c 100644 --- a/dlls/bcrypt/bcrypt_main.c +++ b/dlls/bcrypt/bcrypt_main.c @@ -1806,6 +1806,11 @@ static NTSTATUS key_import_pair( struct algorithm *alg, const WCHAR *type, BCRYP magic = BCRYPT_ECDSA_PRIVATE_P256_MAGIC; break; + case ALG_ID_ECDSA_P384: + bitlen = 384; + magic = BCRYPT_ECDSA_PRIVATE_P384_MAGIC; + break; + default: FIXME( "algorithm %u does not yet support importing blob of type %s\n", alg->id, debugstr_w(type) ); return STATUS_NOT_SUPPORTED; From f5a928037679244f9224f94e3c897f0dbe826a0a Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 6 Aug 2025 11:08:16 -0600 Subject: [PATCH 324/454] bcrypt: Support ECDSA_P521 algorithm. CW-Bug-Id: #25779 --- dlls/bcrypt/bcrypt_internal.h | 3 +- dlls/bcrypt/bcrypt_main.c | 14 +++- dlls/bcrypt/gnutls.c | 36 +++++++++- dlls/bcrypt/tests/bcrypt.c | 122 +++++++++++++++++++++++++++++++++- 4 files changed, 169 insertions(+), 6 deletions(-) diff --git a/dlls/bcrypt/bcrypt_internal.h b/dlls/bcrypt/bcrypt_internal.h index d2a5b8022971..ce0b904dd2c6 100644 --- a/dlls/bcrypt/bcrypt_internal.h +++ b/dlls/bcrypt/bcrypt_internal.h @@ -69,6 +69,7 @@ enum alg_id ALG_ID_RSA_SIGN, ALG_ID_ECDSA_P256, ALG_ID_ECDSA_P384, + ALG_ID_ECDSA_P521, ALG_ID_DSA, /* rng */ @@ -280,7 +281,7 @@ enum key_funcs static inline ULONG len_from_bitlen( ULONG bitlen ) { - return bitlen / 8; + return (bitlen + 7) / 8; } #endif /* __BCRYPT_INTERNAL_H */ diff --git a/dlls/bcrypt/bcrypt_main.c b/dlls/bcrypt/bcrypt_main.c index e09c66966b5c..3b5746721d72 100644 --- a/dlls/bcrypt/bcrypt_main.c +++ b/dlls/bcrypt/bcrypt_main.c @@ -121,6 +121,7 @@ builtin_algorithms[] = { BCRYPT_RSA_SIGN_ALGORITHM, BCRYPT_SIGNATURE_INTERFACE, 0, 0, 0 }, { BCRYPT_ECDSA_P256_ALGORITHM, BCRYPT_SIGNATURE_INTERFACE, 0, 0, 0 }, { BCRYPT_ECDSA_P384_ALGORITHM, BCRYPT_SIGNATURE_INTERFACE, 0, 0, 0 }, + { BCRYPT_ECDSA_P521_ALGORITHM, BCRYPT_SIGNATURE_INTERFACE, 0, 0, 0 }, { BCRYPT_DSA_ALGORITHM, BCRYPT_SIGNATURE_INTERFACE, 0, 0, 0 }, { BCRYPT_RNG_ALGORITHM, BCRYPT_RNG_INTERFACE, 0, 0, 0 }, { BCRYPT_PBKDF2_ALGORITHM, BCRYPT_KEY_DERIVATION_INTERFACE, 618, 0, 0 }, @@ -251,7 +252,7 @@ static const struct algorithm pseudo_algorithms[] = {{ MAGIC_ALG }, ALG_ID_DSA }, {{ MAGIC_ALG }, ALG_ID_ECDSA_P256 }, {{ MAGIC_ALG }, ALG_ID_ECDSA_P384 }, - {{ 0 }}, /* ECDSA_P512 */ + {{ MAGIC_ALG }, ALG_ID_ECDSA_P521 }, {{ MAGIC_ALG }, ALG_ID_RSA_SIGN }, }; @@ -1762,6 +1763,11 @@ static NTSTATUS key_import_pair( struct algorithm *alg, const WCHAR *type, BCRYP magic = BCRYPT_ECDSA_PUBLIC_P384_MAGIC; break; + case ALG_ID_ECDSA_P521: + bitlen = 521; + magic = BCRYPT_ECDSA_PUBLIC_P521_MAGIC; + break; + default: FIXME( "algorithm %u does not yet support importing blob of type %s\n", alg->id, debugstr_w(type) ); return STATUS_NOT_SUPPORTED; @@ -1811,6 +1817,11 @@ static NTSTATUS key_import_pair( struct algorithm *alg, const WCHAR *type, BCRYP magic = BCRYPT_ECDSA_PRIVATE_P384_MAGIC; break; + case ALG_ID_ECDSA_P521: + bitlen = 521; + magic = BCRYPT_ECDSA_PRIVATE_P521_MAGIC; + break; + default: FIXME( "algorithm %u does not yet support importing blob of type %s\n", alg->id, debugstr_w(type) ); return STATUS_NOT_SUPPORTED; @@ -2188,6 +2199,7 @@ static const WCHAR *resolve_blob_type( const WCHAR *type, UCHAR *input, ULONG in case BCRYPT_ECDH_PUBLIC_P384_MAGIC: case BCRYPT_ECDSA_PUBLIC_P256_MAGIC: case BCRYPT_ECDSA_PUBLIC_P384_MAGIC: + case BCRYPT_ECDSA_PUBLIC_P521_MAGIC: return BCRYPT_ECCPUBLIC_BLOB; case BCRYPT_RSAPUBLIC_MAGIC: diff --git a/dlls/bcrypt/gnutls.c b/dlls/bcrypt/gnutls.c index 3ba8ba95643c..fe3ab51af591 100644 --- a/dlls/bcrypt/gnutls.c +++ b/dlls/bcrypt/gnutls.c @@ -1062,6 +1062,11 @@ static NTSTATUS key_export_ecc_public( struct key *key, UCHAR *buf, ULONG len, U size = 48; break; + case ALG_ID_ECDSA_P521: + magic = BCRYPT_ECDSA_PUBLIC_P521_MAGIC; + size = 66; + break; + default: FIXME( "algorithm %u not supported\n", key->alg_id ); return STATUS_NOT_IMPLEMENTED; @@ -1078,7 +1083,7 @@ static NTSTATUS key_export_ecc_public( struct key *key, UCHAR *buf, ULONG len, U return STATUS_INTERNAL_ERROR; } - if (curve != GNUTLS_ECC_CURVE_SECP256R1 && curve != GNUTLS_ECC_CURVE_SECP384R1) + if (curve != GNUTLS_ECC_CURVE_SECP256R1 && curve != GNUTLS_ECC_CURVE_SECP384R1 && curve != GNUTLS_ECC_CURVE_SECP521R1) { FIXME( "curve %u not supported\n", curve ); free( x.data ); free( y.data ); @@ -1523,6 +1528,11 @@ static NTSTATUS key_asymmetric_generate( void *args ) bitlen = GNUTLS_CURVE_TO_BITS( GNUTLS_ECC_CURVE_SECP384R1 ); break; + case ALG_ID_ECDSA_P521: + pk_alg = GNUTLS_PK_ECC; /* compatible with ECDSA and ECDH */ + bitlen = GNUTLS_CURVE_TO_BITS( GNUTLS_ECC_CURVE_SECP521R1 ); + break; + default: FIXME( "algorithm %u not supported\n", key->alg_id ); return STATUS_NOT_SUPPORTED; @@ -1589,6 +1599,11 @@ static NTSTATUS key_export_ecc( struct key *key, UCHAR *buf, ULONG len, ULONG *r size = 48; break; + case ALG_ID_ECDSA_P521: + magic = BCRYPT_ECDSA_PRIVATE_P521_MAGIC; + size = 66; + break; + default: FIXME( "algorithm %u does not yet support exporting ecc blob\n", key->alg_id ); return STATUS_NOT_IMPLEMENTED; @@ -1602,7 +1617,7 @@ static NTSTATUS key_export_ecc( struct key *key, UCHAR *buf, ULONG len, ULONG *r return STATUS_INTERNAL_ERROR; } - if (curve != GNUTLS_ECC_CURVE_SECP256R1 && curve != GNUTLS_ECC_CURVE_SECP384R1) + if (curve != GNUTLS_ECC_CURVE_SECP256R1 && curve != GNUTLS_ECC_CURVE_SECP384R1 && curve != GNUTLS_ECC_CURVE_SECP521R1) { FIXME( "curve %u not supported\n", curve ); free( x.data ); free( y.data ); free( d.data ); @@ -1646,6 +1661,10 @@ static NTSTATUS key_import_ecc( struct key *key, UCHAR *buf, ULONG len ) curve = GNUTLS_ECC_CURVE_SECP384R1; break; + case ALG_ID_ECDSA_P521: + curve = GNUTLS_ECC_CURVE_SECP521R1; + break; + default: FIXME( "algorithm %u not yet supported\n", key->alg_id ); return STATUS_NOT_IMPLEMENTED; @@ -1911,6 +1930,9 @@ static NTSTATUS key_import_ecc_public( struct key *key, UCHAR *buf, ULONG len ) case ALG_ID_ECDSA_P384: curve = GNUTLS_ECC_CURVE_SECP384R1; break; + case ALG_ID_ECDSA_P521: + curve = GNUTLS_ECC_CURVE_SECP521R1; break; + default: FIXME( "algorithm %u not yet supported\n", key->alg_id ); return STATUS_NOT_IMPLEMENTED; @@ -2071,6 +2093,7 @@ static NTSTATUS key_asymmetric_export( void *args ) case ALG_ID_ECDH_P384: case ALG_ID_ECDSA_P256: case ALG_ID_ECDSA_P384: + case ALG_ID_ECDSA_P521: if (flags & KEY_EXPORT_FLAG_PUBLIC) return key_export_ecc_public( key, params->buf, params->len, params->ret_len ); return key_export_ecc( key, params->buf, params->len, params->ret_len ); @@ -2158,6 +2181,7 @@ static NTSTATUS key_asymmetric_import( void *args ) case ALG_ID_ECDH_P384: case ALG_ID_ECDSA_P256: case ALG_ID_ECDSA_P384: + case ALG_ID_ECDSA_P521: if (flags & KEY_IMPORT_FLAG_PUBLIC) return key_import_ecc_public( key, params->buf, params->len ); ret = key_import_ecc( key, params->buf, params->len ); @@ -2300,6 +2324,7 @@ static NTSTATUS prepare_gnutls_signature( struct key *key, UCHAR *signature, ULO { case ALG_ID_ECDSA_P256: case ALG_ID_ECDSA_P384: + case ALG_ID_ECDSA_P521: case ALG_ID_DSA: return prepare_gnutls_signature_dsa( key, signature, signature_len, gnutls_signature ); @@ -2366,6 +2391,7 @@ static NTSTATUS key_asymmetric_verify( void *args ) { case ALG_ID_ECDSA_P256: case ALG_ID_ECDSA_P384: + case ALG_ID_ECDSA_P521: { if (flags) FIXME( "flags %#x not supported\n", flags ); @@ -2460,6 +2486,7 @@ static unsigned int get_signature_length( enum alg_id id ) { case ALG_ID_ECDSA_P256: return 64; case ALG_ID_ECDSA_P384: return 96; + case ALG_ID_ECDSA_P521: return 132; case ALG_ID_DSA: return 40; default: FIXME( "unhandled algorithm %u\n", id ); @@ -2482,6 +2509,7 @@ static NTSTATUS format_gnutls_signature( enum alg_id type, gnutls_datum_t signat } case ALG_ID_ECDSA_P256: case ALG_ID_ECDSA_P384: + case ALG_ID_ECDSA_P521: case ALG_ID_DSA: { int err; @@ -2548,7 +2576,7 @@ static NTSTATUS key_asymmetric_sign( void *args ) NTSTATUS status; int ret; - if (key->alg_id == ALG_ID_ECDSA_P256 || key->alg_id == ALG_ID_ECDSA_P384) + if (key->alg_id == ALG_ID_ECDSA_P256 || key->alg_id == ALG_ID_ECDSA_P384 || key->alg_id == ALG_ID_ECDSA_P521) { /* With ECDSA, we find the digest algorithm from the hash length, and verify it */ switch (params->input_len) @@ -2713,6 +2741,7 @@ static NTSTATUS dup_privkey( struct key *key_orig, struct key *key_copy ) case ALG_ID_ECDH_P384: case ALG_ID_ECDSA_P256: case ALG_ID_ECDSA_P384: + case ALG_ID_ECDSA_P521: { gnutls_ecc_curve_t curve; gnutls_datum_t x, y, k; @@ -2777,6 +2806,7 @@ static NTSTATUS dup_pubkey( struct key *key_orig, struct key *key_copy ) case ALG_ID_ECDH_P384: case ALG_ID_ECDSA_P256: case ALG_ID_ECDSA_P384: + case ALG_ID_ECDSA_P521: { gnutls_ecc_curve_t curve; gnutls_datum_t x, y; diff --git a/dlls/bcrypt/tests/bcrypt.c b/dlls/bcrypt/tests/bcrypt.c index 2a14675cdb5a..d15e1de0e1a4 100644 --- a/dlls/bcrypt/tests/bcrypt.c +++ b/dlls/bcrypt/tests/bcrypt.c @@ -2195,6 +2195,27 @@ static BYTE eccPrivkey[] = 0xb9, 0xcd, 0xbe, 0xd4, 0x75, 0x5d, 0x05, 0xe5, 0x83, 0x0c, 0xd3, 0x37, 0x34, 0x15, 0xe3, 0x2c, 0xe5, 0x85, 0x15, 0xa9, 0xee, 0xba, 0x94, 0x03, 0x03, 0x0b, 0x86, 0xea, 0x85, 0x40, 0xbd, 0x35, }; +static BYTE ecc521Privkey[] = +{ + /* X */ + 0x00, 0x5f, 0xea, 0x1e, 0x01, 0xae, 0x69, 0xc3, 0x88, 0x1c, 0xbf, 0x7f, 0x86, 0x1a, 0x48, 0x20, + 0xd3, 0xba, 0xac, 0x9f, 0x1c, 0xc9, 0x99, 0xfa, 0x7d, 0x39, 0xf6, 0xe0, 0xd6, 0x92, 0x97, 0xee, + 0xf6, 0xca, 0x65, 0x40, 0x24, 0xa6, 0xf7, 0x97, 0x17, 0x8c, 0xe1, 0x81, 0x6c, 0x10, 0x92, 0xcd, + 0x41, 0xbc, 0x1c, 0xde, 0x37, 0x4a, 0x21, 0xb9, 0xbc, 0x46, 0x40, 0xa9, 0x91, 0xd9, 0x61, 0x84, + 0x15, 0x33, + /* Y */ + 0x00, 0x31, 0xde, 0xe9, 0x64, 0x9d, 0xb8, 0x43, 0x3a, 0x93, 0x5d, 0xc8, 0x82, 0xec, 0xe4, 0x7f, + 0x83, 0x8d, 0x2c, 0xc7, 0xe8, 0x24, 0x38, 0x5a, 0x81, 0x3d, 0xe6, 0x8d, 0xd4, 0xb1, 0xa0, 0x37, + 0x89, 0xae, 0x1f, 0x81, 0x23, 0x22, 0x8f, 0xd1, 0xe0, 0xc4, 0x6a, 0x99, 0xcc, 0xc8, 0xe4, 0xa0, + 0x65, 0x42, 0x9e, 0xbd, 0xaf, 0x07, 0x79, 0xe8, 0x88, 0xc2, 0xfe, 0xc0, 0x2d, 0x88, 0xd5, 0x3a, + 0xbd, 0xb1, + /* d */ + 0x00, 0x8b, 0xc5, 0xd5, 0x06, 0x3a, 0x1d, 0xd2, 0xf8, 0x26, 0x8e, 0xa2, 0xd3, 0x69, 0x5a, 0xf9, + 0xb6, 0x42, 0x8b, 0x1a, 0x9c, 0x34, 0x04, 0xa6, 0x1d, 0xfc, 0x67, 0xe5, 0x23, 0x71, 0x8e, 0xad, + 0x61, 0x45, 0x4f, 0x00, 0x3e, 0x8f, 0x61, 0xa3, 0xfb, 0xb6, 0x7a, 0x98, 0xf8, 0x27, 0x2c, 0x1b, + 0xa8, 0xda, 0xb7, 0x78, 0xe9, 0xf5, 0x9d, 0xff, 0x6a, 0x07, 0xb0, 0xe2, 0xae, 0x64, 0x15, 0x03, + 0xb3, 0x8a, +}; static BYTE eccPubkey[] = { /* X */ @@ -2218,10 +2239,22 @@ static BYTE certSignature[] = 0xe3, 0x94, 0x15, 0x3b, 0x6c, 0x71, 0x6e, 0x44, 0x22, 0xcb, 0xa0, 0x88, 0xcd, 0x0a, 0x5a, 0x50, 0x29, 0x7c, 0x5c, 0xd6, 0x6c, 0xd2, 0xe0, 0x7f, 0xcd, 0x02, 0x92, 0x21, 0x4c, 0x2c, 0x92, 0xee, }; +static BYTE cert521Signature[] = +{ + 0x01, 0x6b, 0xd6, 0xca, 0xac, 0x28, 0xa8, 0xa9, 0x83, 0x9d, 0xca, 0x13, 0x08, 0xd6, 0xf2, 0x9c, + 0x94, 0x6b, 0x28, 0x6b, 0x93, 0x58, 0x3c, 0x65, 0x54, 0xb4, 0xa6, 0xb8, 0x0d, 0x55, 0xed, 0x4e, + 0xc9, 0x98, 0x26, 0x96, 0x1a, 0xbb, 0x9f, 0x9e, 0x5c, 0xb1, 0x1e, 0x8b, 0x04, 0x82, 0xe6, 0x32, + 0x15, 0x92, 0xcb, 0xfe, 0xe7, 0x53, 0xfc, 0x17, 0xe0, 0xc9, 0x44, 0xf5, 0x1d, 0x37, 0x33, 0x02, + 0xbb, 0x75, 0x01, 0x65, 0x84, 0xab, 0x89, 0xb3, 0x69, 0x56, 0xf4, 0x18, 0xb0, 0xdd, 0xfd, 0x69, + 0xe6, 0x52, 0x1e, 0x75, 0x4f, 0x98, 0xa8, 0x49, 0x88, 0x84, 0x15, 0x58, 0x23, 0x9f, 0x89, 0x06, + 0x73, 0x6b, 0x8c, 0xf9, 0x9a, 0x85, 0x1d, 0xd2, 0xf4, 0x06, 0x65, 0xa5, 0x88, 0x12, 0xa3, 0x4e, + 0xcd, 0x99, 0x06, 0x1b, 0xf8, 0x17, 0xe0, 0xeb, 0xb8, 0x7f, 0x6b, 0x89, 0x47, 0xd2, 0x5d, 0x30, + 0xf4, 0xf6, 0x5f, 0x83, +}; static void test_ECDSA(void) { - BYTE buffer[sizeof(BCRYPT_ECCKEY_BLOB) + sizeof(eccPrivkey)]; + BYTE buffer[sizeof(BCRYPT_ECCKEY_BLOB) + sizeof(ecc521Privkey)]; BCRYPT_ECCKEY_BLOB *ecckey = (void *)buffer; BCRYPT_ALG_HANDLE alg; BCRYPT_KEY_HANDLE key; @@ -2321,6 +2354,67 @@ static void test_ECDSA(void) BCryptDestroyKey(key); BCryptCloseAlgorithmProvider(alg, 0); + + /* P521 */ + status = BCryptOpenAlgorithmProvider(&alg, BCRYPT_ECDSA_P521_ALGORITHM, NULL, 0); + ok(!status, "got %#lx\n", status); + + ecckey->dwMagic = BCRYPT_ECDSA_PUBLIC_P521_MAGIC; + ecckey->cbKey = 66; + size = sizeof(BCRYPT_ECCKEY_BLOB) + ecckey->cbKey * 2; + memcpy(ecckey + 1, ecc521Privkey, ecckey->cbKey * 2); + status = BCryptImportKeyPair(alg, NULL, BCRYPT_ECCPUBLIC_BLOB, &key, buffer, size, 0); + ok(!status, "BCryptImportKeyPair failed: %#lx\n", status); + + keylen = 0; + status = BCryptGetProperty(key, BCRYPT_KEY_STRENGTH, (UCHAR *)&keylen, sizeof(keylen), &size, 0); + ok(!status, "got %#lx\n", status); + ok(size == sizeof(keylen), "got %lu\n", size); + ok(keylen == 521, "got %lu\n", keylen); + + memset(buffer, 0xcc, sizeof(buffer)); + status = BCryptExportKey(key, NULL, BCRYPT_ECCPUBLIC_BLOB, buffer, sizeof(buffer), &size, 0); + ok(!status, "Got unexpected status %#lx\n", status); + ok(ecckey->dwMagic == BCRYPT_ECDSA_PUBLIC_P521_MAGIC, "Got unexpected magic %#lx.\n", ecckey->dwMagic); + ok(ecckey->cbKey == 66, "got %lu\n", ecckey->cbKey); + ok(!memcmp(ecckey + 1, ecc521Privkey, ecckey->cbKey * 2), "Got unexpected key data.\n"); + + memcpy(buffer, cert521Signature, sizeof(cert521Signature)); + status = BCryptVerifySignature(key, NULL, certHash, sizeof(certHash), buffer, sizeof(cert521Signature), 0); + ok(!status, "BCryptVerifySignature failed: %#lx\n", status); + + ++buffer[5]; + status = BCryptVerifySignature(key, NULL, certHash, sizeof(certHash), buffer, sizeof(cert521Signature), 0); + ok(status == STATUS_INVALID_SIGNATURE, "BCryptVerifySignature failed: %#lx\n", status); + + BCryptDestroyKey(key); + + ecckey->dwMagic = BCRYPT_ECDSA_PRIVATE_P521_MAGIC; + ecckey->cbKey = 66; + memcpy(ecckey + 1, ecc521Privkey, sizeof(ecc521Privkey)); + size = sizeof(*ecckey) + sizeof(ecc521Privkey); + status = BCryptImportKeyPair(alg, NULL, BCRYPT_ECCPRIVATE_BLOB, &key, buffer, size, 0); + ok(!status, "BCryptImportKeyPair failed: %#lx\n", status); + + memset( buffer, 0xcc, sizeof(buffer) ); + status = BCryptExportKey(key, NULL, BCRYPT_ECCPUBLIC_BLOB, buffer, sizeof(buffer), &size, 0); + ok(!status, "Got unexpected status %#lx\n", status); + ok(ecckey->dwMagic == BCRYPT_ECDSA_PUBLIC_P521_MAGIC, "got %#lx\n", ecckey->dwMagic); + ok(ecckey->cbKey == 66, "got %lu\n", ecckey->cbKey); + ok(!memcmp(ecckey + 1, ecc521Privkey, ecckey->cbKey * 2), "Got unexpected key data.\n"); + + size = sizeof(BCRYPT_ECCKEY_BLOB) + sizeof(ecc521Privkey); + memset( buffer, 0xcc, sizeof(buffer) ); + status = BCryptExportKey(key, NULL, BCRYPT_ECCPRIVATE_BLOB, buffer, size, &size, 0); + ok(status == STATUS_SUCCESS, "got %#lx\n", status); + ecckey = (BCRYPT_ECCKEY_BLOB *)buffer; + ok(ecckey->dwMagic == BCRYPT_ECDSA_PRIVATE_P521_MAGIC, "got %#lx\n", ecckey->dwMagic); + ok(ecckey->cbKey == 66, "got %lu\n", ecckey->cbKey); + ok(size == sizeof(*ecckey) + ecckey->cbKey * 3, "got %lu\n", size); + ok(!memcmp(ecckey + 1, ecc521Privkey, ecckey->cbKey * 3), "Got unexpected key data.\n"); + + BCryptDestroyKey(key); + BCryptCloseAlgorithmProvider(alg, 0); } static UCHAR rsaPublicBlob[] = @@ -3587,6 +3681,32 @@ static void test_BCryptSignHash(void) ret = BCryptCloseAlgorithmProvider(alg, 0); ok(!ret, "got %#lx\n", ret); + + /* ECDSA P521 */ + ret = BCryptOpenAlgorithmProvider(&alg, BCRYPT_ECDSA_P521_ALGORITHM, NULL, 0); + ok(!ret, "got %#lx\n", ret); + + ret = BCryptGenerateKeyPair(alg, &key, 256, 0); + todo_wine ok(ret == STATUS_INVALID_PARAMETER, "got %#lx\n", ret); + ret = BCryptGenerateKeyPair(alg, &key, 522, 0); + todo_wine ok(ret == STATUS_INVALID_PARAMETER, "got %#lx\n", ret); + + ret = BCryptGenerateKeyPair(alg, &key, 521, 0); + ok(ret == STATUS_SUCCESS, "got %#lx\n", ret); + ret = BCryptFinalizeKeyPair(key, 0); + ok(ret == STATUS_SUCCESS, "got %#lx\n", ret); + len = 0; + ret = BCryptSignHash(key, NULL, hash, sizeof(hash), sig, sizeof(sig), &len, 0); + ok (!ret, "got %#lx\n", ret); + ok (len == 132, "got %lu\n", len); + + ret = BCryptVerifySignature(key, NULL, hash, sizeof(hash), sig, len, 0); + ok(!ret, "got %#lx\n", ret); + + ret = BCryptDestroyKey(key); + ok(!ret, "got %#lx\n", ret); + ret = BCryptCloseAlgorithmProvider(alg, 0); + ok(!ret, "got %#lx\n", ret); } static void test_BCryptEnumAlgorithms(void) From 44a4ee394634f1f9952423011bd8f7982dbbb3e9 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 6 Aug 2025 12:16:12 -0600 Subject: [PATCH 325/454] bcrypt/tests: Test ECDH_384 same way as ECDH_256. CW-Bug-Id: #25779 --- dlls/bcrypt/tests/bcrypt.c | 305 ++++++++++++++++++++++--------------- 1 file changed, 178 insertions(+), 127 deletions(-) diff --git a/dlls/bcrypt/tests/bcrypt.c b/dlls/bcrypt/tests/bcrypt.c index d15e1de0e1a4..539b38ac8bb0 100644 --- a/dlls/bcrypt/tests/bcrypt.c +++ b/dlls/bcrypt/tests/bcrypt.c @@ -3099,57 +3099,38 @@ static void test_RSA_SIGN(void) ok(!ret, "BCryptCloseAlgorithmProvider failed: %#lx\n", ret); } -static BYTE eccprivkey[] = +struct ecdh_test { - 0x45, 0x43, 0x4b, 0x32, 0x20, 0x00, 0x00, 0x00, - 0xfb, 0xbd, 0x3d, 0x20, 0x1b, 0x6d, 0x66, 0xb3, 0x7c, 0x9f, 0x89, 0xf3, 0xe4, 0x41, 0x16, 0xa5, - 0x68, 0x52, 0x77, 0xac, 0xab, 0x55, 0xb2, 0x6c, 0xb0, 0x23, 0x55, 0xcb, 0x96, 0x14, 0xfd, 0x0b, - 0x1c, 0xef, 0xdf, 0x07, 0x6d, 0x31, 0xaf, 0x39, 0xce, 0x8c, 0x8f, 0x9d, 0x75, 0xd0, 0x7b, 0xea, - 0x81, 0xdc, 0x40, 0x21, 0x1f, 0x58, 0x22, 0x5f, 0x72, 0x55, 0xfc, 0x58, 0x8a, 0xeb, 0x88, 0x5d, - 0x02, 0x09, 0x90, 0xd2, 0xe3, 0x36, 0xac, 0xfe, 0x83, 0x13, 0x6c, 0x88, 0x1a, 0xab, 0x9b, 0xdd, - 0xaa, 0x8a, 0xee, 0x69, 0x9a, 0x6a, 0x62, 0x86, 0x6a, 0x13, 0x69, 0x88, 0xb7, 0xd5, 0xa3, 0xcd -}; - -static BYTE ecdh_pubkey[] = -{ - 0x45, 0x43, 0x4b, 0x31, 0x20, 0x00, 0x00, 0x00, - 0x07, 0x61, 0x9d, 0x49, 0x63, 0x6b, 0x96, 0x94, 0xd1, 0x8f, 0xd1, 0x48, 0xcc, 0xcf, 0x72, 0x4d, - 0xff, 0x43, 0xf4, 0x97, 0x0f, 0xa3, 0x8a, 0x72, 0xe9, 0xe0, 0xba, 0x87, 0x6d, 0xc3, 0x62, 0x15, - 0xae, 0x65, 0xdd, 0x31, 0x51, 0xfc, 0x3b, 0xc9, 0x59, 0xa1, 0x0a, 0x92, 0x17, 0x2b, 0x64, 0x55, - 0x03, 0x3e, 0x62, 0x1d, 0xac, 0x3e, 0x37, 0x40, 0x6a, 0x4c, 0xb6, 0x21, 0x3f, 0x73, 0x5c, 0xf5 -}; - -/* little endian */ -static BYTE ecdh_secret[] = -{ - 0x48, 0xb0, 0x11, 0xdb, 0x69, 0x4e, 0xb4, 0xf4, 0xf5, 0x3e, 0xe1, 0x9b, 0xca, 0x00, 0x04, 0xc8, - 0x9b, 0x69, 0xaf, 0xd1, 0xaf, 0x1f, 0xc2, 0xd7, 0x83, 0x0a, 0xb7, 0xf8, 0x4f, 0x24, 0x32, 0x8e, -}; - -BCryptBuffer hash_param_buffers[] = -{ -{ - sizeof(BCRYPT_SHA1_ALGORITHM), - KDF_HASH_ALGORITHM, - (void *)BCRYPT_SHA1_ALGORITHM, -} -}; - -BCryptBufferDesc hash_params = -{ - BCRYPTBUFFER_VERSION, - ARRAY_SIZE(hash_param_buffers), - hash_param_buffers, + const WCHAR *alg; + ULONG bitlen; + BYTE *eccprivkey; + ULONG eccprivkey_len; + BYTE *ecdh_pubkey; + ULONG ecdh_pubkey_len; + BYTE *ecdh_secret; + ULONG ecdh_secret_len; + BYTE *hashed_secret; + DWORD public_magic; + DWORD private_magic; }; -static BYTE hashed_secret[] = +static void test_ECDH_alg(const struct ecdh_test *t) { - 0x1b, 0xe7, 0xbf, 0x0f, 0x65, 0x1e, 0xd0, 0x07, 0xf9, 0xf4, 0x77, 0x48, 0x48, 0x39, 0xd0, 0xf8, - 0xf3, 0xce, 0xfc, 0x89 -}; + BCryptBuffer hash_param_buffers[] = + { + { + sizeof(BCRYPT_SHA1_ALGORITHM), + KDF_HASH_ALGORITHM, + (void *)BCRYPT_SHA1_ALGORITHM, + } + }; -static void test_ECDH(void) -{ + BCryptBufferDesc hash_params = + { + BCRYPTBUFFER_VERSION, + ARRAY_SIZE(hash_param_buffers), + hash_param_buffers, + }; BYTE *buf; BCRYPT_ECCKEY_BLOB *ecckey; BCRYPT_ALG_HANDLE alg; @@ -3158,11 +3139,11 @@ static void test_ECDH(void) NTSTATUS status; ULONG size; - status = BCryptOpenAlgorithmProvider(&alg, BCRYPT_ECDH_P256_ALGORITHM, NULL, 0); + status = BCryptOpenAlgorithmProvider(&alg, t->alg, NULL, 0); ok(status == STATUS_SUCCESS, "got %#lx\n", status); key = NULL; - status = BCryptGenerateKeyPair(alg, &key, 256, 0); + status = BCryptGenerateKeyPair(alg, &key, t->bitlen, 0); ok(status == STATUS_SUCCESS, "got %#lx\n", status); ok(key != NULL, "key not set\n"); @@ -3178,8 +3159,8 @@ static void test_ECDH(void) status = BCryptExportKey(key, NULL, BCRYPT_ECCPUBLIC_BLOB, buf, size, &size, 0); ok(status == STATUS_SUCCESS, "got %#lx\n", status); ecckey = (BCRYPT_ECCKEY_BLOB *)buf; - ok(ecckey->dwMagic == BCRYPT_ECDH_PUBLIC_P256_MAGIC, "got %#lx\n", ecckey->dwMagic); - ok(ecckey->cbKey == 32, "got %lu\n", ecckey->cbKey); + ok(ecckey->dwMagic == t->public_magic, "got %#lx\n", ecckey->dwMagic); + ok(ecckey->cbKey == (t->bitlen + 7) / 8, "got %lu\n", ecckey->cbKey); ok(size == sizeof(*ecckey) + ecckey->cbKey * 2, "got %lu\n", size); status = BCryptImportKeyPair(alg, NULL, BCRYPT_PUBLIC_KEY_BLOB, &pubkey, buf, size, 0); @@ -3199,8 +3180,8 @@ static void test_ECDH(void) status = BCryptExportKey(key, NULL, BCRYPT_ECCPRIVATE_BLOB, buf, size, &size, 0); ok(status == STATUS_SUCCESS, "got %#lx\n", status); ecckey = (BCRYPT_ECCKEY_BLOB *)buf; - ok(ecckey->dwMagic == BCRYPT_ECDH_PRIVATE_P256_MAGIC, "got %#lx\n", ecckey->dwMagic); - ok(ecckey->cbKey == 32, "got %lu\n", ecckey->cbKey); + ok(ecckey->dwMagic == t->private_magic, "got %#lx\n", ecckey->dwMagic); + ok(ecckey->cbKey == (t->bitlen + 7) / 8, "got %lu\n", ecckey->cbKey); ok(size == sizeof(*ecckey) + ecckey->cbKey * 3, "got %lu\n", size); status = BCryptImportKeyPair(alg, NULL, BCRYPT_ECCPRIVATE_BLOB, &privkey, buf, size, 0); @@ -3210,7 +3191,7 @@ static void test_ECDH(void) BCryptDestroyKey(privkey); BCryptDestroyKey(key); - status = BCryptImportKeyPair(alg, NULL, BCRYPT_ECCPRIVATE_BLOB, &privkey, eccprivkey, sizeof(eccprivkey), 0); + status = BCryptImportKeyPair(alg, NULL, BCRYPT_ECCPRIVATE_BLOB, &privkey, t->eccprivkey, t->eccprivkey_len, 0); ok(status == STATUS_SUCCESS, "got %#lx\n", status); size = 0; @@ -3221,11 +3202,11 @@ static void test_ECDH(void) buf = malloc(size); status = BCryptExportKey(privkey, NULL, BCRYPT_ECCPRIVATE_BLOB, buf, size, &size, 0); ok(status == STATUS_SUCCESS, "got %#lx\n", status); - ok(size == sizeof(eccprivkey), "got %lu\n", size); - ok(!memcmp(buf, eccprivkey, size), "wrong data\n"); + ok(size == t->eccprivkey_len, "got %lu\n", size); + ok(!memcmp(buf, t->eccprivkey, size), "wrong data\n"); free(buf); - status = BCryptImportKeyPair(alg, NULL, BCRYPT_ECCPUBLIC_BLOB, &pubkey, ecdh_pubkey, sizeof(ecdh_pubkey), 0); + status = BCryptImportKeyPair(alg, NULL, BCRYPT_ECCPUBLIC_BLOB, &pubkey, t->ecdh_pubkey, t->ecdh_pubkey_len, 0); ok(status == STATUS_SUCCESS, "got %#lx\n", status); status = BCryptSecretAgreement(privkey, pubkey, &secret, 0); @@ -3240,7 +3221,7 @@ static void test_ECDH(void) } ok(status == STATUS_SUCCESS, "got %#lx\n", status); - ok(size == 32, "size of secret key incorrect, got %lu, expected 32\n", size); + ok(size == (t->bitlen + 7) / 8, "size of secret key incorrect, got %lu, expected 32\n", size); if (!size) goto raw_secret_end; @@ -3248,7 +3229,7 @@ static void test_ECDH(void) buf = malloc(size); status = BCryptDeriveKey(secret, BCRYPT_KDF_RAW_SECRET, NULL, buf, size, &size, 0); ok(status == STATUS_SUCCESS, "got %#lx\n", status); - ok(!(memcmp(ecdh_secret, buf, size)), "wrong data\n"); + ok(!(memcmp(t->ecdh_secret, buf, size)), "wrong data\n"); free(buf); raw_secret_end: @@ -3260,7 +3241,7 @@ static void test_ECDH(void) buf = malloc(size); status = BCryptDeriveKey(secret, BCRYPT_KDF_HASH, &hash_params, buf, size, &size, 0); ok(status == STATUS_SUCCESS, "got %#lx\n", status); - ok(!(memcmp(hashed_secret, buf, size)), "wrong data\n"); + ok(!(memcmp(t->hashed_secret, buf, size)), "wrong data\n"); free(buf); /* ulVersion is not verified */ @@ -3285,40 +3266,95 @@ static void test_ECDH(void) BCryptDestroyKey(pubkey); BCryptDestroyKey(privkey); BCryptCloseAlgorithmProvider(alg, 0); +} - status = BCryptOpenAlgorithmProvider(&alg, BCRYPT_ECDH_P384_ALGORITHM, NULL, 0); - ok(status == STATUS_SUCCESS, "got %#lx\n", status); - - key = NULL; - status = BCryptGenerateKeyPair(alg, &key, 384, 0); - ok(status == STATUS_SUCCESS, "got %#lx\n", status); - ok(key != NULL, "key not set\n"); - - status = BCryptFinalizeKeyPair(key, 0); - ok(status == STATUS_SUCCESS, "got %#lx\n", status); - - size = 0; - status = BCryptExportKey(key, NULL, BCRYPT_ECCPUBLIC_BLOB, NULL, 0, &size, 0); - ok(status == STATUS_SUCCESS, "got %#lx\n", status); - ok(size, "size not set\n"); +static void test_ECDH(void) +{ + static BYTE ecc256privkey[] = + { + 0x45, 0x43, 0x4b, 0x32, 0x20, 0x00, 0x00, 0x00, + 0xfb, 0xbd, 0x3d, 0x20, 0x1b, 0x6d, 0x66, 0xb3, 0x7c, 0x9f, 0x89, 0xf3, 0xe4, 0x41, 0x16, 0xa5, + 0x68, 0x52, 0x77, 0xac, 0xab, 0x55, 0xb2, 0x6c, 0xb0, 0x23, 0x55, 0xcb, 0x96, 0x14, 0xfd, 0x0b, + 0x1c, 0xef, 0xdf, 0x07, 0x6d, 0x31, 0xaf, 0x39, 0xce, 0x8c, 0x8f, 0x9d, 0x75, 0xd0, 0x7b, 0xea, + 0x81, 0xdc, 0x40, 0x21, 0x1f, 0x58, 0x22, 0x5f, 0x72, 0x55, 0xfc, 0x58, 0x8a, 0xeb, 0x88, 0x5d, + 0x02, 0x09, 0x90, 0xd2, 0xe3, 0x36, 0xac, 0xfe, 0x83, 0x13, 0x6c, 0x88, 0x1a, 0xab, 0x9b, 0xdd, + 0xaa, 0x8a, 0xee, 0x69, 0x9a, 0x6a, 0x62, 0x86, 0x6a, 0x13, 0x69, 0x88, 0xb7, 0xd5, 0xa3, 0xcd + }; + static BYTE ecdh256_pubkey[] = + { + 0x45, 0x43, 0x4b, 0x31, 0x20, 0x00, 0x00, 0x00, + 0x07, 0x61, 0x9d, 0x49, 0x63, 0x6b, 0x96, 0x94, 0xd1, 0x8f, 0xd1, 0x48, 0xcc, 0xcf, 0x72, 0x4d, + 0xff, 0x43, 0xf4, 0x97, 0x0f, 0xa3, 0x8a, 0x72, 0xe9, 0xe0, 0xba, 0x87, 0x6d, 0xc3, 0x62, 0x15, + 0xae, 0x65, 0xdd, 0x31, 0x51, 0xfc, 0x3b, 0xc9, 0x59, 0xa1, 0x0a, 0x92, 0x17, 0x2b, 0x64, 0x55, + 0x03, 0x3e, 0x62, 0x1d, 0xac, 0x3e, 0x37, 0x40, 0x6a, 0x4c, 0xb6, 0x21, 0x3f, 0x73, 0x5c, 0xf5 + }; + static BYTE ecdh256_secret[] = + { + 0x48, 0xb0, 0x11, 0xdb, 0x69, 0x4e, 0xb4, 0xf4, 0xf5, 0x3e, 0xe1, 0x9b, 0xca, 0x00, 0x04, 0xc8, + 0x9b, 0x69, 0xaf, 0xd1, 0xaf, 0x1f, 0xc2, 0xd7, 0x83, 0x0a, 0xb7, 0xf8, 0x4f, 0x24, 0x32, 0x8e, + }; + static BYTE hashed256_secret[] = + { + 0x1b, 0xe7, 0xbf, 0x0f, 0x65, 0x1e, 0xd0, 0x07, 0xf9, 0xf4, 0x77, 0x48, 0x48, 0x39, 0xd0, 0xf8, + 0xf3, 0xce, 0xfc, 0x89 + }; - buf = malloc(size); - status = BCryptExportKey(key, NULL, BCRYPT_ECCPUBLIC_BLOB, buf, size, &size, 0); - ok(status == STATUS_SUCCESS, "got %#lx\n", status); - ecckey = (BCRYPT_ECCKEY_BLOB *)buf; - ok(ecckey->dwMagic == BCRYPT_ECDH_PUBLIC_P384_MAGIC, "got %#lx\n", ecckey->dwMagic); - ok(ecckey->cbKey == 48, "got %lu\n", ecckey->cbKey); - ok(size == sizeof(*ecckey) + ecckey->cbKey * 2, "got %lu\n", size); + static BYTE ecc384privkey[] = + { + 0x45, 0x43, 0x4b, 0x34, 0x30, 0x00, 0x00, 0x00, + 0xc9, 0xcb, 0x38, 0x54, 0xa1, 0xe2, 0xb6, 0x60, 0x13, 0xd9, 0x45, 0x0d, 0x76, 0x90, 0xf9, 0x49, + 0x75, 0x81, 0x76, 0xac, 0x43, 0x96, 0xc1, 0x04, 0x04, 0xda, 0x76, 0x72, 0xb1, 0x19, 0x38, 0xbd, + 0xaf, 0x96, 0x0c, 0x4e, 0xc3, 0x29, 0x67, 0x91, 0x6c, 0xac, 0xcc, 0x33, 0x51, 0x1f, 0x82, 0xd5, + 0x17, 0xbf, 0xa2, 0x94, 0xd7, 0x15, 0x4f, 0x83, 0xe7, 0xa3, 0xb8, 0x6d, 0xd0, 0x7f, 0xbc, 0x8a, + 0x30, 0x09, 0x9e, 0x13, 0xaa, 0x2e, 0xf6, 0xde, 0x1c, 0x02, 0x4a, 0x65, 0xf6, 0x72, 0xb3, 0xf0, + 0x4f, 0x7f, 0x1a, 0xce, 0x4e, 0x94, 0xc5, 0x98, 0x89, 0x74, 0xad, 0x51, 0x8f, 0x2b, 0x25, 0x17, + 0xdc, 0x4a, 0x54, 0x8a, 0x42, 0xda, 0x30, 0x1a, 0xe3, 0x6d, 0x77, 0x4d, 0x3b, 0x33, 0x9a, 0xe3, + 0x37, 0xd4, 0x06, 0x5b, 0xb3, 0x3f, 0x73, 0xb9, 0x7e, 0x0b, 0x37, 0x02, 0x8b, 0xed, 0x08, 0x10, + 0x03, 0xf8, 0x69, 0xe3, 0x2a, 0x4f, 0xbb, 0x20, 0x6c, 0x5d, 0x24, 0x09, 0x0d, 0xd9, 0x86, 0x32, + }; + static BYTE ecdh384_pubkey[] = + { + 0x45, 0x43, 0x4b, 0x33, 0x30, 0x00, 0x00, 0x00, + 0xd6, 0xc3, 0xef, 0x4a, 0xbb, 0x4c, 0xa2, 0x27, 0xa1, 0x96, 0x03, 0x3b, 0x0a, 0x83, 0x01, 0xff, + 0xeb, 0x9a, 0xf1, 0x06, 0xee, 0x83, 0xce, 0x7c, 0xaa, 0x6b, 0x7c, 0x43, 0x1c, 0x8b, 0x30, 0x82, + 0x99, 0x8f, 0xc4, 0x86, 0x0d, 0x19, 0x16, 0xb6, 0xab, 0xd1, 0x9f, 0xeb, 0xf9, 0x31, 0xda, 0xcd, + 0xb9, 0xf8, 0xea, 0x87, 0xa1, 0x36, 0xaf, 0x10, 0x98, 0x8f, 0x9b, 0xcc, 0x6c, 0xe3, 0x24, 0xb4, + 0x82, 0x37, 0xde, 0x1e, 0x04, 0x53, 0x03, 0xc0, 0x2a, 0x41, 0xe9, 0x50, 0x07, 0x87, 0xb2, 0x60, + 0xe6, 0x32, 0x53, 0x4c, 0x8e, 0xa7, 0x80, 0x0f, 0xad, 0x25, 0x3b, 0x01, 0xa4, 0xd6, 0xe3, 0x54, + }; + static BYTE ecdh384_secret[] = + { + 0x08, 0x8c, 0xd1, 0xfa, 0x57, 0xb6, 0xf9, 0x79, 0x9e, 0x0c, 0x71, 0xc3, 0xcb, 0xa5, 0xb1, 0xfd, + 0xde, 0xbc, 0x6d, 0x7c, 0x8a, 0x5b, 0x26, 0xe8, 0x18, 0x80, 0x61, 0x45, 0x6a, 0x38, 0xf9, 0x13, + 0x2a, 0x8f, 0x4b, 0xfe, 0x75, 0x02, 0x62, 0xe9, 0xb3, 0x6e, 0xc5, 0x52, 0xc9, 0x82, 0x38, 0x53, + }; + static BYTE hashed384_secret[] = + { + 0x78, 0xf8, 0x07, 0xab, 0x00, 0x35, 0xaa, 0x8c, 0x22, 0xd0, 0xe7, 0x06, 0xfc, 0x0b, 0x74, 0x41, + 0xed, 0xdc, 0x16, 0x6c, + }; - status = BCryptImportKeyPair(alg, NULL, BCRYPT_PUBLIC_KEY_BLOB, &pubkey, buf, size, 0); - ok(status == STATUS_SUCCESS, "got %#lx\n", status); - BCryptDestroyKey(pubkey); + static const struct ecdh_test tests[] = + { + { + BCRYPT_ECDH_P256_ALGORITHM, 256, ecc256privkey, sizeof(ecc256privkey), ecdh256_pubkey, sizeof(ecdh256_pubkey), + ecdh256_secret, sizeof(ecdh256_secret), hashed256_secret, + BCRYPT_ECDH_PUBLIC_P256_MAGIC, BCRYPT_ECDH_PRIVATE_P256_MAGIC, + }, + { + BCRYPT_ECDH_P384_ALGORITHM, 384, ecc384privkey, sizeof(ecc384privkey), ecdh384_pubkey, sizeof(ecdh384_pubkey), + ecdh384_secret, sizeof(ecdh384_secret), hashed384_secret, + BCRYPT_ECDH_PUBLIC_P384_MAGIC, BCRYPT_ECDH_PRIVATE_P384_MAGIC, + }, + }; + unsigned int i; - status = BCryptImportKeyPair(alg, NULL, BCRYPT_ECCPUBLIC_BLOB, &pubkey, buf, size, 0); - ok(status == STATUS_SUCCESS, "got %#lx\n", status); - free(buf); - BCryptDestroyKey(pubkey); - BCryptCloseAlgorithmProvider(alg, 0); + for (i = 0; i < ARRAY_SIZE(tests); ++i) + { + winetest_push_context("%s", debugstr_w(tests[i].alg)); + test_ECDH_alg(&tests[i]); + winetest_pop_context(); + } } static BYTE dh_pubkey[] = @@ -4138,6 +4174,16 @@ static void test_SecretAgreement(void) { 0x3213db5b, 0x8cc8250b, 0xc829eaab, 0x00933709, 0x68160aa9, 0xfb9f1e20, 0xf92368e6, 0x2b8e18eb, }; + static const struct + { + const WCHAR *alg; + ULONG bitlen; + } + ecdh_algorithms[] = + { + { BCRYPT_ECDH_P256_ALGORITHM, 256 }, + { BCRYPT_ECDH_P384_ALGORITHM, 384 }, + }; static const ULONG length = 1024; BCRYPT_DH_PARAMETER_HEADER *dh_header; BCRYPT_DH_KEY_BLOB *dh_key_blob; @@ -4148,61 +4194,66 @@ static void test_SecretAgreement(void) NTSTATUS status; ULONG size, i; - status = BCryptOpenAlgorithmProvider(&alg, BCRYPT_ECDH_P256_ALGORITHM, NULL, 0); - ok(status == STATUS_SUCCESS, "got %#lx\n", status); + for (i = 0; i < ARRAY_SIZE(ecdh_algorithms); ++i) + { + winetest_push_context("%s", debugstr_w(ecdh_algorithms[i].alg)); + status = BCryptOpenAlgorithmProvider(&alg, ecdh_algorithms[i].alg, NULL, 0); + ok(status == STATUS_SUCCESS, "got %#lx\n", status); - key = NULL; - status = BCryptGenerateKeyPair(alg, &key, 256, 0); - ok(status == STATUS_SUCCESS, "got %#lx\n", status); - ok(key != NULL, "key not set\n"); + key = NULL; + status = BCryptGenerateKeyPair(alg, &key, ecdh_algorithms[i].bitlen, 0); + ok(status == STATUS_SUCCESS, "got %#lx\n", status); + ok(key != NULL, "key not set\n"); - status = BCryptFinalizeKeyPair(key, 0); - ok(status == STATUS_SUCCESS, "got %#lx\n", status); + status = BCryptFinalizeKeyPair(key, 0); + ok(status == STATUS_SUCCESS, "got %#lx\n", status); - status = BCryptSecretAgreement(NULL, key, &secret, 0); - ok(status == STATUS_INVALID_HANDLE, "got %#lx\n", status); + status = BCryptSecretAgreement(NULL, key, &secret, 0); + ok(status == STATUS_INVALID_HANDLE, "got %#lx\n", status); - status = BCryptSecretAgreement(key, NULL, &secret, 0); - ok(status == STATUS_INVALID_HANDLE, "got %#lx\n", status); + status = BCryptSecretAgreement(key, NULL, &secret, 0); + ok(status == STATUS_INVALID_HANDLE, "got %#lx\n", status); - status = BCryptSecretAgreement(key, key, NULL, 0); - ok(status == STATUS_INVALID_PARAMETER, "got %#lx\n", status); + status = BCryptSecretAgreement(key, key, NULL, 0); + ok(status == STATUS_INVALID_PARAMETER, "got %#lx\n", status); - status = BCryptSecretAgreement(key, key, &secret, 0); - ok(status == STATUS_SUCCESS, "got %#lx\n", status); + status = BCryptSecretAgreement(key, key, &secret, 0); + ok(status == STATUS_SUCCESS, "got %#lx\n", status); - status = BCryptDeriveKey(NULL, L"HASH", NULL, NULL, 0, &size, 0); - ok(status == STATUS_INVALID_HANDLE, "got %#lx\n", status); + status = BCryptDeriveKey(NULL, L"HASH", NULL, NULL, 0, &size, 0); + ok(status == STATUS_INVALID_HANDLE, "got %#lx\n", status); - status = BCryptDeriveKey(key, L"HASH", NULL, NULL, 0, &size, 0); - ok(status == STATUS_INVALID_HANDLE, "got %#lx\n", status); + status = BCryptDeriveKey(key, L"HASH", NULL, NULL, 0, &size, 0); + ok(status == STATUS_INVALID_HANDLE, "got %#lx\n", status); - status = BCryptDeriveKey(secret, NULL, NULL, NULL, 0, &size, 0); - ok(status == STATUS_INVALID_PARAMETER, "got %#lx\n", status); + status = BCryptDeriveKey(secret, NULL, NULL, NULL, 0, &size, 0); + ok(status == STATUS_INVALID_PARAMETER, "got %#lx\n", status); - status = BCryptDeriveKey(secret, L"HASH", NULL, NULL, 0, &size, 0); - ok(status == STATUS_SUCCESS, "got %#lx\n", status); + status = BCryptDeriveKey(secret, L"HASH", NULL, NULL, 0, &size, 0); + ok(status == STATUS_SUCCESS, "got %#lx\n", status); - status = BCryptDestroyHash(secret); - ok(status == STATUS_INVALID_PARAMETER, "got %#lx\n", status); + status = BCryptDestroyHash(secret); + ok(status == STATUS_INVALID_PARAMETER, "got %#lx\n", status); - status = BCryptDestroyKey(secret); - ok(status == STATUS_INVALID_HANDLE, "got %#lx\n", status); + status = BCryptDestroyKey(secret); + ok(status == STATUS_INVALID_HANDLE, "got %#lx\n", status); - status = BCryptDestroySecret(NULL); - ok(status == STATUS_INVALID_HANDLE, "got %#lx\n", status); + status = BCryptDestroySecret(NULL); + ok(status == STATUS_INVALID_HANDLE, "got %#lx\n", status); - status = BCryptDestroySecret(alg); - ok(status == STATUS_INVALID_HANDLE, "got %#lx\n", status); + status = BCryptDestroySecret(alg); + ok(status == STATUS_INVALID_HANDLE, "got %#lx\n", status); - status = BCryptDestroySecret(secret); - ok(status == STATUS_SUCCESS, "got %#lx\n", status); + status = BCryptDestroySecret(secret); + ok(status == STATUS_SUCCESS, "got %#lx\n", status); - status = BCryptDestroyKey(key); - ok(status == STATUS_SUCCESS, "got %#lx\n", status); + status = BCryptDestroyKey(key); + ok(status == STATUS_SUCCESS, "got %#lx\n", status); - status = BCryptCloseAlgorithmProvider(alg, 0); - ok(status == STATUS_SUCCESS, "got %#lx\n", status); + status = BCryptCloseAlgorithmProvider(alg, 0); + ok(status == STATUS_SUCCESS, "got %#lx\n", status); + winetest_pop_context(); + } /* DH */ status = BCryptOpenAlgorithmProvider(&alg, BCRYPT_DH_ALGORITHM, NULL, 0); From 810a929febb0bbcd207cf1348ab4b50b0870a805 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 6 Aug 2025 12:14:10 -0600 Subject: [PATCH 326/454] bcrypt: Support ECDH_P521 algorithm. CW-Bug-Id: #25779 --- dlls/bcrypt/bcrypt_internal.h | 1 + dlls/bcrypt/bcrypt_main.c | 14 +++++++++- dlls/bcrypt/gnutls.c | 17 ++++++++++++ dlls/bcrypt/tests/bcrypt.c | 50 +++++++++++++++++++++++++++++++++++ include/bcrypt.h | 1 + 5 files changed, 82 insertions(+), 1 deletion(-) diff --git a/dlls/bcrypt/bcrypt_internal.h b/dlls/bcrypt/bcrypt_internal.h index ce0b904dd2c6..ea6acf5b448b 100644 --- a/dlls/bcrypt/bcrypt_internal.h +++ b/dlls/bcrypt/bcrypt_internal.h @@ -64,6 +64,7 @@ enum alg_id ALG_ID_DH, ALG_ID_ECDH_P256, ALG_ID_ECDH_P384, + ALG_ID_ECDH_P521, /* signature */ ALG_ID_RSA_SIGN, diff --git a/dlls/bcrypt/bcrypt_main.c b/dlls/bcrypt/bcrypt_main.c index 3b5746721d72..0d94ed8bc5b3 100644 --- a/dlls/bcrypt/bcrypt_main.c +++ b/dlls/bcrypt/bcrypt_main.c @@ -118,6 +118,7 @@ builtin_algorithms[] = { BCRYPT_DH_ALGORITHM, BCRYPT_SECRET_AGREEMENT_INTERFACE, 0, 0, 0 }, { BCRYPT_ECDH_P256_ALGORITHM, BCRYPT_SECRET_AGREEMENT_INTERFACE, 0, 0, 0 }, { BCRYPT_ECDH_P384_ALGORITHM, BCRYPT_SECRET_AGREEMENT_INTERFACE, 0, 0, 0 }, + { BCRYPT_ECDH_P521_ALGORITHM, BCRYPT_SECRET_AGREEMENT_INTERFACE, 0, 0, 0 }, { BCRYPT_RSA_SIGN_ALGORITHM, BCRYPT_SIGNATURE_INTERFACE, 0, 0, 0 }, { BCRYPT_ECDSA_P256_ALGORITHM, BCRYPT_SIGNATURE_INTERFACE, 0, 0, 0 }, { BCRYPT_ECDSA_P384_ALGORITHM, BCRYPT_SIGNATURE_INTERFACE, 0, 0, 0 }, @@ -248,7 +249,7 @@ static const struct algorithm pseudo_algorithms[] = {{ 0 }}, /* ECDH */ {{ MAGIC_ALG }, ALG_ID_ECDH_P256 }, {{ MAGIC_ALG }, ALG_ID_ECDH_P384 }, - {{ 0 }}, /* ECDH_P512 */ + {{ MAGIC_ALG }, ALG_ID_ECDH_P521 }, {{ MAGIC_ALG }, ALG_ID_DSA }, {{ MAGIC_ALG }, ALG_ID_ECDSA_P256 }, {{ MAGIC_ALG }, ALG_ID_ECDSA_P384 }, @@ -1753,6 +1754,11 @@ static NTSTATUS key_import_pair( struct algorithm *alg, const WCHAR *type, BCRYP magic = BCRYPT_ECDH_PUBLIC_P384_MAGIC; break; + case ALG_ID_ECDH_P521: + bitlen = 521; + magic = BCRYPT_ECDH_PUBLIC_P521_MAGIC; + break; + case ALG_ID_ECDSA_P256: bitlen = 256; magic = BCRYPT_ECDSA_PUBLIC_P256_MAGIC; @@ -1807,6 +1813,11 @@ static NTSTATUS key_import_pair( struct algorithm *alg, const WCHAR *type, BCRYP magic = BCRYPT_ECDH_PRIVATE_P384_MAGIC; break; + case ALG_ID_ECDH_P521: + bitlen = 521; + magic = BCRYPT_ECDH_PRIVATE_P521_MAGIC; + break; + case ALG_ID_ECDSA_P256: bitlen = 256; magic = BCRYPT_ECDSA_PRIVATE_P256_MAGIC; @@ -2197,6 +2208,7 @@ static const WCHAR *resolve_blob_type( const WCHAR *type, UCHAR *input, ULONG in { case BCRYPT_ECDH_PUBLIC_P256_MAGIC: case BCRYPT_ECDH_PUBLIC_P384_MAGIC: + case BCRYPT_ECDH_PUBLIC_P521_MAGIC: case BCRYPT_ECDSA_PUBLIC_P256_MAGIC: case BCRYPT_ECDSA_PUBLIC_P384_MAGIC: case BCRYPT_ECDSA_PUBLIC_P521_MAGIC: diff --git a/dlls/bcrypt/gnutls.c b/dlls/bcrypt/gnutls.c index fe3ab51af591..17adcb377841 100644 --- a/dlls/bcrypt/gnutls.c +++ b/dlls/bcrypt/gnutls.c @@ -1052,6 +1052,11 @@ static NTSTATUS key_export_ecc_public( struct key *key, UCHAR *buf, ULONG len, U size = 48; break; + case ALG_ID_ECDH_P521: + magic = BCRYPT_ECDH_PUBLIC_P521_MAGIC; + size = 66; + break; + case ALG_ID_ECDSA_P256: magic = BCRYPT_ECDSA_PUBLIC_P256_MAGIC; size = 32; @@ -1528,6 +1533,7 @@ static NTSTATUS key_asymmetric_generate( void *args ) bitlen = GNUTLS_CURVE_TO_BITS( GNUTLS_ECC_CURVE_SECP384R1 ); break; + case ALG_ID_ECDH_P521: case ALG_ID_ECDSA_P521: pk_alg = GNUTLS_PK_ECC; /* compatible with ECDSA and ECDH */ bitlen = GNUTLS_CURVE_TO_BITS( GNUTLS_ECC_CURVE_SECP521R1 ); @@ -1589,6 +1595,11 @@ static NTSTATUS key_export_ecc( struct key *key, UCHAR *buf, ULONG len, ULONG *r size = 48; break; + case ALG_ID_ECDH_P521: + magic = BCRYPT_ECDH_PRIVATE_P521_MAGIC; + size = 66; + break; + case ALG_ID_ECDSA_P256: magic = BCRYPT_ECDSA_PRIVATE_P256_MAGIC; size = 32; @@ -1661,6 +1672,7 @@ static NTSTATUS key_import_ecc( struct key *key, UCHAR *buf, ULONG len ) curve = GNUTLS_ECC_CURVE_SECP384R1; break; + case ALG_ID_ECDH_P521: case ALG_ID_ECDSA_P521: curve = GNUTLS_ECC_CURVE_SECP521R1; break; @@ -1930,6 +1942,7 @@ static NTSTATUS key_import_ecc_public( struct key *key, UCHAR *buf, ULONG len ) case ALG_ID_ECDSA_P384: curve = GNUTLS_ECC_CURVE_SECP384R1; break; + case ALG_ID_ECDH_P521: case ALG_ID_ECDSA_P521: curve = GNUTLS_ECC_CURVE_SECP521R1; break; @@ -2091,6 +2104,7 @@ static NTSTATUS key_asymmetric_export( void *args ) { case ALG_ID_ECDH_P256: case ALG_ID_ECDH_P384: + case ALG_ID_ECDH_P521: case ALG_ID_ECDSA_P256: case ALG_ID_ECDSA_P384: case ALG_ID_ECDSA_P521: @@ -2179,6 +2193,7 @@ static NTSTATUS key_asymmetric_import( void *args ) { case ALG_ID_ECDH_P256: case ALG_ID_ECDH_P384: + case ALG_ID_ECDH_P521: case ALG_ID_ECDSA_P256: case ALG_ID_ECDSA_P384: case ALG_ID_ECDSA_P521: @@ -2739,6 +2754,7 @@ static NTSTATUS dup_privkey( struct key *key_orig, struct key *key_copy ) } case ALG_ID_ECDH_P256: case ALG_ID_ECDH_P384: + case ALG_ID_ECDH_P521: case ALG_ID_ECDSA_P256: case ALG_ID_ECDSA_P384: case ALG_ID_ECDSA_P521: @@ -2804,6 +2820,7 @@ static NTSTATUS dup_pubkey( struct key *key_orig, struct key *key_copy ) } case ALG_ID_ECDH_P256: case ALG_ID_ECDH_P384: + case ALG_ID_ECDH_P521: case ALG_ID_ECDSA_P256: case ALG_ID_ECDSA_P384: case ALG_ID_ECDSA_P521: diff --git a/dlls/bcrypt/tests/bcrypt.c b/dlls/bcrypt/tests/bcrypt.c index 539b38ac8bb0..0314ed95a1e6 100644 --- a/dlls/bcrypt/tests/bcrypt.c +++ b/dlls/bcrypt/tests/bcrypt.c @@ -3334,6 +3334,50 @@ static void test_ECDH(void) 0xed, 0xdc, 0x16, 0x6c, }; + static BYTE ecc521privkey[] = + { + 0x45, 0x43, 0x4b, 0x36, 0x42, 0x00, 0x00, 0x00, + 0x01, 0x96, 0xb0, 0x4e, 0x35, 0x6f, 0xbe, 0x00, 0xb6, 0xc3, 0x83, 0x53, 0x92, 0x18, 0xda, 0x86, + 0x9e, 0x4b, 0x0f, 0xb2, 0x0b, 0xc3, 0x9f, 0xd8, 0x9c, 0x18, 0x8a, 0x93, 0x5c, 0x91, 0xb2, 0x4f, + 0x56, 0x7d, 0x0e, 0xf7, 0xf4, 0xdf, 0x91, 0xc6, 0x74, 0x00, 0xc7, 0xb8, 0x59, 0xeb, 0x55, 0xc0, + 0xb5, 0x26, 0x7f, 0x6d, 0x49, 0x53, 0x02, 0x3b, 0x3c, 0xa0, 0x57, 0x1e, 0x1c, 0x7c, 0x5b, 0x08, + 0x23, 0x68, 0x01, 0x47, 0x4d, 0x47, 0xcf, 0x05, 0xfe, 0x18, 0x26, 0x81, 0x9d, 0xb4, 0x34, 0xfa, + 0x50, 0x7e, 0x03, 0x29, 0xa3, 0x6e, 0x90, 0x9c, 0x27, 0x69, 0x66, 0x2c, 0x70, 0x7b, 0xf9, 0xe7, + 0xef, 0xac, 0x27, 0xbf, 0x15, 0x86, 0xf6, 0xff, 0x2d, 0x99, 0x41, 0x9e, 0x36, 0x1f, 0xe9, 0x3a, + 0x99, 0x74, 0x54, 0xf3, 0xc3, 0x08, 0xb1, 0x00, 0x28, 0x84, 0x82, 0x84, 0xe3, 0xf4, 0x32, 0xfd, + 0x48, 0x67, 0xae, 0x08, 0x01, 0xe2, 0x08, 0x1d, 0xeb, 0x27, 0xc2, 0x98, 0x45, 0x8b, 0x33, 0x20, + 0x3b, 0x21, 0x5c, 0x7f, 0x56, 0xbd, 0xa5, 0x99, 0x58, 0xea, 0x19, 0xf8, 0xbc, 0xf1, 0x9e, 0x39, + 0x00, 0xb9, 0x2c, 0x2a, 0xb6, 0x19, 0x3a, 0xaf, 0xea, 0x4b, 0xa6, 0x22, 0xb4, 0x35, 0x09, 0x86, + 0x2e, 0x67, 0xe0, 0xfe, 0x81, 0x0e, 0x6a, 0x68, 0x6a, 0xb3, 0x32, 0x3b, 0xf8, 0x89, 0x45, 0x72, + 0x69, 0xf1, 0xe1, 0x84, 0xd7, 0xed, + }; + static BYTE ecdh521_pubkey[] = + { + 0x45, 0x43, 0x4b, 0x35, 0x42, 0x00, 0x00, 0x00, + 0x00, 0xfd, 0x72, 0xca, 0x31, 0x08, 0x76, 0xd9, 0x08, 0xb7, 0x26, 0x4a, 0x04, 0xbc, 0x73, 0x1a, + 0x04, 0xbb, 0x77, 0xf4, 0xe2, 0xfc, 0x3a, 0x88, 0x0a, 0xa8, 0x72, 0x8f, 0xfc, 0xe9, 0xe3, 0x5f, + 0x73, 0x05, 0xf1, 0x7f, 0x31, 0xee, 0x15, 0x91, 0x36, 0xe9, 0xeb, 0xed, 0xbe, 0x0e, 0x78, 0xa1, + 0x28, 0x4e, 0xc5, 0xcb, 0xba, 0xd8, 0x0c, 0x96, 0x75, 0x44, 0x71, 0x71, 0x00, 0x41, 0x43, 0xdf, + 0x27, 0x91, 0x00, 0x81, 0xcc, 0x56, 0x8d, 0x8e, 0x32, 0xae, 0x78, 0xfb, 0x3e, 0x84, 0x7b, 0x3b, + 0xf8, 0x8e, 0x7b, 0x27, 0x73, 0xa4, 0x11, 0x61, 0x24, 0x40, 0x9b, 0xbe, 0xd3, 0xc3, 0x0e, 0xbb, + 0x26, 0xae, 0x55, 0x58, 0xd1, 0xec, 0x14, 0xd3, 0x13, 0xf2, 0x6b, 0x2b, 0xc3, 0xf9, 0xa4, 0x50, + 0x9e, 0xce, 0xfe, 0x18, 0xa0, 0x8b, 0x10, 0x80, 0x68, 0x72, 0x2e, 0x5c, 0xa0, 0xb3, 0x01, 0x6b, + 0x43, 0x26, 0xad, 0x58, + }; + static BYTE ecdh521_secret[] = + { + 0x2d, 0xab, 0x9f, 0x38, 0x14, 0x5d, 0x49, 0x65, 0x06, 0x22, 0x04, 0xf9, 0xb3, 0x25, 0xca, 0xf9, + 0xe6, 0xdc, 0xc6, 0xe8, 0xf9, 0x0b, 0xbf, 0x3c, 0x9d, 0xf0, 0x08, 0x95, 0x92, 0xdd, 0x92, 0x8a, + 0x95, 0x85, 0xe8, 0x1c, 0x6d, 0x41, 0xb9, 0xe4, 0x7b, 0x7a, 0xa5, 0x68, 0x30, 0x48, 0x8e, 0xc0, + 0xbc, 0x2b, 0xc6, 0xe9, 0x75, 0x9c, 0xed, 0xc3, 0xf5, 0x3a, 0xa3, 0xd7, 0x41, 0xec, 0x28, 0x82, + 0xef, 0x01, + }; + static BYTE hashed521_secret[] = + { + 0x72, 0x39, 0x73, 0x5f, 0xc9, 0x26, 0x1f, 0x8e, 0xe3, 0x30, 0x11, 0xe1, 0x4f, 0xc4, 0x65, 0xc0, + 0xde, 0xf9, 0xe6, 0x6a, + }; + static const struct ecdh_test tests[] = { { @@ -3346,6 +3390,11 @@ static void test_ECDH(void) ecdh384_secret, sizeof(ecdh384_secret), hashed384_secret, BCRYPT_ECDH_PUBLIC_P384_MAGIC, BCRYPT_ECDH_PRIVATE_P384_MAGIC, }, + { + BCRYPT_ECDH_P521_ALGORITHM, 521, ecc521privkey, sizeof(ecc521privkey), ecdh521_pubkey, sizeof(ecdh521_pubkey), + ecdh521_secret, sizeof(ecdh521_secret), hashed521_secret, + BCRYPT_ECDH_PUBLIC_P521_MAGIC, BCRYPT_ECDH_PRIVATE_P521_MAGIC, + }, }; unsigned int i; @@ -4183,6 +4232,7 @@ static void test_SecretAgreement(void) { { BCRYPT_ECDH_P256_ALGORITHM, 256 }, { BCRYPT_ECDH_P384_ALGORITHM, 384 }, + { BCRYPT_ECDH_P521_ALGORITHM, 521 }, }; static const ULONG length = 1024; BCRYPT_DH_PARAMETER_HEADER *dh_header; diff --git a/include/bcrypt.h b/include/bcrypt.h index b7d6c1614671..bb6ed5c16254 100644 --- a/include/bcrypt.h +++ b/include/bcrypt.h @@ -91,6 +91,7 @@ typedef LONG NTSTATUS; #define BCRYPT_DSA_ALGORITHM L"DSA" #define BCRYPT_ECDH_P256_ALGORITHM L"ECDH_P256" #define BCRYPT_ECDH_P384_ALGORITHM L"ECDH_P384" +#define BCRYPT_ECDH_P521_ALGORITHM L"ECDH_P521" #define BCRYPT_ECDSA_P256_ALGORITHM L"ECDSA_P256" #define BCRYPT_ECDSA_P384_ALGORITHM L"ECDSA_P384" #define BCRYPT_ECDSA_P521_ALGORITHM L"ECDSA_P521" From 5b4ff85400958c991e75fbb05a25220826cfe635 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 6 Aug 2025 13:34:35 -0600 Subject: [PATCH 327/454] bcrypt: Support for ECDH_P521 through gcrypt. CW-Bug-Id: #25779 --- dlls/bcrypt/gnutls.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/dlls/bcrypt/gnutls.c b/dlls/bcrypt/gnutls.c index 17adcb377841..59af50b8903d 100644 --- a/dlls/bcrypt/gnutls.c +++ b/dlls/bcrypt/gnutls.c @@ -3449,6 +3449,7 @@ static NTSTATUS key_asymmetric_derive_key( void *args ) case ALG_ID_ECDH_P256: case ALG_ID_ECDH_P384: + case ALG_ID_ECDH_P521: /* this is necessary since GNUTLS doesn't support ECDH public key encryption, maybe we can replace this when it does: https://github.com/gnutls/gnutls/blob/cdc4fc288d87f91f974aa23b6e8595a53970ce00/lib/nettle/pk.c#L495 */ #if defined(HAVE_GCRYPT_H) && defined(SONAME_LIBGCRYPT) @@ -3481,9 +3482,14 @@ static NTSTATUS key_asymmetric_derive_key( void *args ) pubkey_format = "NIST P-384"; key_length = 48; } + else if (priv_key->alg_id == ALG_ID_ECDH_P521) + { + pubkey_format = "NIST P-521"; + key_length = 66; + } else return STATUS_NOT_IMPLEMENTED; - if (key_length != priv_key->u.a.bitlen / 8) + if (key_length != len_from_bitlen( priv_key->u.a.bitlen )) { ERR( "Key length mismatch, key->u.a.bitlen %u, key_length %u.\n", (int)priv_key->u.a.bitlen, (int)key_length ); From 8f056505fd8d6ec439f589278f47c6a94e11d5c0 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 6 Aug 2025 14:36:01 -0600 Subject: [PATCH 328/454] bcrypt: Check output size early in key_asymmetric_encrypt() for RSA. CW-Bug-Id: #25779 --- dlls/bcrypt/gnutls.c | 6 ++++++ dlls/bcrypt/tests/bcrypt.c | 32 +++++++++++++++++++++++++++++--- 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/dlls/bcrypt/gnutls.c b/dlls/bcrypt/gnutls.c index 59af50b8903d..eb4189a4ca88 100644 --- a/dlls/bcrypt/gnutls.c +++ b/dlls/bcrypt/gnutls.c @@ -3276,6 +3276,12 @@ static NTSTATUS key_asymmetric_encrypt( void *args ) if (!key_data(params->key)->a.pubkey) return STATUS_INVALID_HANDLE; + if (params->key->alg_id == ALG_ID_RSA + && (!params->output || len_from_bitlen( params->key->u.a.bitlen ) > params->output_len)) + { + *params->ret_len = len_from_bitlen( params->key->u.a.bitlen ); + return !params->output ? STATUS_SUCCESS : STATUS_BUFFER_TOO_SMALL; + } if (gcrypt_available && (params->flags == BCRYPT_PAD_NONE || params->flags == BCRYPT_PAD_OAEP)) return key_asymmetric_encrypt_gcrypt( args ); diff --git a/dlls/bcrypt/tests/bcrypt.c b/dlls/bcrypt/tests/bcrypt.c index 0314ed95a1e6..a990750b4624 100644 --- a/dlls/bcrypt/tests/bcrypt.c +++ b/dlls/bcrypt/tests/bcrypt.c @@ -2613,6 +2613,12 @@ static void test_rsa_encrypt(void) /* No padding */ memset(input_no_padding, 0, sizeof(input_no_padding)); + + encrypted_size = 0; + ret = BCryptEncrypt(key, input_no_padding, sizeof(input_no_padding), NULL, NULL, 0, NULL, 0, &encrypted_size, BCRYPT_PAD_NONE); + ok(ret == STATUS_SUCCESS, "got %lx\n", ret); + ok(encrypted_size == 64, "got size of %ld\n", encrypted_size); + strcpy((char *)input_no_padding, "Hello World"); encrypted_size = 0; ret = BCryptEncrypt(key, input_no_padding, sizeof(input_no_padding), NULL, NULL, 0, NULL, 0, &encrypted_size, BCRYPT_PAD_NONE); @@ -2712,20 +2718,40 @@ static void test_rsa_encrypt(void) ok(ret == STATUS_SUCCESS, "got %lx\n", ret); ok(encrypted_size == 80, "got size of %ld\n", encrypted_size); + encrypted_size = 0; + ret = BCryptEncrypt(key, input, sizeof(input), NULL, NULL, 0, NULL, 0, &encrypted_size, BCRYPT_PAD_OAEP); + ok(ret == STATUS_SUCCESS, "got %lx\n", ret); + ok(encrypted_size == 80, "got size of %ld\n", encrypted_size); + encrypted_a = realloc(encrypted_a, encrypted_size * 2); memset(encrypted_a, 0, encrypted_size * 2); encrypted_b = realloc(encrypted_b, encrypted_size * 2); memset(encrypted_b, 0, encrypted_size); - ret = BCryptEncrypt(key, input, sizeof(input), &oaep_pad, NULL, 0, encrypted_a, encrypted_size * 2, &encrypted_size, BCRYPT_PAD_OAEP); + ret = BCryptEncrypt(key, input, sizeof(input), NULL, NULL, 0, NULL, encrypted_size, &encrypted_size, BCRYPT_PAD_OAEP); ok(ret == STATUS_SUCCESS, "got %lx\n", ret); ok(encrypted_size == 80, "got size of %ld\n", encrypted_size); + encrypted_size = 0; + ret = BCryptEncrypt(key, input, sizeof(input), NULL, NULL, 0, encrypted_a, 0, &encrypted_size, BCRYPT_PAD_OAEP); + ok(ret == STATUS_BUFFER_TOO_SMALL, "got %lx\n", ret); + ok(encrypted_size == 80, "got size of %ld\n", encrypted_size); + + ret = BCryptEncrypt(key, input, sizeof(input), &oaep_pad, NULL, 0, encrypted_a, encrypted_size, &encrypted_size, BCRYPT_PAD_OAEP); + todo_wine ok(ret == STATUS_SUCCESS, "got %lx\n", ret); + ok(encrypted_size == 80, "got size of %ld\n", encrypted_size); + ret = BCryptEncrypt(key, input, sizeof(input), &oaep_pad, NULL, 0, encrypted_b, encrypted_size, &encrypted_size, BCRYPT_PAD_OAEP); - ok(ret == STATUS_SUCCESS, "got %lx\n", ret); + todo_wine ok(ret == STATUS_SUCCESS, "got %lx\n", ret); ok(encrypted_size == 80, "got size of %ld\n", encrypted_size); - ok(memcmp(encrypted_a, encrypted_b, encrypted_size), "Both outputs are the same\n"); + todo_wine ok(memcmp(encrypted_a, encrypted_b, encrypted_size), "Both outputs are the same\n"); + + decrypted_size = 0; + memset(decrypted, 0, sizeof(decrypted)); + ret = BCryptDecrypt(key, encrypted_a, encrypted_size, NULL, NULL, 0, NULL, 0, &decrypted_size, BCRYPT_PAD_OAEP); + ok(ret == STATUS_INVALID_PARAMETER, "got %lx\n", ret); + todo_wine { decrypted_size = 0; memset(decrypted, 0, sizeof(decrypted)); ret = BCryptDecrypt(key, encrypted_a, encrypted_size, &oaep_pad, NULL, 0, NULL, 0, &decrypted_size, BCRYPT_PAD_OAEP); From 5c54664c804ac74fa01a844e6d48645f8a7228d4 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 6 Aug 2025 15:02:24 -0600 Subject: [PATCH 329/454] kernelbase: Don't redirect BE launcher when run from builtin one. CW-Bug-Id: #25758 --- dlls/kernelbase/process.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/dlls/kernelbase/process.c b/dlls/kernelbase/process.c index 7a51dfd231d8..93554095a480 100644 --- a/dlls/kernelbase/process.c +++ b/dlls/kernelbase/process.c @@ -559,6 +559,12 @@ static int battleye_launcher_redirect_hack( const WCHAR *app_name, WCHAR *new_na static const WCHAR belauncherW[] = L"c:\\windows\\system32\\belauncher.exe"; unsigned int len; + if (GetEnvironmentVariableW(L"PROTON_ORIG_LAUNCHER_NAME", NULL, 0)) + { + /* run from builtin belauncher. */ + return 0; + } + /* We detect the BattlEye launcher executable through the product name property, as the executable name varies */ if (!product_name_matches( app_name, "BattlEye Launcher" )) return 0; From 73a2ec085b134341dbcb9de07af5b94a4235c518 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 6 Aug 2025 15:29:21 -0600 Subject: [PATCH 330/454] belauncher: Start original launcher process. CW-Bug-Id: #25758 --- programs/belauncher/main.c | 36 +++++++++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/programs/belauncher/main.c b/programs/belauncher/main.c index d48169cbe6cb..a2b0666ad916 100644 --- a/programs/belauncher/main.c +++ b/programs/belauncher/main.c @@ -11,14 +11,15 @@ WINE_DEFAULT_DEBUG_CHANNEL(belauncher); int WINAPI wWinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPWSTR cmdline, int cmdshow) { char *configs, *config, *arch_32_exe = NULL, *arch_64_exe = NULL, *game_exe = NULL, *be_arg = NULL; - WCHAR path[MAX_PATH], *p, config_path[MAX_PATH], game_exeW[MAX_PATH], **argvW; + WCHAR orig_path[MAX_PATH], path[MAX_PATH], *p, config_path[MAX_PATH], game_exeW[MAX_PATH], **argvW; LARGE_INTEGER launcher_cfg_size; unsigned char battleye_status; int game_exe_len, arg_len, path_len; PROCESS_INFORMATION pi; HANDLE launcher_cfg; LPWSTR launch_cmd; - STARTUPINFOW si = {0}; + STARTUPINFOW si = { sizeof(si) }; + HANDLE orig_launcher_process = NULL; int i, argc; DWORD size; BOOL wow64; @@ -27,8 +28,11 @@ int WINAPI wWinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPWSTR cmdline, int cm _write(1, &battleye_status, 1); *path = 0; - if ((size = GetEnvironmentVariableW(L"PROTON_ORIG_LAUNCHER_NAME", path, ARRAY_SIZE(path))) && size <= ARRAY_SIZE(path)) + *orig_path = 0; + if ((size = GetEnvironmentVariableW(L"PROTON_ORIG_LAUNCHER_NAME", orig_path, ARRAY_SIZE(orig_path))) + && size <= ARRAY_SIZE(orig_path)) { + wcscpy(path, orig_path); WINE_TRACE("PROTON_ORIG_LAUNCHER_NAME %s.\n", wine_dbgstr_w(path)); for (p = path + wcslen(path); p != path; --p) @@ -147,6 +151,20 @@ int WINAPI wWinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPWSTR cmdline, int cm arg_len = MultiByteToWideChar(CP_ACP, 0, be_arg, -1, NULL, 0) - 1; } + if (*orig_path) + { + WINE_TRACE("Launching original launcher %s.\n", debugstr_w(orig_path)); + if (!CreateProcessW(orig_path, NULL, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi)) + { + DWORD err = GetLastError(); + + WINE_ERR("CreateProcessW failed for original launcher %s, err %lu.\n", debugstr_w(orig_path), err); + return err; + } + CloseHandle(pi.hThread); + orig_launcher_process = pi.hProcess; + } + WINE_TRACE("Launching game executable %s for BattlEye.\n", game_exe); battleye_status = 0x9; /* Launching Game */ _write(1, &battleye_status, 1); @@ -175,15 +193,23 @@ int WINAPI wWinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPWSTR cmdline, int cm if (!CreateProcessW(NULL, launch_cmd, NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi)) { - WINE_ERR("CreateProcessW failed.\n"); + DWORD err = GetLastError(); + + WINE_ERR("CreateProcessW failed, error %lu.\n", err); + if (orig_launcher_process) + TerminateProcess(orig_launcher_process, err); battleye_status = 0xA; /* Launch Failed */ _write(1, &battleye_status, 1); - return GetLastError(); + return err; } HeapFree( GetProcessHeap(), 0, launch_cmd ); + CloseHandle(pi.hThread); WaitForSingleObject(pi.hProcess, INFINITE); CloseHandle(pi.hProcess); + + if (orig_launcher_process) + TerminateProcess(orig_launcher_process, 0); return 0; start_failed: From 6aa25d09d6f7f71044a923a9056037d7c4bfcf05 Mon Sep 17 00:00:00 2001 From: Zhiyi Zhang Date: Thu, 7 Aug 2025 11:01:12 +0800 Subject: [PATCH 331/454] winex11.drv: Limit fullscreen visible rects to the virtual screen rect. Fix a regression from 31fda1f4, which allowed the visible rect to be larger than the monitor rect. After 31fda1f4, CHRONO TRIGGER (613830) sets a window rect slightly larger than the monitor rect and will change the window rect to the rect it previously set if the game detects a different window rect. Adding __NET_WM_STATE_FULLSCREEN will cause WMs to move the window to cover exactly the monitor rect. So the window rect will be repeatedly changed by the WM and the game, causing a flickering effect. Limit fullscreen visible rects to the virtual screen rect so that the visible rects in winex11.drv are of the same size as the monitor rect. Thus, adding __NET_WM_STATE_FULLSCREEN won't trigger a size change. CW-Bug-Id: #25747 --- dlls/winex11.drv/window.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index 0adcc76753dd..9c085ccc5f4a 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -3473,8 +3473,23 @@ void X11DRV_WindowPosChanged( HWND hwnd, HWND insert_after, HWND owner_hint, UIN { struct x11drv_win_data *data; UINT new_style = NtUserGetWindowLongW( hwnd, GWL_STYLE ), old_style; - struct window_rects old_rects; + struct window_rects old_rects, tmp_rects; BOOL was_fullscreen, activate = !(swp_flags & SWP_NOACTIVATE); + RECT virtual; + + /* If the visible rect is fullscreen on any one of the monitors, limit the visible rect to the + * virtual screen rect. This is needed because adding __NET_WM_STATE_FULLSCREEN will make WMs + * move the window to cover exactly the monitor rect. If the application sets a visible rect + * slightly larger than the monitor rect and insists on changing to the rect that it previously + * set when the rect is changed by the WM, then the window rect will be repeatedly changed by + * the WM and the application, causing a flickering effect */ + if (fullscreen) + { + virtual = NtUserGetVirtualScreenRect( MDT_RAW_DPI ); + tmp_rects = *new_rects; + intersect_rect( &tmp_rects.visible, &tmp_rects.visible, &virtual ); + new_rects = &tmp_rects; + } set_surface_window_rects( surface, new_rects ); From 73aebc421479578c56227f3141f3687ec5842077 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 8 Aug 2025 18:23:15 -0600 Subject: [PATCH 332/454] nsiproxy.sys: Implement IP interface table. CW-Bug-Id: #25798 --- dlls/nsiproxy.sys/ip.c | 195 +++++++++++++++++++++++++++++++ dlls/nsiproxy.sys/unix_private.h | 23 ++++ include/wine/nsi.h | 42 +++++++ 3 files changed, 260 insertions(+) diff --git a/dlls/nsiproxy.sys/ip.c b/dlls/nsiproxy.sys/ip.c index 32ba62417610..098abbd11629 100644 --- a/dlls/nsiproxy.sys/ip.c +++ b/dlls/nsiproxy.sys/ip.c @@ -810,6 +810,183 @@ static NTSTATUS ipv6_ipstats_get_all_parameters( const void *key, UINT key_size, #endif } +static NTSTATUS ip_interface_fill( UINT fam, const char *unix_name, void *key_data, UINT key_size, + void *rw_data, UINT rw_size, void *dynamic_data, UINT dynamic_size, + void *static_data, UINT static_size, UINT_PTR *count ) +{ + BOOL want_data = key_size || rw_size || dynamic_size || static_size; + int base_reachable_time, dad_transmits, site_prefix_len; + struct nsi_ip_interface_dynamic *dyn = dynamic_data; + struct nsi_ip_interface_static *stat = static_data; + struct nsi_ndis_ifinfo_dynamic iface_dynamic; + struct nsi_ip_interface_key *key = key_data; + struct nsi_ndis_ifinfo_static iface_static; + struct ipv6_addr_scope *addr_scopes = NULL; + unsigned int addr_scopes_size = 0; + struct nsi_ip_interface_rw *rw = rw_data; + struct ifaddrs *addrs, *entry, *entry2; + UINT num = 0, scope_id = 0xffffffff; + char path[256]; + NET_LUID luid; + + if (getifaddrs( &addrs )) return STATUS_NO_MORE_ENTRIES; + + if (fam == AF_INET6) addr_scopes = get_ipv6_addr_scope_table( &addr_scopes_size ); + + rw = rw_data; + for (entry = addrs; entry; entry = entry->ifa_next) + { + if (!entry->ifa_addr || entry->ifa_addr->sa_family != fam) continue; + if (unix_name && strcmp( entry->ifa_name, unix_name )) continue; + if (fam == AF_INET6) + { + scope_id = find_ipv6_addr_scope( (IN6_ADDR*)&((struct sockaddr_in6 *)entry->ifa_addr)->sin6_addr, addr_scopes, + addr_scopes_size ); + /* Info in IP interface table entry correspons to link local IPV6 address, while reported info for loopback + * is different on Windows. */ + if (scope_id != 0xffffffff && scope_id != 0x1000 /* loopback */ && scope_id != 0x2000 /* link_local */) + continue; + } + if (!unix_name) + { + /* getifaddrs may return multipe entries for the same interface having different IP addresses. + * IP interface table being returned has only one entry per network interface. */ + for (entry2 = addrs; entry2 && entry2 != entry; entry2 = entry2->ifa_next) + { + if (!entry2->ifa_addr || entry2->ifa_addr->sa_family != fam) continue; + if (fam == AF_INET6) + { + scope_id = find_ipv6_addr_scope( (IN6_ADDR*)&((struct sockaddr_in6 *)entry2->ifa_addr)->sin6_addr, + addr_scopes, addr_scopes_size ); + if (scope_id != 0xffffffff && scope_id != 0x1000 /* loopback */ + && scope_id != 0x2000 /* link_local */) continue; + } + if (!strcmp( entry2->ifa_name, entry->ifa_name )) + break; + } + if (entry2 != entry) continue; + } + if (!convert_unix_name_to_luid( entry->ifa_name, &luid )) continue; + if (!nsi_get_all_parameters( &NPI_MS_NDIS_MODULEID, NSI_NDIS_IFINFO_TABLE, &luid, sizeof(luid), + NULL, 0, &iface_dynamic, sizeof(iface_dynamic), + &iface_static, sizeof(iface_static) )) + { + ERR( "Could not get iface parameters.\n" ); + continue; + } + + if (!count || num < *count) + { + if (key && count) memcpy( key, &luid, sizeof(luid) ); + if (stat) memset( stat, 0, sizeof(*stat) ); + + base_reachable_time = 0; + if (iface_static.type == MIB_IF_TYPE_LOOPBACK) dad_transmits = 0; + else dad_transmits = (fam == AF_INET6) ? 1 : 3; +#if __linux__ + if (rw || dyn) + { + sprintf( path, "/proc/sys/net/%s/neigh/%s/base_reachable_time_ms", + (fam == AF_INET) ? "ipv4" : "ipv6", entry->ifa_name); + read_sysctl_int( path, &base_reachable_time ); + } + if (rw) + { + if (fam == AF_INET6 && iface_static.type != MIB_IF_TYPE_LOOPBACK) + { + sprintf( path, "/proc/sys/net/ipv6/conf/%s/dad_transmits", entry->ifa_name); + read_sysctl_int( path, &dad_transmits ); + } + } +#endif + if (rw) + { + site_prefix_len = 64; + if (fam == AF_INET6 && iface_static.type != MIB_IF_TYPE_LOOPBACK) + { + /* For some reason prefix length reported on ipv4 is 64 for ipv4 addresses on Windows and + * prefix len is 64 for loopback device. */ + site_prefix_len = mask_v6_to_prefix( &((struct sockaddr_in6 *)entry->ifa_netmask)->sin6_addr ); + } + memset( rw, 0, sizeof(*rw) ); + rw->mtu = iface_dynamic.mtu; + rw->site_prefix_len = site_prefix_len; + rw->base_reachable_time = base_reachable_time; + rw->dad_transmits = dad_transmits; + rw->retransmit_time = 1000; + rw->path_mtu_discovery_timeout = 600000; + rw->link_local_address_behavior = (fam == AF_INET6) ? LinkLocalAlwaysOn : LinkLocalDelayed; + rw->link_local_address_timeout = (fam == AF_INET6) ? 0 : 6500; + } + if (dyn) + { + memset( dyn, 0, sizeof(*dyn) ); + dyn->if_index = iface_static.if_index; + dyn->connected = (iface_dynamic.media_conn_state == MediaConnectStateConnected); + dyn->reachable_time = base_reachable_time; + } + } + if (!count) + { + freeifaddrs( addrs ); + free( addr_scopes ); + return STATUS_SUCCESS; + } + ++num; + if (key) ++key; + if (rw) ++rw; + if (dyn) ++dyn; + if (stat) ++stat; + } + freeifaddrs( addrs ); + free( addr_scopes ); + + if (!count) return STATUS_NOT_FOUND; + if (want_data && num > *count) return STATUS_BUFFER_OVERFLOW; + *count = num; + return STATUS_SUCCESS; +} + +static NTSTATUS ipv4_interface_enumerate_all( void *key_data, UINT key_size, void *rw_data, UINT rw_size, + void *dynamic_data, UINT dynamic_size, + void *static_data, UINT static_size, UINT_PTR *count ) +{ + return ip_interface_fill( AF_INET, NULL, key_data, key_size, rw_data, rw_size, dynamic_data, dynamic_size, + static_data, static_size, count ); +} + +static NTSTATUS ipv4_interface_get_all_parameters( const void *key, UINT key_size, void *rw_data, UINT rw_size, + void *dynamic_data, UINT dynamic_size, void *static_data, UINT static_size ) +{ + struct nsi_ip_interface_key *ip_key = (void *)key; + const char *unix_name; + + if (!convert_luid_to_unix_name( &ip_key->luid, &unix_name )) return STATUS_NOT_FOUND; + + return ip_interface_fill( AF_INET, unix_name, ip_key, key_size, rw_data, rw_size, dynamic_data, dynamic_size, + static_data, static_size, NULL ); +} + +static NTSTATUS ipv6_interface_enumerate_all( void *key_data, UINT key_size, void *rw_data, UINT rw_size, + void *dynamic_data, UINT dynamic_size, + void *static_data, UINT static_size, UINT_PTR *count ) +{ + return ip_interface_fill( AF_INET6, NULL, key_data, key_size, rw_data, rw_size, dynamic_data, dynamic_size, + static_data, static_size, count ); +} + +static NTSTATUS ipv6_interface_get_all_parameters( const void *key, UINT key_size, void *rw_data, UINT rw_size, + void *dynamic_data, UINT dynamic_size, void *static_data, UINT static_size ) +{ + struct nsi_ip_interface_key *ip_key = (void *)key; + const char *unix_name; + + if (!convert_luid_to_unix_name( &ip_key->luid, &unix_name )) return STATUS_NOT_FOUND; + + return ip_interface_fill( AF_INET6, unix_name, ip_key, key_size, rw_data, rw_size, dynamic_data, dynamic_size, + static_data, static_size, NULL ); +} + static void unicast_fill_entry( struct ifaddrs *entry, void *key, struct nsi_ip_unicast_rw *rw, struct nsi_ip_unicast_dynamic *dyn, struct nsi_ip_unicast_static *stat ) { @@ -1660,6 +1837,15 @@ static struct module_table ipv4_tables[] = NULL, ipv4_ipstats_get_all_parameters, }, + { + NSI_IP_INTERFACE_TABLE, + { + sizeof(struct nsi_ip_interface_key), sizeof(struct nsi_ip_interface_rw), + sizeof(struct nsi_ip_interface_dynamic), sizeof(struct nsi_ip_interface_static) + }, + ipv4_interface_enumerate_all, + ipv4_interface_get_all_parameters, + }, { NSI_IP_UNICAST_TABLE, { @@ -1725,6 +1911,15 @@ static struct module_table ipv6_tables[] = NULL, ipv6_ipstats_get_all_parameters, }, + { + NSI_IP_INTERFACE_TABLE, + { + sizeof(struct nsi_ip_interface_key), sizeof(struct nsi_ip_interface_rw), + sizeof(struct nsi_ip_interface_dynamic), sizeof(struct nsi_ip_interface_static) + }, + ipv6_interface_enumerate_all, + ipv6_interface_get_all_parameters, + }, { NSI_IP_UNICAST_TABLE, { diff --git a/dlls/nsiproxy.sys/unix_private.h b/dlls/nsiproxy.sys/unix_private.h index b7c56710e222..5487de5111d8 100644 --- a/dlls/nsiproxy.sys/unix_private.h +++ b/dlls/nsiproxy.sys/unix_private.h @@ -98,6 +98,29 @@ static inline BOOL convert_index_to_luid( UINT index, NET_LUID *luid ) return !nsi_get_parameter_ex( ¶ms ); } +static inline BOOL nsi_get_all_parameters( const NPI_MODULEID *module, UINT table, + void *key_data, UINT key_size, void *rw_data, UINT rw_size, + void *dynamic_data, UINT dynamic_size, void *static_data, UINT static_size ) +{ + struct nsi_get_all_parameters_ex params; + + params.unknown[0] = 0; + params.unknown[1] = 0; + params.module = module; + params.table = table; + params.first_arg = 1; + params.unknown2 = 0; + params.key = key_data; + params.key_size = key_size; + params.rw_data = rw_data; + params.rw_size = rw_size; + params.dynamic_data = dynamic_data; + params.dynamic_size = dynamic_size; + params.static_data = static_data; + params.static_size = static_size; + return !nsi_get_all_parameters_ex( ¶ms ); +} + struct ipv6_addr_scope { IN6_ADDR addr; diff --git a/include/wine/nsi.h b/include/wine/nsi.h index 6bf3ce5bd5de..0df313db624e 100644 --- a/include/wine/nsi.h +++ b/include/wine/nsi.h @@ -24,6 +24,7 @@ #include "ws2def.h" #include "ws2ipdef.h" #include "winioctl.h" +#include "nldef.h" /* Undocumented NSI NDIS tables */ #define NSI_NDIS_IFINFO_TABLE 0 @@ -101,6 +102,7 @@ struct nsi_ndis_ifinfo_static #define NSI_IP_COMPARTMENT_TABLE 2 #define NSI_IP_ICMPSTATS_TABLE 3 #define NSI_IP_IPSTATS_TABLE 6 +#define NSI_IP_INTERFACE_TABLE 7 #define NSI_IP_UNICAST_TABLE 10 #define NSI_IP_NEIGHBOUR_TABLE 11 #define NSI_IP_FORWARD_TABLE 16 @@ -293,6 +295,46 @@ struct nsi_ip_forward_static UINT if_index; }; +struct nsi_ip_interface_key +{ + NET_LUID luid; +}; + +struct nsi_ip_interface_rw +{ + UINT32 unk1[5]; + UINT router_discovery_behaviour; + UINT32 unk2; + ULONG metric; + ULONG base_reachable_time; + ULONG retransmit_time; + ULONG path_mtu_discovery_timeout; + ULONG dad_transmits; + UINT link_local_address_behavior; + ULONG link_local_address_timeout; + ULONG zone_indices[ScopeLevelCount]; + ULONG mtu; + ULONG site_prefix_len; + UINT32 unk3[28]; +}; + +struct nsi_ip_interface_dynamic +{ + UINT if_index; + UINT unk1; + UINT supports_wakeup_patterns; + ULONG reachable_time; + UINT connected; + UINT unk2[3]; + NL_INTERFACE_OFFLOAD_ROD transmit_offload; + UINT32 unk3[13]; +}; + +struct nsi_ip_interface_static +{ + UINT32 unk[8]; +}; + /* Undocumented NSI TCP tables */ #define NSI_TCP_STATS_TABLE 0 #define NSI_TCP_ALL_TABLE 3 From c09c80c7e8e29c60a70e2333421a241b1d3e29a9 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 11 Aug 2025 15:39:56 -0600 Subject: [PATCH 333/454] iphlpapi: Implement GetIpInterfaceTable(). CW-Bug-Id: #25798 --- dlls/iphlpapi/iphlpapi_main.c | 92 ++++++++++++++++++++++++++++++++-- dlls/iphlpapi/tests/iphlpapi.c | 59 ++++++++++++++++++++++ 2 files changed, 148 insertions(+), 3 deletions(-) diff --git a/dlls/iphlpapi/iphlpapi_main.c b/dlls/iphlpapi/iphlpapi_main.c index 78000a628b74..4c34318b2ce0 100644 --- a/dlls/iphlpapi/iphlpapi_main.c +++ b/dlls/iphlpapi/iphlpapi_main.c @@ -4530,13 +4530,99 @@ char *WINAPI IPHLP_if_indextoname( NET_IFINDEX index, char *name ) return name; } +static void fill_ip_interface_table_row( ADDRESS_FAMILY fam, MIB_IPINTERFACE_ROW *row, struct nsi_ip_interface_key *key, + struct nsi_ip_interface_rw *rw, struct nsi_ip_interface_dynamic *dyn ) +{ + row->Family = fam; + row->InterfaceLuid = key->luid; + row->InterfaceIndex = dyn->if_index; + row->RouterDiscoveryBehavior = rw->router_discovery_behaviour; + row->DadTransmits = rw->dad_transmits; + row->BaseReachableTime = rw->base_reachable_time; + row->RetransmitTime = rw->retransmit_time; + row->PathMtuDiscoveryTimeout = rw->path_mtu_discovery_timeout; + row->LinkLocalAddressBehavior = rw->link_local_address_behavior; + row->LinkLocalAddressTimeout = rw->link_local_address_timeout; + memcpy( row->ZoneIndices, rw->zone_indices, sizeof(row->ZoneIndices) ); + row->SitePrefixLength = rw->site_prefix_len; + row->Metric = rw->metric; + row->NlMtu = rw->mtu; + row->Connected = dyn->connected; + row->SupportsWakeUpPatterns = dyn->supports_wakeup_patterns; + row->ReachableTime = dyn->reachable_time; + row->TransmitOffload = dyn->transmit_offload; + + /* MinRouterAdvertisementInterval / MaxRouterAdvertisementInterval don't seem to have an + * entry in NSI interface table, or maybe these fields are usually default and NSI tables have 0 in that case + * and thus weren't discovered. */ + row->MinRouterAdvertisementInterval = 200; + row->MaxRouterAdvertisementInterval = 600; + + /* Flags exact locations were not yet discovered in NSI table. */ + row->UseAutomaticMetric = 1; + row->UseNeighborUnreachabilityDetection = 1; + row->SupportsNeighborDiscovery = 1; + row->SupportsRouterDiscovery = 1; +} + /****************************************************************** * GetIpInterfaceTable (IPHLPAPI.@) */ -DWORD WINAPI GetIpInterfaceTable(ADDRESS_FAMILY family, PMIB_IPINTERFACE_TABLE *table) +DWORD WINAPI GetIpInterfaceTable( ADDRESS_FAMILY family, MIB_IPINTERFACE_TABLE **table ) { - FIXME("(%u %p): stub\n", family, table); - return ERROR_NOT_SUPPORTED; + struct nsi_ip_interface_dynamic *dyn; + struct nsi_ip_interface_key *keys; + DWORD err, count, total_count = 0; + MIB_IPINTERFACE_TABLE *new_alloc; + struct nsi_ip_interface_rw *rw; + ADDRESS_FAMILY fam[3] = { 0 }; + unsigned int i, family_idx; + + TRACE( "(%u %p).\n", family, table ); + + if (!table) return ERROR_INVALID_PARAMETER; + + if (family == AF_UNSPEC) + { + fam[0] = AF_INET; + fam[1] = AF_INET6; + } + else fam[0] = family; + + *table = NULL; + for (family_idx = 0; fam[family_idx]; ++family_idx) + { + err = NsiAllocateAndGetTable( 1, ip_module_id( fam[family_idx] ), NSI_IP_INTERFACE_TABLE, + (void **)&keys, sizeof(*keys), (void **)&rw, sizeof(*rw), + (void **)&dyn, sizeof(*dyn), NULL, 0, &count, 0 ); + if (err) + { + heap_free( *table ); + return err; + } + total_count += count; + new_alloc = heap_alloc_zero( offsetof(MIB_IPINTERFACE_TABLE, Table[total_count]) ); + if (!new_alloc) + { + heap_free( *table ); + NsiFreeTable( keys, rw, dyn, NULL ); + return ERROR_NOT_ENOUGH_MEMORY; + } + if (*table) + { + memcpy( new_alloc, *table, offsetof(MIB_IPINTERFACE_TABLE, Table[(*table)->NumEntries]) ); + free( *table ); + } + *table = new_alloc; + for (i = 0; i < count; ++i) + { + fill_ip_interface_table_row( fam[family_idx], &(*table)->Table[(*table)->NumEntries], + &keys[i], &rw[i], &dyn[i] ); + ++(*table)->NumEntries; + } + NsiFreeTable( keys, rw, dyn, NULL ); + } + return ERROR_SUCCESS; } /****************************************************************** diff --git a/dlls/iphlpapi/tests/iphlpapi.c b/dlls/iphlpapi/tests/iphlpapi.c index 397b2e8fad90..dfe067798a6a 100644 --- a/dlls/iphlpapi/tests/iphlpapi.c +++ b/dlls/iphlpapi/tests/iphlpapi.c @@ -67,6 +67,7 @@ static DWORD (WINAPI *pParseNetworkString)(const WCHAR*,DWORD,NET_ADDRESS_INFO*, static DWORD (WINAPI *pNotifyUnicastIpAddressChange)(ADDRESS_FAMILY, PUNICAST_IPADDRESS_CHANGE_CALLBACK, PVOID, BOOLEAN, HANDLE *); static DWORD (WINAPI *pCancelMibChangeNotify2)(HANDLE); +static DWORD (WINAPI *pGetIpInterfaceTable)(ADDRESS_FAMILY family, MIB_IPINTERFACE_TABLE **table); DWORD WINAPI ConvertGuidToStringA( const GUID *, char *, DWORD ); DWORD WINAPI ConvertGuidToStringW( const GUID *, WCHAR *, DWORD ); @@ -88,6 +89,7 @@ static void loadIPHlpApi(void) pParseNetworkString = (void *)GetProcAddress(hLibrary, "ParseNetworkString"); pNotifyUnicastIpAddressChange = (void *)GetProcAddress(hLibrary, "NotifyUnicastIpAddressChange"); pCancelMibChangeNotify2 = (void *)GetProcAddress(hLibrary, "CancelMibChangeNotify2"); + pGetIpInterfaceTable = (void *)GetProcAddress(hLibrary, "GetIpInterfaceTable"); } } @@ -3106,6 +3108,62 @@ static void test_compartments(void) ok(id == NET_IF_COMPARTMENT_ID_PRIMARY, "got %u\n", id); } +static void test_GetIpInterfaceTable(void) +{ + MIB_IPINTERFACE_TABLE *table; + MIB_IF_ROW2 *if_info = NULL; + MIB_IPINTERFACE_ROW *row; + MIB_IF_TABLE2 *if_table; + unsigned int i, j; + BOOL connected, is_loopback, loopback_found = FALSE; + DWORD err; + + if (!pGetIpInterfaceTable) + { + win_skip( "GetIpInterfaceTable is not available\n" ); + return; + } + + err = GetIfTable2( &if_table ); + ok( !err, "got %ld\n", err ); + + err = pGetIpInterfaceTable( AF_UNSPEC, NULL ); + ok( err == ERROR_INVALID_PARAMETER, "got %lu.\n", err ); + + err = pGetIpInterfaceTable( AF_UNSPEC, &table ); + ok( !err, "got %lu.\n", err ); + for (i = 0; i < table->NumEntries; ++i) + { + row = &table->Table[i]; + ok( row->Family == AF_INET || row->Family == AF_INET6, "got %d.\n", row->Family ); + for (j = 0; j < if_table->NumEntries; ++j) + { + if_info = &if_table->Table[j]; + if (if_info->InterfaceIndex == row->InterfaceIndex) break; + } + ok( j < if_table->NumEntries, "could not find interface.\n" ); + ok( row->InterfaceLuid.Value == if_info->InterfaceLuid.Value, "luid doesn't match.\n" ); + is_loopback = (if_info->Type == IF_TYPE_SOFTWARE_LOOPBACK); + if (is_loopback) loopback_found = TRUE; + if (row->Family == AF_INET) + ok( row->SitePrefixLength == 64, "got %lu.\n", row->SitePrefixLength ); + if (is_loopback) + { + ok( !row->DadTransmits, "got %lu.\n", row->DadTransmits ); + ok( row->SitePrefixLength == 64, "got %lu.\n", row->SitePrefixLength ); + } + ok( row->MinRouterAdvertisementInterval == 200, "got %lu.\n", row->MinRouterAdvertisementInterval ); + ok( row->MaxRouterAdvertisementInterval == 600, "got %lu.\n", row->MaxRouterAdvertisementInterval ); + if (is_loopback) + todo_wine ok( row->NlMtu == ~0u, "got %lu.\n", row->NlMtu ); + connected = (if_info->MediaConnectState == MediaConnectStateConnected); + ok( row->Connected == connected, "got %d, expected %d.\n", row->Connected, connected ); + } + ok( loopback_found, "loopback not found.\n" ); + FreeMibTable( table ); + FreeMibTable( if_table ); +} + START_TEST(iphlpapi) { WSADATA wsa_data; @@ -3147,6 +3205,7 @@ START_TEST(iphlpapi) test_NotifyUnicastIpAddressChange(); test_ConvertGuidToString(); test_compartments(); + test_GetIpInterfaceTable(); freeIPHlpApi(); } From 89981307d00814c68f9fa86caa96bec62d24139d Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 11 Aug 2025 15:50:48 -0600 Subject: [PATCH 334/454] nsi/tests: Add tests for IP interface table. CW-Bug-Id: #25798 --- dlls/nsi/tests/nsi.c | 64 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/dlls/nsi/tests/nsi.c b/dlls/nsi/tests/nsi.c index 0697e09b1e08..56cf8508e7b0 100644 --- a/dlls/nsi/tests/nsi.c +++ b/dlls/nsi/tests/nsi.c @@ -802,6 +802,68 @@ static void test_ip_forward( int family ) winetest_pop_context(); } +static void test_ip_interface( int family ) +{ + const NPI_MODULEID *mod = (family == AF_INET) ? &NPI_MS_IPV4_MODULEID : &NPI_MS_IPV6_MODULEID; + struct nsi_ip_interface_key *key_tbl; + struct nsi_ip_interface_rw *rw_tbl, *rw; + struct nsi_ip_interface_dynamic *dyn_tbl, *dyn; + struct nsi_ip_interface_static *stat_tbl; + MIB_IPINTERFACE_TABLE *table; + MIB_IPINTERFACE_ROW *row; + unsigned int i; + DWORD err, count; + + winetest_push_context( family == AF_INET ? "AF_INET" : "AF_INET6" ); + + err = GetIpInterfaceTable( family, &table ); + ok( !err, "got %lu.\n", err ); + err = NsiAllocateAndGetTable( 1, mod, 7, (void **)&key_tbl, sizeof(*key_tbl), + (void **)&rw_tbl, sizeof(*rw_tbl), (void **)&dyn_tbl, sizeof(*dyn_tbl), + (void **)&stat_tbl, sizeof(*stat_tbl), &count, 0 ); + ok( !err, "got %lu.\n", err ); + for (i = 0; i < count; ++i) + { + row = &table->Table[i]; + rw = &rw_tbl[i]; + dyn = &dyn_tbl[i]; + if (row->BaseReachableTime != rw->base_reachable_time) + { + /* Before Win10 1709. */ + win_skip( "Old NSI tables layout, skipping tests.\n" ); + break; + } + ok( row->BaseReachableTime == rw->base_reachable_time, "mismatch: %#lx vs %#lx.\n", + row->BaseReachableTime, rw->base_reachable_time ); + ok( row->Metric == rw->metric, "mismatch: %#lx vs %#lx.\n", row->Metric, rw->metric ); + ok( row->RetransmitTime == rw->retransmit_time, "mismatch: %#lx vs %#lx.\n", row->RetransmitTime, rw->retransmit_time ); + ok( row->PathMtuDiscoveryTimeout == rw->path_mtu_discovery_timeout, "mismatch: %#lx vs %#lx.\n", + row->PathMtuDiscoveryTimeout, rw->path_mtu_discovery_timeout ); + ok( row->DadTransmits == rw->dad_transmits, "mismatch: %#lx vs %#lx.\n", row->DadTransmits, rw->dad_transmits ); + ok( row->LinkLocalAddressBehavior == rw->link_local_address_behavior, "mismatch: %#x vs %#x.\n", + row->LinkLocalAddressBehavior, rw->link_local_address_behavior ); + ok( row->LinkLocalAddressTimeout == rw->link_local_address_timeout, "mismatch: %#lx vs %#lx.\n", + row->LinkLocalAddressTimeout, rw->link_local_address_timeout ); + ok( !memcmp( row->ZoneIndices, rw->zone_indices, sizeof(row->ZoneIndices) ), "mismatch.\n" ); + ok( row->NlMtu == rw->mtu, "mismatch: %#lx vs %#lx.\n", row->NlMtu, rw->mtu ); + ok( row->SitePrefixLength == rw->site_prefix_len, "mismatch: %#lx vs %#lx.\n", + row->SitePrefixLength, rw->site_prefix_len ); + ok( row->RouterDiscoveryBehavior == rw->router_discovery_behaviour, "mismatch: %#x vs %#x.\n", + row->RouterDiscoveryBehavior, rw->router_discovery_behaviour ); + + ok( row->InterfaceIndex == dyn->if_index, "mismatch: %#lx vs %#x.\n", row->InterfaceIndex, dyn->if_index ); + ok( row->SupportsWakeUpPatterns == dyn->supports_wakeup_patterns, "mismatch: %#x vs %#x.\n", + row->SupportsWakeUpPatterns, dyn->supports_wakeup_patterns ); + ok( row->ReachableTime == dyn->reachable_time, "mismatch: %#lx vs %#lx.\n", row->InterfaceIndex, dyn->reachable_time ); + ok( row->Connected == dyn->connected, "mismatch: %#x vs %#x.\n", row->Connected, dyn->connected ); + ok( *(UINT *)&row->TransmitOffload == *(UINT *)&dyn->transmit_offload, "mismatch: %#x vs %#x.\n", + *(UINT *)&row->TransmitOffload, *(UINT *)&dyn->transmit_offload ); + } + FreeMibTable( table ); + NsiFreeTable( key_tbl, rw_tbl, dyn_tbl, stat_tbl ); + winetest_pop_context(); +} + static void test_tcp_stats( int family ) { DWORD err; @@ -1111,6 +1173,8 @@ START_TEST( nsi ) test_ip_neighbour( AF_INET6 ); test_ip_forward( AF_INET ); test_ip_forward( AF_INET6 ); + test_ip_interface( AF_INET ); + test_ip_interface( AF_INET6 ); test_tcp_stats( AF_INET ); test_tcp_stats( AF_INET6 ); From 8b8339d33a866c1d06deb0d8132e1269aaf642f8 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 11 Aug 2025 17:36:12 -0600 Subject: [PATCH 335/454] iphlpapi: Implement GetIpInterfaceEntry(). CW-Bug-Id: #25798 --- dlls/iphlpapi/iphlpapi.spec | 2 +- dlls/iphlpapi/iphlpapi_main.c | 27 +++++++++++++++++ dlls/iphlpapi/tests/iphlpapi.c | 53 +++++++++++++++++++++++++++++++--- include/netioapi.h | 1 + 4 files changed, 78 insertions(+), 5 deletions(-) diff --git a/dlls/iphlpapi/iphlpapi.spec b/dlls/iphlpapi/iphlpapi.spec index facb52e04487..c04560ad656d 100644 --- a/dlls/iphlpapi/iphlpapi.spec +++ b/dlls/iphlpapi/iphlpapi.spec @@ -106,7 +106,7 @@ @ stdcall GetIpForwardTable( ptr ptr long ) @ stdcall GetIpForwardTable2( long ptr ) @ stub GetIpForwardTableFromStack -#@ stub GetIpInterfaceEntry +@ stdcall GetIpInterfaceEntry( ptr ) @ stdcall GetIpInterfaceTable( long ptr ) #@ stub GetIpNetEntry2 @ stdcall GetIpNetTable( ptr ptr long ) diff --git a/dlls/iphlpapi/iphlpapi_main.c b/dlls/iphlpapi/iphlpapi_main.c index 4c34318b2ce0..94769ed09fe4 100644 --- a/dlls/iphlpapi/iphlpapi_main.c +++ b/dlls/iphlpapi/iphlpapi_main.c @@ -4625,6 +4625,33 @@ DWORD WINAPI GetIpInterfaceTable( ADDRESS_FAMILY family, MIB_IPINTERFACE_TABLE * return ERROR_SUCCESS; } +/****************************************************************** + * GetIpInterfaceEntry (IPHLPAPI.@) + */ +DWORD WINAPI GetIpInterfaceEntry( MIB_IPINTERFACE_ROW *row ) +{ + struct nsi_ip_interface_dynamic dyn; + struct nsi_ip_interface_key key; + struct nsi_ip_interface_rw rw; + DWORD err; + + TRACE( "%p.\n", row ); + + if (!row) return ERROR_INVALID_PARAMETER; + if (row->Family != AF_INET && row->Family != AF_INET6) return ERROR_INVALID_PARAMETER; + + key.luid = row->InterfaceLuid; + if (!key.luid.Value && ConvertInterfaceIndexToLuid( row->InterfaceIndex, &key.luid )) return ERROR_NOT_FOUND; + + err = NsiGetAllParameters( 1, ip_module_id( row->Family ), NSI_IP_INTERFACE_TABLE, + &key, sizeof(key), &rw, sizeof(rw), + &dyn, sizeof(dyn), NULL, 0 ); + if (err) return err; + fill_ip_interface_table_row( row->Family, row, &key, &rw, &dyn ); + return ERROR_SUCCESS; +} + + /****************************************************************** * GetBestRoute2 (IPHLPAPI.@) */ diff --git a/dlls/iphlpapi/tests/iphlpapi.c b/dlls/iphlpapi/tests/iphlpapi.c index dfe067798a6a..e189fd795f87 100644 --- a/dlls/iphlpapi/tests/iphlpapi.c +++ b/dlls/iphlpapi/tests/iphlpapi.c @@ -67,6 +67,7 @@ static DWORD (WINAPI *pParseNetworkString)(const WCHAR*,DWORD,NET_ADDRESS_INFO*, static DWORD (WINAPI *pNotifyUnicastIpAddressChange)(ADDRESS_FAMILY, PUNICAST_IPADDRESS_CHANGE_CALLBACK, PVOID, BOOLEAN, HANDLE *); static DWORD (WINAPI *pCancelMibChangeNotify2)(HANDLE); +static DWORD (WINAPI *pGetIpInterfaceEntry)(MIB_IPINTERFACE_ROW*); static DWORD (WINAPI *pGetIpInterfaceTable)(ADDRESS_FAMILY family, MIB_IPINTERFACE_TABLE **table); DWORD WINAPI ConvertGuidToStringA( const GUID *, char *, DWORD ); @@ -90,6 +91,7 @@ static void loadIPHlpApi(void) pNotifyUnicastIpAddressChange = (void *)GetProcAddress(hLibrary, "NotifyUnicastIpAddressChange"); pCancelMibChangeNotify2 = (void *)GetProcAddress(hLibrary, "CancelMibChangeNotify2"); pGetIpInterfaceTable = (void *)GetProcAddress(hLibrary, "GetIpInterfaceTable"); + pGetIpInterfaceEntry = (void *)GetProcAddress(hLibrary, "GetIpInterfaceEntry"); } } @@ -3108,8 +3110,9 @@ static void test_compartments(void) ok(id == NET_IF_COMPARTMENT_ID_PRIMARY, "got %u\n", id); } -static void test_GetIpInterfaceTable(void) +static void test_GetIpInterface(void) { + MIB_IPINTERFACE_ROW entry_row; MIB_IPINTERFACE_TABLE *table; MIB_IF_ROW2 *if_info = NULL; MIB_IPINTERFACE_ROW *row; @@ -3118,9 +3121,9 @@ static void test_GetIpInterfaceTable(void) BOOL connected, is_loopback, loopback_found = FALSE; DWORD err; - if (!pGetIpInterfaceTable) + if (!pGetIpInterfaceTable || !pGetIpInterfaceEntry) { - win_skip( "GetIpInterfaceTable is not available\n" ); + win_skip( "GetIpInterfaceTable or GetIpInterfaceEntry is not available\n" ); return; } @@ -3158,6 +3161,48 @@ static void test_GetIpInterfaceTable(void) todo_wine ok( row->NlMtu == ~0u, "got %lu.\n", row->NlMtu ); connected = (if_info->MediaConnectState == MediaConnectStateConnected); ok( row->Connected == connected, "got %d, expected %d.\n", row->Connected, connected ); + + err = GetIpInterfaceEntry( NULL ); + ok( err == ERROR_INVALID_PARAMETER, "got %ld\n", err ); + + memset( &entry_row, 0, sizeof(entry_row) ); + entry_row.Family = AF_UNSPEC; + entry_row.InterfaceLuid = row->InterfaceLuid; + err = GetIpInterfaceEntry( &entry_row ); + ok( err == ERROR_INVALID_PARAMETER, "got %ld\n", err ); + + memset( &entry_row, 0xcc, sizeof(entry_row) ); + entry_row.Family = row->Family; + entry_row.InterfaceLuid = row->InterfaceLuid; + err = pGetIpInterfaceEntry( &entry_row ); + ok( !err, "got %ld\n", err ); + ok( entry_row.Family == row->Family, "got %d, expected %d.\n", entry_row.Family, row->Family ); + ok( entry_row.InterfaceLuid.Value == row->InterfaceLuid.Value, "got %#I64x, expected %#I64x.\n", + entry_row.InterfaceLuid.Value, row->InterfaceLuid.Value ); + ok( entry_row.InterfaceIndex == row->InterfaceIndex, "got %lu, expected %lu.\n", + entry_row.InterfaceIndex, row->InterfaceIndex ); + ok( entry_row.BaseReachableTime == row->BaseReachableTime, "got %lu, expected %lu.\n", + entry_row.BaseReachableTime, row->BaseReachableTime ); + + memset( &entry_row, 0xcc, sizeof(entry_row) ); + entry_row.Family = row->Family; + entry_row.InterfaceIndex = row->InterfaceIndex; + err = GetIpInterfaceEntry( &entry_row ); + ok( err == ERROR_NOT_FOUND, "got %ld\n", err ); + + memset( &entry_row, 0xcc, sizeof(entry_row) ); + entry_row.Family = row->Family; + entry_row.InterfaceLuid.Value = 0; + entry_row.InterfaceIndex = row->InterfaceIndex; + err = GetIpInterfaceEntry( &entry_row ); + ok( !err, "got %ld\n", err ); + ok( entry_row.Family == row->Family, "got %d, expected %d.\n", entry_row.Family, row->Family ); + ok( entry_row.InterfaceLuid.Value == row->InterfaceLuid.Value, "got %#I64x, expected %#I64x.\n", + entry_row.InterfaceLuid.Value, row->InterfaceLuid.Value ); + ok( entry_row.InterfaceIndex == row->InterfaceIndex, "got %lu, expected %lu.\n", + entry_row.InterfaceIndex, row->InterfaceIndex ); + ok( entry_row.BaseReachableTime == row->BaseReachableTime, "got %lu, expected %lu.\n", + entry_row.BaseReachableTime, row->BaseReachableTime ); } ok( loopback_found, "loopback not found.\n" ); FreeMibTable( table ); @@ -3205,7 +3250,7 @@ START_TEST(iphlpapi) test_NotifyUnicastIpAddressChange(); test_ConvertGuidToString(); test_compartments(); - test_GetIpInterfaceTable(); + test_GetIpInterface(); freeIPHlpApi(); } diff --git a/include/netioapi.h b/include/netioapi.h index 9a9cff8ca794..09824f946e4b 100644 --- a/include/netioapi.h +++ b/include/netioapi.h @@ -276,6 +276,7 @@ IPHLPAPI_DLL_LINKAGE DWORD WINAPI GetIfTable2(MIB_IF_TABLE2**); IPHLPAPI_DLL_LINKAGE DWORD WINAPI GetIfTable2Ex(MIB_IF_TABLE_LEVEL,MIB_IF_TABLE2**); IPHLPAPI_DLL_LINKAGE DWORD WINAPI GetIpForwardEntry2(MIB_IPFORWARD_ROW2*); IPHLPAPI_DLL_LINKAGE DWORD WINAPI GetIpForwardTable2(ADDRESS_FAMILY,MIB_IPFORWARD_TABLE2**); +IPHLPAPI_DLL_LINKAGE DWORD WINAPI GetIpInterfaceEntry(MIB_IPINTERFACE_ROW*); IPHLPAPI_DLL_LINKAGE DWORD WINAPI GetIpInterfaceTable(ADDRESS_FAMILY,MIB_IPINTERFACE_TABLE**); IPHLPAPI_DLL_LINKAGE DWORD WINAPI GetIpNetEntry2(MIB_IPNET_ROW2*); IPHLPAPI_DLL_LINKAGE DWORD WINAPI GetIpNetTable2(ADDRESS_FAMILY,MIB_IPNET_TABLE2**); From ef390d09ac81a5805c4be40ae2c57aa8058ce0b6 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 11 Aug 2025 18:15:15 -0600 Subject: [PATCH 336/454] iphlpapi: Add stub for Icmp6ParseReplies(). CW-Bug-Id: #25798 --- dlls/iphlpapi/iphlpapi.spec | 2 +- dlls/iphlpapi/iphlpapi_main.c | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/dlls/iphlpapi/iphlpapi.spec b/dlls/iphlpapi/iphlpapi.spec index c04560ad656d..ed2214c99ce6 100644 --- a/dlls/iphlpapi/iphlpapi.spec +++ b/dlls/iphlpapi/iphlpapi.spec @@ -153,7 +153,7 @@ @ stdcall GetUnicastIpAddressTable(long ptr) @ stdcall GetUniDirectionalAdapterInfo( ptr ptr ) @ stdcall Icmp6CreateFile() -#@ stub Icmp6ParseReplies +@ stdcall Icmp6ParseReplies( ptr long ) @ stdcall Icmp6SendEcho2(ptr ptr ptr ptr ptr ptr ptr long ptr ptr long long) @ stdcall IcmpCloseHandle(ptr) @ stdcall IcmpCreateFile() diff --git a/dlls/iphlpapi/iphlpapi_main.c b/dlls/iphlpapi/iphlpapi_main.c index 94769ed09fe4..dd409fff2f74 100644 --- a/dlls/iphlpapi/iphlpapi_main.c +++ b/dlls/iphlpapi/iphlpapi_main.c @@ -4963,6 +4963,14 @@ DWORD WINAPI Icmp6SendEcho2( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc_ro return 0; } +DWORD WINAPI Icmp6ParseReplies( void *buffer, DWORD size ) +{ + FIXME( "(%p, %lu) stub.\n", buffer, size ); + + SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); + return 0; +} + /*********************************************************************** * GetCurrentThreadCompartmentId (IPHLPAPI.@) */ From 9bb035977925cd0ffad57504323f149f2c8babb3 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 12 Aug 2025 10:03:21 -0600 Subject: [PATCH 337/454] ntdll: fsync: HACK: Enable fsync_help_simulated_pulse for God Eater 2 Rage Burst. CW-Bug-Id: #25721 --- dlls/ntdll/unix/loader.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c index e6969571e9e2..993848e8ea77 100644 --- a/dlls/ntdll/unix/loader.c +++ b/dlls/ntdll/unix/loader.c @@ -2193,7 +2193,7 @@ static void hacks_init(void) env_str = getenv("WINE_FSYNC_HELP_SIMULATED_PULSE"); if (env_str) fsync_help_simulated_pulse = !!atoi(env_str); - else if (sgi) fsync_help_simulated_pulse = !strcmp(sgi, "460870"); + else if (sgi) fsync_help_simulated_pulse = !strcmp(sgi, "460870") || !strcmp(sgi, "438490"); if (fsync_help_simulated_pulse) ERR("HACK: fsync: helping simulated pulse event.\n"); From c8bb38055568d5648590eb3331c31bd81d4d94dd Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 12 Aug 2025 18:45:31 -0600 Subject: [PATCH 338/454] amd_ags_x64: Support version 6.3.0. CW-Bug-Id: #25807 --- dlls/amd_ags_x64/amd_ags.h | 3 +++ dlls/amd_ags_x64/amd_ags_x64.spec | 1 + dlls/amd_ags_x64/amd_ags_x64_main.c | 29 +++++++++++++++++++++++------ dlls/amd_ags_x64/unixlib.c | 6 ++++++ 4 files changed, 33 insertions(+), 6 deletions(-) diff --git a/dlls/amd_ags_x64/amd_ags.h b/dlls/amd_ags_x64/amd_ags.h index f6e50cad8f32..2b9674362224 100644 --- a/dlls/amd_ags_x64/amd_ags.h +++ b/dlls/amd_ags_x64/amd_ags.h @@ -475,6 +475,7 @@ typedef enum AsicFamily AsicFamily_RDNA, ///< AMD RDNA architecture AsicFamily_RDNA2, ///< AMD RDNA2 architecture AsicFamily_RDNA3, ///< AMD RDNA3 architecture + AsicFamily_RDNA4, ///< AMD RDNA4 architecture AsicFamily_Count ///< Number of enumerated ASIC families } AsicFamily; @@ -987,6 +988,8 @@ AMD_AGS_API AGSReturnCode agsDeInit( AGSContext* context ); /// AMD_AGS_API AGSReturnCode agsDeInitialize( AGSContext* context ); +AMD_AGS_API AGSReturnCode agsGetGPUInfo( AGSContext* context, AGSGPUInfo_600 *gpu_info ); + /// /// Function used to query the number of GPUs used for Crossfire acceleration. /// This may be different from the total number of GPUs present in the system. diff --git a/dlls/amd_ags_x64/amd_ags_x64.spec b/dlls/amd_ags_x64/amd_ags_x64.spec index 27c8d8854168..32b47f2f5537 100644 --- a/dlls/amd_ags_x64/amd_ags_x64.spec +++ b/dlls/amd_ags_x64/amd_ags_x64.spec @@ -63,3 +63,4 @@ @ stdcall agsGetGPUMemorySize(ptr long ptr) @ stdcall agsGetEyefinityConfigInfo(ptr long ptr ptr ptr) @ stdcall agsDriverExtensions_SetCrossfireMode(ptr long) +@ stdcall agsGetGPUInfo(ptr ptr) diff --git a/dlls/amd_ags_x64/amd_ags_x64_main.c b/dlls/amd_ags_x64/amd_ags_x64_main.c index 5fa09aba8ce1..3ba410cb9333 100644 --- a/dlls/amd_ags_x64/amd_ags_x64_main.c +++ b/dlls/amd_ags_x64/amd_ags_x64_main.c @@ -64,6 +64,7 @@ enum amd_ags_version AMD_AGS_VERSION_5_4_2, AMD_AGS_VERSION_6_0_0, AMD_AGS_VERSION_6_1_0, + AMD_AGS_VERSION_6_3_0, AMD_AGS_VERSION_COUNT }; @@ -88,34 +89,35 @@ amd_ags_info[AMD_AGS_VERSION_COUNT] = {AGS_MAKE_VERSION(5, 4, 2), AGS_MAKE_VERSION(5, 4, 2), sizeof(AGSDeviceInfo_542), sizeof(AGSDX11ReturnedParams_520), AsicFamily_RDNA}, {AGS_MAKE_VERSION(6, 0, 0), AGS_MAKE_VERSION(6, 0, 1), sizeof(AGSDeviceInfo_600), sizeof(AGSDX11ReturnedParams_600), AsicFamily_RDNA2}, {AGS_MAKE_VERSION(6, 1, 0), AGS_MAKE_VERSION(6, 2, 0), sizeof(AGSDeviceInfo_600), sizeof(AGSDX11ReturnedParams_600), AsicFamily_RDNA3}, + {AGS_MAKE_VERSION(6, 3, 0), AGS_MAKE_VERSION(6, 3, 0), sizeof(AGSDeviceInfo_600), sizeof(AGSDX11ReturnedParams_600), AsicFamily_RDNA4}, }; #define DEF_FIELD(name) {DEVICE_FIELD_##name, {offsetof(AGSDeviceInfo_511, name), offsetof(AGSDeviceInfo_511, name), \ offsetof(AGSDeviceInfo_511, name), offsetof(AGSDeviceInfo_520, name), \ offsetof(AGSDeviceInfo_520, name), offsetof(AGSDeviceInfo_540, name), \ offsetof(AGSDeviceInfo_541, name), offsetof(AGSDeviceInfo_542, name), \ - offsetof(AGSDeviceInfo_600, name), offsetof(AGSDeviceInfo_600, name)}} + offsetof(AGSDeviceInfo_600, name), offsetof(AGSDeviceInfo_600, name), offsetof(AGSDeviceInfo_600, name)}} #define DEF_FIELD_520_BELOW(name) {DEVICE_FIELD_##name, {offsetof(AGSDeviceInfo_511, name), offsetof(AGSDeviceInfo_511, name), \ offsetof(AGSDeviceInfo_511, name), offsetof(AGSDeviceInfo_520, name), \ offsetof(AGSDeviceInfo_520, name), -1, \ - -1, -1, -1, -1}} + -1, -1, -1, -1, -1}} #define DEF_FIELD_520_UP(name) {DEVICE_FIELD_##name, {-1, -1, -1, offsetof(AGSDeviceInfo_520, name), \ offsetof(AGSDeviceInfo_520, name), offsetof(AGSDeviceInfo_540, name), \ offsetof(AGSDeviceInfo_541, name), offsetof(AGSDeviceInfo_542, name), \ - offsetof(AGSDeviceInfo_600, name), offsetof(AGSDeviceInfo_600, name)}} + offsetof(AGSDeviceInfo_600, name), offsetof(AGSDeviceInfo_600, name), offsetof(AGSDeviceInfo_600, name)}} #define DEF_FIELD_540_UP(name) {DEVICE_FIELD_##name, {-1, -1, -1, -1, \ -1, offsetof(AGSDeviceInfo_540, name), \ offsetof(AGSDeviceInfo_541, name), offsetof(AGSDeviceInfo_542, name), \ - offsetof(AGSDeviceInfo_600, name), offsetof(AGSDeviceInfo_600, name)}} + offsetof(AGSDeviceInfo_600, name), offsetof(AGSDeviceInfo_600, name), offsetof(AGSDeviceInfo_600, name)}} #define DEF_FIELD_540_600(name) {DEVICE_FIELD_##name, {-1, -1, -1, -1, \ -1, offsetof(AGSDeviceInfo_540, name), \ offsetof(AGSDeviceInfo_541, name), offsetof(AGSDeviceInfo_542, name), \ - -1, -1}} + -1, -1, -1}} #define DEF_FIELD_600_BELOW(name) {DEVICE_FIELD_##name, {offsetof(AGSDeviceInfo_511, name), offsetof(AGSDeviceInfo_511, name), \ offsetof(AGSDeviceInfo_511, name), offsetof(AGSDeviceInfo_520, name), \ offsetof(AGSDeviceInfo_520, name), offsetof(AGSDeviceInfo_540, name), \ offsetof(AGSDeviceInfo_541, name), offsetof(AGSDeviceInfo_542, name), \ - -1, -1}} + -1, -1, -1}} #define DEVICE_FIELD_adapterString 0 #define DEVICE_FIELD_architectureVersion 1 @@ -1057,6 +1059,21 @@ AGSReturnCode WINAPI agsInitialize(int ags_version, const AGSConfiguration *conf return AGS_SUCCESS; } +AGSReturnCode WINAPI agsGetGPUInfo(AGSContext* context, AGSGPUInfo_600 *gpu_info) +{ + TRACE("context %p, gpu_info %p.\n", context, gpu_info); + + if (!context || !gpu_info) + return AGS_INVALID_ARGS; + + memset(gpu_info, 0, sizeof(*gpu_info)); + gpu_info->driverVersion = driver_version; + gpu_info->radeonSoftwareVersion = radeon_version; + gpu_info->numDevices = context->device_count; + gpu_info->devices = context->devices; + return AGS_SUCCESS; +} + AGSReturnCode WINAPI agsDeInit(AGSContext *context) { return agsDeInitialize(context); diff --git a/dlls/amd_ags_x64/unixlib.c b/dlls/amd_ags_x64/unixlib.c index 7e5bc5bf4d6c..d34a162b55cb 100644 --- a/dlls/amd_ags_x64/unixlib.c +++ b/dlls/amd_ags_x64/unixlib.c @@ -155,6 +155,7 @@ typedef enum AsicFamily AsicFamily_RDNA, ///< AMD RDNA architecture AsicFamily_RDNA2, ///< AMD RDNA2 architecture AsicFamily_RDNA3, ///< AMD RDNA3 architecture + AsicFamily_RDNA4, } AsicFamily; /* Constants from Mesa source. */ @@ -176,6 +177,7 @@ typedef enum AsicFamily #define FAMILY_GFX1103 0x94 #define FAMILY_GFX1150 0x96 #define FAMILY_MDN 0x97 /* # 151 / Mendocino */ +#define FAMILY_GFX12 0x98 #define ROUND_DIV(value, div) (((value) + (div) / 2) / (div)) @@ -220,6 +222,10 @@ static void fill_device_info(struct drm_amdgpu_info_device *info, struct get_dev case FAMILY_GFX1150: out->asic_family = AsicFamily_RDNA3; break; + + case FAMILY_GFX12: + out->asic_family = AsicFamily_RDNA4; + break; } TRACE("family %u, erev %#x -> asicFamily %d.\n", info->family, erev, out->asic_family); if (out->asic_family == AsicFamily_Unknown && info->family != FAMILY_UNKNOWN) From f6c9ff0cef89a1a847236eae6afea1b555e10f39 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 12 Aug 2025 19:00:13 -0600 Subject: [PATCH 339/454] Revert "kernelbase: HACK: Try harder to force GL QtWebEngine rendering for EADesktop." This reverts commit a52f4f14bbdb1cd7acd833d7160b0eea0a232587. CW-Bug-Id: #25805 --- dlls/kernelbase/process.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/dlls/kernelbase/process.c b/dlls/kernelbase/process.c index 93554095a480..ad2fd9960594 100644 --- a/dlls/kernelbase/process.c +++ b/dlls/kernelbase/process.c @@ -2034,16 +2034,6 @@ BOOL WINAPI DECLSPEC_HOTPATCH SetEnvironmentVariableW( LPCWSTR name, LPCWSTR val len = lstrlenW(names[i]); if (size > len && !memcmp( module + size - len, names[i], len * sizeof(*module) )) { - HMODULE h = GetModuleHandleW(L"Qt5Core.dll"); - void (WINAPI *QCoreApplication_setAttribute)(int attr, BOOL set); - - QCoreApplication_setAttribute = (void *)GetProcAddress(h, "?setAttribute@QCoreApplication@@SAXW4ApplicationAttribute@Qt@@_N@Z"); - if (QCoreApplication_setAttribute) - { - QCoreApplication_setAttribute(16 /* AA_UseOpenGLES */, 0); - QCoreApplication_setAttribute(15 /* AA_UseDesktopOpenGL */, 1); - } - else ERR("QCoreApplication_setAttribute not found, h %p.\n", h); value = L"desktop"; FIXME( "HACK: setting QT_OPENGL=desktop.\n" ); break; From d0e4ec5f6e8e2937a61bbc4d60efcb920dca57b5 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 12 Aug 2025 19:02:20 -0600 Subject: [PATCH 340/454] Revert "kernelbase: HACK: Force GL QtWebEngine rendering for EADesktop." This reverts commit c88c784b41cf19c2eb7a52f1d2ecf64b1efa3eb8. CW-Bug-Id: #25805 --- dlls/kernelbase/process.c | 31 ------------------------------- dlls/ntdll/unix/loader.c | 8 -------- 2 files changed, 39 deletions(-) diff --git a/dlls/kernelbase/process.c b/dlls/kernelbase/process.c index ad2fd9960594..67cbbfface8d 100644 --- a/dlls/kernelbase/process.c +++ b/dlls/kernelbase/process.c @@ -2012,37 +2012,6 @@ BOOL WINAPI DECLSPEC_HOTPATCH SetEnvironmentVariableW( LPCWSTR name, LPCWSTR val return FALSE; } - if (name && !lstrcmpW( name, L"QT_OPENGL" ) && value && !lstrcmpW( value, L"angle" )) - { - static const WCHAR *names[] = - { - L"\\EADesktop.exe", - L"\\Link2EA.exe", - L"\\EAConnect_microsoft.exe", - L"\\EALaunchHelper.exe", - L"\\EACrashReporter.exe", - L"EA Desktop\\ErrorReporter.exe", - }; - unsigned int i, len; - WCHAR module[256]; - DWORD size; - - if ((size = GetModuleFileNameW( NULL, module, ARRAY_SIZE(module) )) && size < ARRAY_SIZE(module)) - { - for (i = 0; i < ARRAY_SIZE(names); ++i) - { - len = lstrlenW(names[i]); - if (size > len && !memcmp( module + size - len, names[i], len * sizeof(*module) )) - { - value = L"desktop"; - FIXME( "HACK: setting QT_OPENGL=desktop.\n" ); - break; - } - } - } - } - - RtlInitUnicodeString( &us_name, name ); if (value) { diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c index 993848e8ea77..c958665ede2c 100644 --- a/dlls/ntdll/unix/loader.c +++ b/dlls/ntdll/unix/loader.c @@ -2260,14 +2260,6 @@ static void hacks_init(void) setenv("LIBGL_ALWAYS_SOFTWARE", "1", 0); } - if (main_argc > 1 && (strstr(main_argv[1], "\\EADesktop.exe") || strstr(main_argv[1], "\\Link2EA.exe") - || strstr(main_argv[1], "EA Desktop\\ErrorReporter.exe") || strstr(main_argv[1], "\\EAConnect_microsoft.exe") - || strstr(main_argv[1], "\\EALaunchHelper.exe") || strstr(main_argv[1], "\\EACrashReporter.exe"))) - { - ERR("HACK: setting LIBGL_ALWAYS_SOFTWARE.\n"); - setenv("LIBGL_ALWAYS_SOFTWARE", "1", 0); - } - if (sgi && !strcmp(sgi, "2379390")) { ERR("HACK: setting vk_x11_override_min_image_count, vk_x11_strict_image_count.\n"); From 70e57ab1e75b3f166a8691e46dc699d006d730d0 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 12 Aug 2025 19:04:12 -0600 Subject: [PATCH 341/454] Revert "kernelbase: HACK: Force Vulkan rendering for EACefSubProcess." This reverts commit 293ebd70a54b76bc86a2f7b31e4b1a6261992387. CW-Bug-Id: #25805 --- dlls/kernelbase/process.c | 1 - 1 file changed, 1 deletion(-) diff --git a/dlls/kernelbase/process.c b/dlls/kernelbase/process.c index 67cbbfface8d..0aa85aea7cee 100644 --- a/dlls/kernelbase/process.c +++ b/dlls/kernelbase/process.c @@ -624,7 +624,6 @@ static const WCHAR *hack_append_command_line( const WCHAR *cmd ) {L"Red Tie Runner.exe", L" --use-angle=gl"}, {L"UnrealCEFSubProcess.exe", L" --use-gl=swiftshader", "2316580"}, {L"UnrealCEFSubProcess.exe", L" --use-angle=d3d9", "2684500"}, - {L"\\EACefSubProcess.exe", L" --use-angle=vulkan"}, }; unsigned int i; char sgi[64]; From c57e4eb08569c76a54b82f37afaeb429b207ae0a Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 12 Aug 2025 19:33:45 -0600 Subject: [PATCH 342/454] Revert "kernelbase: HACK: Force Angle Vulkan instead of GL for UplayWebCore." This reverts commit de138b4a24035b862fdcd93f7d955f2136e1618c. CW-Bug-Id: #25805 --- dlls/kernelbase/process.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/kernelbase/process.c b/dlls/kernelbase/process.c index 0aa85aea7cee..d6a0285cbb44 100644 --- a/dlls/kernelbase/process.c +++ b/dlls/kernelbase/process.c @@ -608,7 +608,7 @@ static const WCHAR *hack_append_command_line( const WCHAR *cmd ) {L"Insanitys Blade\\nw.exe", L" --use-gl=swiftshader"}, {L"Warhammer2.exe", L" --in-process-gpu"}, {L"SummerIslands.exe", L" --in-process-gpu"}, - {L"UplayWebCore.exe", L" --use-angle=vulkan"}, + {L"UplayWebCore.exe", L" --use-gl=swiftshader"}, {L"Paradox Launcher.exe", L" --use-angle=gl"}, {L"Montaro\\nw.exe", L" --use-gl=swiftshader"}, {L"Aisling and the Tavern of Elves\\nw.exe", L" --use-gl=swiftshader"}, From 33a9f372a20db63894d42cea5784fb1e50bd0c97 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 12 Aug 2025 19:35:52 -0600 Subject: [PATCH 343/454] Revert "kernelbase: HACK: Force CEF software rendering for UplayWebCore." This reverts commit 18e797478405a201d7c184c962a83e5c35fe65ef. CW-Bug-Id: #25805 --- dlls/kernelbase/process.c | 1 - 1 file changed, 1 deletion(-) diff --git a/dlls/kernelbase/process.c b/dlls/kernelbase/process.c index d6a0285cbb44..09e373f45f9a 100644 --- a/dlls/kernelbase/process.c +++ b/dlls/kernelbase/process.c @@ -608,7 +608,6 @@ static const WCHAR *hack_append_command_line( const WCHAR *cmd ) {L"Insanitys Blade\\nw.exe", L" --use-gl=swiftshader"}, {L"Warhammer2.exe", L" --in-process-gpu"}, {L"SummerIslands.exe", L" --in-process-gpu"}, - {L"UplayWebCore.exe", L" --use-gl=swiftshader"}, {L"Paradox Launcher.exe", L" --use-angle=gl"}, {L"Montaro\\nw.exe", L" --use-gl=swiftshader"}, {L"Aisling and the Tavern of Elves\\nw.exe", L" --use-gl=swiftshader"}, From 7b3cf0e1a780f373dbb0152dc466697bc9233281 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20K=C3=B6lbl?= Date: Tue, 5 Aug 2025 21:14:28 +0200 Subject: [PATCH 344/454] windows.media.speech: Use Steam UI language for Phasmophobia. CW-Bug-Id: #20134 --- dlls/windows.media.speech/Makefile.in | 2 +- dlls/windows.media.speech/recognizer.c | 8 +++++++- dlls/windows.media.speech/unixlib.c | 20 +++++++++++++------- 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/dlls/windows.media.speech/Makefile.in b/dlls/windows.media.speech/Makefile.in index ea1e42721395..8524fa4105c8 100644 --- a/dlls/windows.media.speech/Makefile.in +++ b/dlls/windows.media.speech/Makefile.in @@ -1,6 +1,6 @@ MODULE = windows.media.speech.dll UNIXLIB = windows.media.speech.so -IMPORTS = combase uuid user32 +IMPORTS = advapi32 combase uuid user32 SOURCES = \ async.c \ diff --git a/dlls/windows.media.speech/recognizer.c b/dlls/windows.media.speech/recognizer.c index f97fa5bc0b41..b3258c2915d4 100644 --- a/dlls/windows.media.speech/recognizer.c +++ b/dlls/windows.media.speech/recognizer.c @@ -1492,11 +1492,15 @@ static HRESULT WINAPI recognizer_get_UIOptions( ISpeechRecognizer *iface, ISpeec static HRESULT recognizer_create_unix_instance( struct session *session, const char **grammar, UINT32 grammar_size ) { struct speech_create_recognizer_params create_params = { 0 }; + DWORD locale_len = LOCALE_NAME_MAX_LENGTH; WCHAR locale[LOCALE_NAME_MAX_LENGTH]; + char *appid = getenv("SteamAppId"); NTSTATUS status; INT len; - if (!(len = GetUserDefaultLocaleName(locale, LOCALE_NAME_MAX_LENGTH))) + if (!strcmp(appid, "739630") && !RegGetValueW(HKEY_CURRENT_USER, L"Software\\Valve\\Steam", L"language", RRF_RT_REG_SZ, NULL, locale, &locale_len)) + len = locale_len; + else if (!(len = GetUserDefaultLocaleName(locale, LOCALE_NAME_MAX_LENGTH))) return E_FAIL; if (CharLowerBuffW(locale, len) != len) @@ -1505,6 +1509,8 @@ static HRESULT recognizer_create_unix_instance( struct session *session, const c if (!WideCharToMultiByte(CP_ACP, 0, locale, len, create_params.locale, ARRAY_SIZE(create_params.locale), NULL, NULL)) return HRESULT_FROM_WIN32(GetLastError()); + TRACE("creating recognizer with language %s\n", debugstr_a(create_params.locale)); + create_params.sample_rate = (FLOAT)session->capture_wfx.nSamplesPerSec; create_params.grammar = grammar; create_params.grammar_size = grammar_size; diff --git a/dlls/windows.media.speech/unixlib.c b/dlls/windows.media.speech/unixlib.c index 55e48a550ff1..dbedd8c620e1 100644 --- a/dlls/windows.media.speech/unixlib.c +++ b/dlls/windows.media.speech/unixlib.c @@ -125,7 +125,9 @@ static const char* map_lang_to_phasmophobia_dir(const char* lang, size_t len) return "Arabic"; if (!strncmp(lang, "ca", len)) return "Catalan"; - if (!strncmp(lang, "zn", len)) + if (!strncmp(lang, "zn", len) || + !strncmp(lang, "schinese", len) || + !strncmp(lang, "tchinese", len)) return "Chinese"; if (!strncmp(lang, "cs", len)) return "Czech"; @@ -137,19 +139,19 @@ static const char* map_lang_to_phasmophobia_dir(const char* lang, size_t len) return "French"; if (!strncmp(lang, "de", len)) return "German"; - if (!strncmp(lang, "de", len)) - return "German"; if (!strncmp(lang, "el", len)) return "Greek"; if (!strncmp(lang, "it", len)) return "Italian"; if (!strncmp(lang, "ja", len)) return "Japanese"; - if (!strncmp(lang, "pt", len)) + if (!strncmp(lang, "pt", len) || + !strncmp(lang, "brazilian", len)) return "Portuguese"; if (!strncmp(lang, "ru", len)) return "Russian"; - if (!strncmp(lang, "es", len)) + if (!strncmp(lang, "es", len) || + !strncmp(lang, "latam", len)) return "Spanish"; if (!strncmp(lang, "sw", len)) return "Swedish"; @@ -158,7 +160,8 @@ static const char* map_lang_to_phasmophobia_dir(const char* lang, size_t len) if (!strncmp(lang, "uk", len)) return "Ukrainian"; - return ""; + /* default to English */ + return "English"; } static NTSTATUS find_model_by_locale_and_path( const char *path, const char *locale, VoskModel **model ) @@ -201,7 +204,7 @@ static NTSTATUS find_model_by_locale_and_path( const char *path, const char *loc * Find the first matching model for lang and region (en-us). * If there isn't any, pick the first one just matching lang (en). */ - if (!strncmp(ent_name, locale, len)) + if (!strncasecmp(ent_name, locale, len)) { if (best_match) free(best_match); best_match = strdup(dirent->d_name); @@ -211,6 +214,9 @@ static NTSTATUS find_model_by_locale_and_path( const char *path, const char *loc if (!best_match && !strncmp(ent_name, locale, delim - locale)) best_match = strdup(dirent->d_name); + /* + * Note: With the hack for Phasmophobia, "locale" can also contain the Steam UI language. + */ if (!best_match && !strcmp(appid, "739630")) { if ((str = (char *)map_lang_to_phasmophobia_dir(locale, delim - locale))) From 5d5d031397e35e708fbc9ce7e6ef0295a562df6c Mon Sep 17 00:00:00 2001 From: Arkadiusz Hiler Date: Thu, 14 Aug 2025 23:08:13 +0300 Subject: [PATCH 345/454] Revert "windows.media.speech: Use Steam UI language for Phasmophobia." This reverts commit 4ff9163efe43eaa4f229a1c46bc2c76b624a4b46. --- dlls/windows.media.speech/Makefile.in | 2 +- dlls/windows.media.speech/recognizer.c | 8 +------- dlls/windows.media.speech/unixlib.c | 20 +++++++------------- 3 files changed, 9 insertions(+), 21 deletions(-) diff --git a/dlls/windows.media.speech/Makefile.in b/dlls/windows.media.speech/Makefile.in index 8524fa4105c8..ea1e42721395 100644 --- a/dlls/windows.media.speech/Makefile.in +++ b/dlls/windows.media.speech/Makefile.in @@ -1,6 +1,6 @@ MODULE = windows.media.speech.dll UNIXLIB = windows.media.speech.so -IMPORTS = advapi32 combase uuid user32 +IMPORTS = combase uuid user32 SOURCES = \ async.c \ diff --git a/dlls/windows.media.speech/recognizer.c b/dlls/windows.media.speech/recognizer.c index b3258c2915d4..f97fa5bc0b41 100644 --- a/dlls/windows.media.speech/recognizer.c +++ b/dlls/windows.media.speech/recognizer.c @@ -1492,15 +1492,11 @@ static HRESULT WINAPI recognizer_get_UIOptions( ISpeechRecognizer *iface, ISpeec static HRESULT recognizer_create_unix_instance( struct session *session, const char **grammar, UINT32 grammar_size ) { struct speech_create_recognizer_params create_params = { 0 }; - DWORD locale_len = LOCALE_NAME_MAX_LENGTH; WCHAR locale[LOCALE_NAME_MAX_LENGTH]; - char *appid = getenv("SteamAppId"); NTSTATUS status; INT len; - if (!strcmp(appid, "739630") && !RegGetValueW(HKEY_CURRENT_USER, L"Software\\Valve\\Steam", L"language", RRF_RT_REG_SZ, NULL, locale, &locale_len)) - len = locale_len; - else if (!(len = GetUserDefaultLocaleName(locale, LOCALE_NAME_MAX_LENGTH))) + if (!(len = GetUserDefaultLocaleName(locale, LOCALE_NAME_MAX_LENGTH))) return E_FAIL; if (CharLowerBuffW(locale, len) != len) @@ -1509,8 +1505,6 @@ static HRESULT recognizer_create_unix_instance( struct session *session, const c if (!WideCharToMultiByte(CP_ACP, 0, locale, len, create_params.locale, ARRAY_SIZE(create_params.locale), NULL, NULL)) return HRESULT_FROM_WIN32(GetLastError()); - TRACE("creating recognizer with language %s\n", debugstr_a(create_params.locale)); - create_params.sample_rate = (FLOAT)session->capture_wfx.nSamplesPerSec; create_params.grammar = grammar; create_params.grammar_size = grammar_size; diff --git a/dlls/windows.media.speech/unixlib.c b/dlls/windows.media.speech/unixlib.c index dbedd8c620e1..55e48a550ff1 100644 --- a/dlls/windows.media.speech/unixlib.c +++ b/dlls/windows.media.speech/unixlib.c @@ -125,9 +125,7 @@ static const char* map_lang_to_phasmophobia_dir(const char* lang, size_t len) return "Arabic"; if (!strncmp(lang, "ca", len)) return "Catalan"; - if (!strncmp(lang, "zn", len) || - !strncmp(lang, "schinese", len) || - !strncmp(lang, "tchinese", len)) + if (!strncmp(lang, "zn", len)) return "Chinese"; if (!strncmp(lang, "cs", len)) return "Czech"; @@ -139,19 +137,19 @@ static const char* map_lang_to_phasmophobia_dir(const char* lang, size_t len) return "French"; if (!strncmp(lang, "de", len)) return "German"; + if (!strncmp(lang, "de", len)) + return "German"; if (!strncmp(lang, "el", len)) return "Greek"; if (!strncmp(lang, "it", len)) return "Italian"; if (!strncmp(lang, "ja", len)) return "Japanese"; - if (!strncmp(lang, "pt", len) || - !strncmp(lang, "brazilian", len)) + if (!strncmp(lang, "pt", len)) return "Portuguese"; if (!strncmp(lang, "ru", len)) return "Russian"; - if (!strncmp(lang, "es", len) || - !strncmp(lang, "latam", len)) + if (!strncmp(lang, "es", len)) return "Spanish"; if (!strncmp(lang, "sw", len)) return "Swedish"; @@ -160,8 +158,7 @@ static const char* map_lang_to_phasmophobia_dir(const char* lang, size_t len) if (!strncmp(lang, "uk", len)) return "Ukrainian"; - /* default to English */ - return "English"; + return ""; } static NTSTATUS find_model_by_locale_and_path( const char *path, const char *locale, VoskModel **model ) @@ -204,7 +201,7 @@ static NTSTATUS find_model_by_locale_and_path( const char *path, const char *loc * Find the first matching model for lang and region (en-us). * If there isn't any, pick the first one just matching lang (en). */ - if (!strncasecmp(ent_name, locale, len)) + if (!strncmp(ent_name, locale, len)) { if (best_match) free(best_match); best_match = strdup(dirent->d_name); @@ -214,9 +211,6 @@ static NTSTATUS find_model_by_locale_and_path( const char *path, const char *loc if (!best_match && !strncmp(ent_name, locale, delim - locale)) best_match = strdup(dirent->d_name); - /* - * Note: With the hack for Phasmophobia, "locale" can also contain the Steam UI language. - */ if (!best_match && !strcmp(appid, "739630")) { if ((str = (char *)map_lang_to_phasmophobia_dir(locale, delim - locale))) From 4523d63aedc0e481e28729d40ea39b2ba25d2c01 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 15 Aug 2025 13:29:37 -0600 Subject: [PATCH 346/454] ntdll: Use process_vm_writev() for other process write. CW-Bug-Id: #25817 --- dlls/ntdll/unix/virtual.c | 3 +- server/process.c | 6 +- server/process.h | 3 +- server/protocol.def | 2 + server/ptrace.c | 124 ++++++-------------------------------- 5 files changed, 29 insertions(+), 109 deletions(-) diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c index d955a6172484..6684f53ef4fc 100644 --- a/dlls/ntdll/unix/virtual.c +++ b/dlls/ntdll/unix/virtual.c @@ -6971,7 +6971,8 @@ NTSTATUS WINAPI NtWriteVirtualMemory( HANDLE process, void *addr, const void *bu req->handle = wine_server_obj_handle( process ); req->addr = wine_server_client_ptr( addr ); wine_server_add_data( req, buffer, size ); - if ((status = wine_server_call( req ))) size = 0; + status = wine_server_call( req ); + size = reply->written; } SERVER_END_REQ; } diff --git a/server/process.c b/server/process.c index 73e85e9d61ac..914ffc105f82 100644 --- a/server/process.c +++ b/server/process.c @@ -1163,8 +1163,8 @@ int set_process_debug_flag( struct process *process, int flag ) peb32 = process->peb + 0x1000; /* BeingDebugged flag is the byte at offset 2 in the PEB */ - if (peb32 && !write_process_memory( process, peb32 + 2, 1, &data )) return 0; - return write_process_memory( process, process->peb + 2, 1, &data ); + if (peb32 && !write_process_memory( process, peb32 + 2, 1, &data, NULL )) return 0; + return write_process_memory( process, process->peb + 2, 1, &data, NULL ); } /* create a new process */ @@ -1712,7 +1712,7 @@ DECL_HANDLER(write_process_memory) if ((process = get_process_from_handle( req->handle, PROCESS_VM_WRITE ))) { data_size_t len = get_req_data_size(); - if (len) write_process_memory( process, req->addr, len, get_req_data() ); + if (len) write_process_memory( process, req->addr, len, get_req_data(), &reply->written ); release_object( process ); } } diff --git a/server/process.h b/server/process.h index d2aadd521e8e..e332069c56f8 100644 --- a/server/process.h +++ b/server/process.h @@ -137,7 +137,8 @@ extern void init_tracing_mechanism(void); extern void init_process_tracing( struct process *process ); extern void finish_process_tracing( struct process *process ); extern int read_process_memory( struct process *process, client_ptr_t ptr, data_size_t size, char *dest ); -extern int write_process_memory( struct process *process, client_ptr_t ptr, data_size_t size, const char *src ); +extern int write_process_memory( struct process *process, client_ptr_t ptr, data_size_t size, const char *src, + data_size_t *written ); static inline process_id_t get_process_id( struct process *process ) { return process->id; } diff --git a/server/protocol.def b/server/protocol.def index fedff72c267b..d120eff58d4e 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -1979,6 +1979,8 @@ struct process_info obj_handle_t handle; /* process handle */ client_ptr_t addr; /* addr to write to */ VARARG(data,bytes); /* data to write */ +@REPLY + data_size_t written; /* number of bytes written. */ @END diff --git a/server/ptrace.c b/server/ptrace.c index e2eea50d6737..a40c5f40847e 100644 --- a/server/ptrace.c +++ b/server/ptrace.c @@ -45,6 +45,8 @@ #endif #include +#include + #include "ntstatus.h" #define WIN32_NO_STATUS #include "winternl.h" @@ -332,22 +334,6 @@ static int read_thread_int( struct thread *thread, void *addr, unsigned int *dat return ret; } -/* write a long to a thread address space */ -static long write_thread_long( struct thread *thread, void *addr, unsigned long data, unsigned long mask ) -{ - unsigned long old_data; - int res; - - if (mask != ~0ul) - { - if (read_thread_long( thread, addr, &old_data ) == -1) return -1; - data = (data & mask) | (old_data & ~mask); - } - if ((res = ptrace( PTRACE_POKEDATA, get_ptrace_pid(thread), (caddr_t)addr, data )) == -1) - file_set_error(); - return res; -} - /* return a thread of the process suitable for ptracing */ static struct thread *get_ptrace_thread( struct process *process ) { @@ -428,31 +414,13 @@ int read_process_memory( struct process *process, client_ptr_t ptr, data_size_t return !len; } -/* make sure we can write to the whole address range */ -/* len is the total size (in longs) */ -static int check_process_write_access( struct thread *thread, long *addr, data_size_t len ) -{ - int page = get_page_size() / sizeof(long); - - for (;;) - { - if (write_thread_long( thread, addr, 0, 0 ) == -1) return 0; - if (len <= page) break; - addr += page; - len -= page; - } - return (write_thread_long( thread, addr + len - 1, 0, 0 ) != -1); -} - /* write data to a process memory space */ -int write_process_memory( struct process *process, client_ptr_t ptr, data_size_t size, const char *src ) +int write_process_memory( struct process *process, client_ptr_t ptr, data_size_t size, const char *src, + data_size_t *written ) { struct thread *thread = get_ptrace_thread( process ); - int ret = 0; - long data = 0; - data_size_t len; - long *addr; - unsigned long first_mask, first_offset, last_mask, last_offset; + struct iovec local, remote; + ssize_t len; if (!thread) return 0; @@ -462,74 +430,22 @@ int write_process_memory( struct process *process, client_ptr_t ptr, data_size_t return 0; } - /* compute the mask for the first long */ - first_mask = ~0; - first_offset = ptr % sizeof(long); - memset( &first_mask, 0, first_offset ); - - /* compute the mask for the last long */ - last_offset = (size + first_offset) % sizeof(long); - if (!last_offset) last_offset = sizeof(long); - last_mask = 0; - memset( &last_mask, 0xff, last_offset ); - - addr = (long *)(unsigned long)(ptr - first_offset); - len = (size + first_offset + sizeof(long) - 1) / sizeof(long); - - if (suspend_for_ptrace( thread )) + if (thread->unix_pid == -1 || !is_process_init_done(thread->process)) { - if (!check_process_write_access( thread, addr, len )) - { - set_error( STATUS_ACCESS_DENIED ); - goto done; - } - - if (len > 3) - { - char procmem[24]; - int fd; - - snprintf( procmem, sizeof(procmem), "/proc/%u/mem", process->unix_pid ); - if ((fd = open( procmem, O_WRONLY )) != -1) - { - ssize_t r = pwrite( fd, src, size, ptr ); - close( fd ); - if (r == size) - { - ret = 1; - goto done; - } - } - } - - /* first word is special */ - if (len > 1) - { - memcpy( (char *)&data + first_offset, src, sizeof(long) - first_offset ); - src += sizeof(long) - first_offset; - if (write_thread_long( thread, addr++, data, first_mask ) == -1) goto done; - first_offset = 0; - len--; - } - else last_mask &= first_mask; - - while (len > 1) - { - memcpy( &data, src, sizeof(long) ); - src += sizeof(long); - if (write_thread_long( thread, addr++, data, ~0ul ) == -1) goto done; - len--; - } - - /* last word is special too */ - memcpy( (char *)&data + first_offset, src, last_offset - first_offset ); - if (write_thread_long( thread, addr, data, last_mask ) == -1) goto done; - ret = 1; - - done: - resume_after_ptrace( thread ); + set_error( STATUS_ACCESS_DENIED ); + return 0; } - return ret; + local.iov_len = remote.iov_len = size; + local.iov_base = (void *)src; + remote.iov_base = (void *)(unsigned long)ptr; + len = process_vm_writev( thread->unix_pid, &local, 1, &remote, 1, 0); + if (written) *written = len == -1 ? 0 : len; + if (len == -1 || len != size) + { + set_error( STATUS_PARTIAL_COPY ); + return 0; + } + return 1; } /* retrieve an LDT selector entry */ From bd1e572e0f68f4f6989ccccea53a137cb88cd01f Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 19 Aug 2025 13:42:06 -0600 Subject: [PATCH 347/454] winex11.drv: Only process raw input in default winstation's explorer.exe. CW-Bug-Id: #25818 --- dlls/winex11.drv/window.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index 9c085ccc5f4a..8229ab43b06e 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -2959,13 +2959,22 @@ BOOL X11DRV_CreateWindow( HWND hwnd ) { if (hwnd == NtUserGetDesktopWindow()) { + static const WCHAR winsta0[] = {'W','i','n','S','t','a','0',0}; struct x11drv_thread_data *data = x11drv_init_thread_data(); + WCHAR winstation_name[64]; XSetWindowAttributes attr; - /* listen to raw xinput event in the desktop window thread */ - data->xinput2_rawinput = TRUE; - x11drv_xinput2_enable( data->display, DefaultRootWindow( data->display ) ); - + if (NtUserGetObjectInformation( NtUserGetProcessWindowStation(), UOI_NAME, winstation_name, + sizeof(winstation_name), NULL )) + { + TRACE( "winstation name %s.\n", debugstr_w(winstation_name) ); + if (!wcscmp( winstation_name, winsta0 )) + { + /* listen to raw xinput event in the desktop window thread */ + data->xinput2_rawinput = TRUE; + x11drv_xinput2_enable( data->display, DefaultRootWindow( data->display ) ); + } + } /* create the cursor clipping window */ attr.override_redirect = TRUE; attr.event_mask = StructureNotifyMask | FocusChangeMask; From 3740e20032f45b3150e3df31965545c6a903da70 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 20 Aug 2025 15:56:30 -0600 Subject: [PATCH 348/454] win32u: Avoid calling server in NtUserGetKeyState() when input keystate is in sync. CW-Bug-Id: #25824 CW-Bug-Id: #21761 --- dlls/win32u/input.c | 19 ++++++++++++++++--- server/protocol.def | 2 ++ server/queue.c | 6 ++++++ server/winstation.c | 1 + 4 files changed, 25 insertions(+), 3 deletions(-) diff --git a/dlls/win32u/input.c b/dlls/win32u/input.c index 97e651a7ce52..14c1121506cb 100644 --- a/dlls/win32u/input.c +++ b/dlls/win32u/input.c @@ -1033,17 +1033,30 @@ SHORT WINAPI NtUserGetKeyState( INT vkey ) { struct object_lock lock = OBJECT_LOCK_INIT; const input_shm_t *input_shm; - BOOL ret = FALSE; + UINT64 keystate_serial = 0; + int input_state = 0; SHORT retval = 0; NTSTATUS status; while ((status = get_shared_input( GetCurrentThreadId(), &lock, &input_shm )) == STATUS_PENDING) { - ret = !!input_shm->keystate_lock; /* needs a request for sync_input_keystate */ + /* when input is not locked needs a request for sync_input_keystate if desktop input state + * changed. */ + input_state = input_shm->keystate_lock ? 1 : -1; + keystate_serial = input_shm->desktop_keystate_serial; retval = (signed char)(input_shm->keystate[vkey & 0xff] & 0x81); } - if (!ret) SERVER_START_REQ( get_key_state ) + if (input_state < 0) + { + struct object_lock lock = OBJECT_LOCK_INIT; + const desktop_shm_t *desktop_shm; + + while ((status = get_shared_desktop( &lock, &desktop_shm )) == STATUS_PENDING) + input_state = (keystate_serial == desktop_shm->keystate_serial); + } + + if (input_state != 1) SERVER_START_REQ( get_key_state ) { req->key = vkey; if (!wine_server_call( req )) retval = (signed char)(reply->state & 0x81); diff --git a/server/protocol.def b/server/protocol.def index d120eff58d4e..5f5506adeb46 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -992,6 +992,7 @@ typedef volatile struct struct shared_cursor cursor; /* global cursor information */ unsigned char keystate[256]; /* asynchronous key state */ unsigned __int64 monitor_serial; /* winstation monitor update counter */ + unsigned __int64 keystate_serial; /* keystate update counter */ } desktop_shm_t; typedef volatile struct @@ -1017,6 +1018,7 @@ typedef volatile struct int cursor_count; /* cursor show count */ unsigned char keystate[256]; /* key state */ int keystate_lock; /* keystate is locked */ + unsigned __int64 desktop_keystate_serial; /* desktop keystate update counter at last sync */ } input_shm_t; typedef volatile union diff --git a/server/queue.c b/server/queue.c index cb43e56a4363..7a5f74ce3344 100644 --- a/server/queue.c +++ b/server/queue.c @@ -302,6 +302,7 @@ static struct thread_input *create_thread_input( struct thread *thread ) shared->cursor_count = 0; memset( (void *)shared->keystate, 0, sizeof(shared->keystate) ); shared->keystate_lock = 0; + shared->desktop_keystate_serial = 0; } SHARED_WRITE_END; } @@ -406,6 +407,7 @@ static void sync_input_keystate( struct thread_input *input ) if (input->desktop_keystate[i] == desktop_shm->keystate[i]) continue; shared->keystate[i] = input->desktop_keystate[i] = desktop_shm->keystate[i]; } + shared->desktop_keystate_serial = desktop_shm->keystate_serial; } SHARED_WRITE_END; } @@ -1561,6 +1563,7 @@ int attach_thread_input( struct thread *thread_from, struct thread *thread_to ) SHARED_WRITE_BEGIN( input_shm, input_shm_t ) { memset( (void *)shared->keystate, 0, sizeof(shared->keystate) ); + shared->desktop_keystate_serial = 0; } SHARED_WRITE_END; } @@ -1833,6 +1836,7 @@ static void update_desktop_key_state( struct desktop *desktop, unsigned int msg, SHARED_WRITE_BEGIN( desktop->shared, desktop_shm_t ) { update_key_state( shared->keystate, msg, wparam, 1 ); + ++shared->keystate_serial; } SHARED_WRITE_END; } @@ -3964,6 +3968,7 @@ DECL_HANDLER(get_key_state) { reply->state = shared->keystate[req->key & 0xff]; shared->keystate[req->key & 0xff] &= ~0x40; + ++shared->keystate_serial; } SHARED_WRITE_END; release_object( desktop ); @@ -4001,6 +4006,7 @@ DECL_HANDLER(set_key_state) SHARED_WRITE_BEGIN( desktop->shared, desktop_shm_t ) { memcpy( (void *)shared->keystate, get_req_data(), size ); + ++shared->keystate_serial; } SHARED_WRITE_END; release_object( desktop ); diff --git a/server/winstation.c b/server/winstation.c index 5a83171e6596..8803d9bf6191 100644 --- a/server/winstation.c +++ b/server/winstation.c @@ -328,6 +328,7 @@ static struct desktop *create_desktop( const struct unicode_str *name, unsigned shared->cursor.clip.right = 0; shared->cursor.clip.bottom = 0; memset( (void *)shared->keystate, 0, sizeof(shared->keystate) ); + shared->keystate_serial = 1; shared->monitor_serial = winstation->monitor_serial; } SHARED_WRITE_END; From b65c772157cadb9df76efcd5119ae0f11d6d691f Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 20 Aug 2025 18:24:59 -0600 Subject: [PATCH 349/454] winex11.drv: Only create dummy parent when needed in create_client_window(). CW-Bug-Id: #25824 --- dlls/winex11.drv/window.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index 8229ab43b06e..3d8d4d301ab2 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -2528,7 +2528,6 @@ void destroy_client_window( HWND hwnd, Window client_window ) */ Window create_client_window( HWND hwnd, RECT client_rect, const XVisualInfo *visual, Colormap colormap ) { - Window dummy_parent = get_dummy_parent(); struct x11drv_win_data *data = get_win_data( hwnd ); XSetWindowAttributes attr; Window ret; @@ -2559,7 +2558,7 @@ Window create_client_window( HWND hwnd, RECT client_rect, const XVisualInfo *vis XSync( gdi_display, False ); /* make sure whole_window is known from gdi_display */ ret = data->client_window = XCreateWindow( gdi_display, - data->whole_window ? data->whole_window : dummy_parent, + data->whole_window ? data->whole_window : get_dummy_parent(), x, y, cx, cy, 0, default_visual.depth, InputOutput, visual->visual, CWBitGravity | CWWinGravity | CWBackingStore | CWColormap | CWBorderPixel, &attr ); From 3626d1528429e8e6002650327bd25caf9fa0d5ad Mon Sep 17 00:00:00 2001 From: Ziqing Hui Date: Wed, 23 Jul 2025 09:08:53 +0800 Subject: [PATCH 350/454] HACK: win32u: Enable GetAsyncKeyState() recent bit for Dungeon Lords Steam Edition. The recent bit was disabled by 22099505 but is needed for this game. Link: https://github.com/ValveSoftware/Proton/issues/8857 CW-Bug-Id: #25678 --- dlls/win32u/input.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/win32u/input.c b/dlls/win32u/input.c index 14c1121506cb..58fa4a291d67 100644 --- a/dlls/win32u/input.c +++ b/dlls/win32u/input.c @@ -824,7 +824,7 @@ SHORT WINAPI NtUserGetAsyncKeyState( INT key ) if (use_recent_bit == -1) { const char *sgi = getenv("SteamGameId"); - use_recent_bit = sgi && strcmp(sgi, "302190") == 0; + use_recent_bit = sgi && (!strcmp(sgi, "302190") || !strcmp(sgi, "271760")); } if (key < 0 || key >= 256) return 0; From 33ea898d8175c805176d0d66a9ad440f56a4fd4f Mon Sep 17 00:00:00 2001 From: Conor McCarthy Date: Wed, 13 Aug 2025 14:10:05 +1000 Subject: [PATCH 351/454] winegstreamer: Use a stride alignment of 2 for NV12 in align_video_info_planes(). (cherry picked from commit 6407cb1bbf87ce5c551165323086df66068e6f3e) CW-Bug-Id: #25319 CW-Bug-Id: #25806 --- dlls/winegstreamer/wg_transform.c | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/dlls/winegstreamer/wg_transform.c b/dlls/winegstreamer/wg_transform.c index 66cbbe0d825e..7a1287b0fa99 100644 --- a/dlls/winegstreamer/wg_transform.c +++ b/dlls/winegstreamer/wg_transform.c @@ -104,6 +104,7 @@ static struct wg_transform *get_transform(wg_transform_t trans) static void align_video_info_planes(MFVideoInfo *video_info, gsize plane_align, GstVideoInfo *info, GstVideoAlignment *align) { + bool fix_nv12 = !plane_align && info->finfo->format == GST_VIDEO_FORMAT_NV12 && (info->width & 3) && (info->width & 3) != 3; const MFVideoArea *aperture = &video_info->MinimumDisplayAperture; gst_video_alignment_reset(align); @@ -126,9 +127,24 @@ static void align_video_info_planes(MFVideoInfo *video_info, gsize plane_align, align->padding_bottom = top; } - align->stride_align[0] = plane_align; - - gst_video_info_align(info, align); + /* TODO: set NV12 GstVideoInfo correctly when padding is present */ + if (fix_nv12 && !align->padding_left && !align->padding_top && !align->padding_right && !align->padding_bottom) + { + /* NV12 minimum stride alignment is 2, and Windows expects 2, + * but gst_video_info_align() imposes a minimum of 4. */ + gint aligned_height = GST_ROUND_UP_2(info->height); + info->stride[0] = GST_ROUND_UP_2(info->width); + info->stride[1] = info->stride[0]; + info->offset[0] = 0; + info->offset[1] = info->stride[0] * aligned_height; + info->size = info->offset[1] + info->stride[0] * aligned_height / 2; + align->stride_align[0] = 1; + } + else + { + align->stride_align[0] = plane_align; + gst_video_info_align(info, align); + } if (video_info->VideoFlags & MFVideoFlag_BottomUpLinearRep) { @@ -203,12 +219,16 @@ static WgVideoBufferPool *wg_video_buffer_pool_create(GstCaps *caps, gsize plane { WgVideoBufferPool *pool; GstStructure *config; + gsize max_size; if (!(pool = g_object_new(wg_video_buffer_pool_get_type(), NULL))) return NULL; gst_video_info_from_caps(&pool->info, caps); + max_size = pool->info.size; align_video_info_planes(video_info, plane_align, &pool->info, align); + /* GStreamer assumes NV12 pools must accommodate a stride alignment of 4, but we use 2 */ + max_size = max(max_size, pool->info.size); if (!(config = gst_buffer_pool_get_config(GST_BUFFER_POOL(pool)))) GST_ERROR("Failed to get %"GST_PTR_FORMAT" config.", pool); @@ -218,7 +238,7 @@ static WgVideoBufferPool *wg_video_buffer_pool_create(GstCaps *caps, gsize plane gst_buffer_pool_config_add_option(config, GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT); gst_buffer_pool_config_set_video_alignment(config, align); - gst_buffer_pool_config_set_params(config, caps, pool->info.size, 0, 0); + gst_buffer_pool_config_set_params(config, caps, max_size, 0, 0); gst_buffer_pool_config_set_allocator(config, allocator, NULL); if (!gst_buffer_pool_set_config(GST_BUFFER_POOL(pool), config)) GST_ERROR("Failed to set %"GST_PTR_FORMAT" config.", pool); From 7a8d4754836f3b67f92eb3501e518e13a004dc2a Mon Sep 17 00:00:00 2001 From: Conor McCarthy Date: Thu, 21 Aug 2025 11:25:23 +1000 Subject: [PATCH 352/454] Revert "winegstreamer: HACK: Fix up NV12 alignment mismatch for decoder D3D-aware output." This reverts commit 88204f9c386f31cdf836a4f254657142f436db20. An upstream fix is now committed in 8339e4507. CW-Bug-Id: #25806 --- dlls/winegstreamer/video_decoder.c | 62 +----------------------------- 1 file changed, 2 insertions(+), 60 deletions(-) diff --git a/dlls/winegstreamer/video_decoder.c b/dlls/winegstreamer/video_decoder.c index f34ca7cbdcd6..f31679b8d08c 100644 --- a/dlls/winegstreamer/video_decoder.c +++ b/dlls/winegstreamer/video_decoder.c @@ -945,54 +945,11 @@ static HRESULT handle_stream_type_change(struct video_decoder *decoder) return MF_E_TRANSFORM_STREAM_CHANGE; } -static void buffer_fixup_nv12(IMFMediaBuffer *buffer, UINT dst_width, UINT src_width, UINT height) -{ - DWORD length = 0; - BYTE *src, *dst; - HRESULT hr; - UINT i; - - dst_width = (dst_width + 1) & ~1; - height = (height + 1) & ~1; - - IMFMediaBuffer_GetCurrentLength(buffer, &length); - if (length < (src_width + src_width / 2u) * height) - { - WARN("Unexpected buffer %p length %lu.\n", buffer, length); - return; - } - - if (FAILED(hr = IMFMediaBuffer_Lock(buffer, &src, NULL, NULL))) - { - WARN("Failed to lock buffer %p, hr %#lx.\n", buffer, hr); - return; - } - - for (i = 0, dst = src; i < height; ++i) - { - memmove(dst, src, dst_width); - dst += dst_width; - src += src_width; - } - for (i = 0; i < height; ++i) - { - memmove(dst, src, dst_width / 2u); - dst += dst_width / 2u; - src += src_width / 2u; - } - - IMFMediaBuffer_Unlock(buffer); - - if (FAILED(hr = IMFMediaBuffer_SetCurrentLength(buffer, (dst_width + dst_width / 2u) * height))) - WARN("Failed to set buffer %p length, hr %#lx.\n", buffer, hr); -} - static HRESULT WINAPI transform_ProcessOutput(IMFTransform *iface, DWORD flags, DWORD count, MFT_OUTPUT_DATA_BUFFER *samples, DWORD *status) { struct video_decoder *decoder = impl_from_IMFTransform(iface); - UINT32 sample_size, frame_width; - BOOL fixup_nv12 = FALSE; + UINT32 sample_size; LONGLONG duration, sample_duration; IMFSample *sample; UINT64 frame_size, frame_rate; @@ -1017,20 +974,7 @@ static HRESULT WINAPI transform_ProcessOutput(IMFTransform *iface, DWORD flags, return hr; if (FAILED(hr = IMFMediaType_GetUINT64(decoder->output_type, &MF_MT_FRAME_SIZE, &frame_size))) return hr; - frame_width = frame_size >> 32; - if ((frame_width & 3) && (frame_width & 3) != 3 && IsEqualGUID(&subtype, &MFVideoFormat_NV12)) - { - /* GStreamer uses a hard-coded 4-pixel width alignment for NV12. So far this has only been an issue - * when decoded from theora (for a cutscene in Resident Evil Village). Streams only provide samples - * to support D3D-awareness, which in Windows supports few compressed formats (theora support here - * is a hack). This issue could also occur when the sample is supplied externally, but there is no - * test case for it. This issue cannot occur with H.264 due to its own alignment requirement. */ - if (!(decoder->output_info.dwFlags & MFT_OUTPUT_STREAM_PROVIDES_SAMPLES)) - FIXME("Alignment fixup for external samples is unimplemented.\n"); - frame_width = (frame_width + 3) & ~3; - fixup_nv12 = TRUE; - } - if (FAILED(hr = MFCalculateImageSize(&subtype, frame_width, (UINT32)frame_size, &sample_size))) + if (FAILED(hr = MFCalculateImageSize(&subtype, frame_size >> 32, (UINT32)frame_size, &sample_size))) return hr; if (decoder->output_info.dwFlags & MFT_OUTPUT_STREAM_PROVIDES_SAMPLES) @@ -1095,8 +1039,6 @@ static HRESULT WINAPI transform_ProcessOutput(IMFTransform *iface, DWORD flags, if (decoder->output_info.dwFlags & MFT_OUTPUT_STREAM_PROVIDES_SAMPLES) { - if (fixup_nv12) - buffer_fixup_nv12(decoder->temp_buffer, frame_size >> 32, frame_width, (UINT32)frame_size); if (hr == S_OK && FAILED(hr = output_sample(decoder, &samples->pSample, sample))) ERR("Failed to output sample, hr %#lx.\n", hr); IMFSample_Release(sample); From 88af46dd94dc380e3ce9c4c03422e70e7d0a372e Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 19 Aug 2025 20:25:26 -0600 Subject: [PATCH 353/454] msvcp140_atomic_wait: Semi-stub __std_tzdb_get_time_zones() / __std_tzdb_delete_time_zones(). (cherry picked from commit b8a6dfff01323e4d5790592bae1c07c795db877c) CW-Bug-Id: #25816 --- dlls/msvcp140_atomic_wait/main.c | 62 +++++++++++++++++++ .../msvcp140_atomic_wait.spec | 4 +- .../tests/msvcp140_atomic_wait.c | 53 ++++++++++++++++ 3 files changed, 117 insertions(+), 2 deletions(-) diff --git a/dlls/msvcp140_atomic_wait/main.c b/dlls/msvcp140_atomic_wait/main.c index 52ecae486eb8..c683ea7f8cda 100644 --- a/dlls/msvcp140_atomic_wait/main.c +++ b/dlls/msvcp140_atomic_wait/main.c @@ -175,3 +175,65 @@ void __stdcall __std_free_crt(void *ptr) { free(ptr); } + +enum tzdb_error +{ + TZDB_ERROR_SUCCESS, + TZDB_ERROR_WIN, + TZDB_ERROR_ICU, +}; + +struct tzdb_time_zones +{ + enum tzdb_error error; + char *ver; + unsigned int count; + char **names; + char **links; +}; + +struct tzdb_time_zones * __stdcall __std_tzdb_get_time_zones(void) +{ + DYNAMIC_TIME_ZONE_INFORMATION tzd; + static char ver[] = "2022g"; + struct tzdb_time_zones *z; + unsigned int i, j; + + FIXME("returning Windows time zone names.\n"); + + z = calloc(1, sizeof(*z)); + while (!EnumDynamicTimeZoneInformation(z->count, &tzd)) + ++z->count; + + z->ver = ver; + z->names = calloc(z->count, sizeof(*z->names)); + z->links = calloc(z->count, sizeof(*z->links)); + + for (i = 0; i < z->count; ++i) + { + if (EnumDynamicTimeZoneInformation(i, &tzd)) + break; + z->names[i] = malloc(wcslen(tzd.StandardName) + 1); + j = 0; + while ((z->names[i][j] = tzd.StandardName[j])) + ++j; + } + z->count = i; + return z; +} + +void __stdcall __std_tzdb_delete_time_zones(struct tzdb_time_zones *z) +{ + unsigned int i; + + TRACE("(%p)\n", z); + + for (i = 0; i < z->count; ++i) + { + free(z->names[i]); + free(z->links[i]); + } + free(z->names); + free(z->links); + free(z); +} diff --git a/dlls/msvcp140_atomic_wait/msvcp140_atomic_wait.spec b/dlls/msvcp140_atomic_wait/msvcp140_atomic_wait.spec index 88ed59adad9b..08ccb27fcf7a 100644 --- a/dlls/msvcp140_atomic_wait/msvcp140_atomic_wait.spec +++ b/dlls/msvcp140_atomic_wait/msvcp140_atomic_wait.spec @@ -24,9 +24,9 @@ @ stub __std_tzdb_delete_current_zone @ stub __std_tzdb_delete_leap_seconds @ stub __std_tzdb_delete_sys_info -@ stub __std_tzdb_delete_time_zones +@ stdcall __std_tzdb_delete_time_zones(ptr) @ stub __std_tzdb_get_current_zone @ stub __std_tzdb_get_leap_seconds @ stub __std_tzdb_get_sys_info -@ stub __std_tzdb_get_time_zones +@ stdcall __std_tzdb_get_time_zones() @ stdcall __std_wait_for_threadpool_work_callbacks(ptr long) diff --git a/dlls/msvcp140_atomic_wait/tests/msvcp140_atomic_wait.c b/dlls/msvcp140_atomic_wait/tests/msvcp140_atomic_wait.c index 882e07c48dbd..a1c062686418 100644 --- a/dlls/msvcp140_atomic_wait/tests/msvcp140_atomic_wait.c +++ b/dlls/msvcp140_atomic_wait/tests/msvcp140_atomic_wait.c @@ -28,6 +28,22 @@ typedef struct SRWLOCK srwlock; } shared_mutex; +enum tzdb_error +{ + TZDB_ERROR_SUCCESS, + TZDB_ERROR_WIN, + TZDB_ERROR_ICU, +}; + +struct tzdb_time_zones +{ + enum tzdb_error error; + char *ver; + unsigned int count; + char **names; + char **links; +}; + static unsigned int (__stdcall *p___std_parallel_algorithms_hw_threads)(void); static void (__stdcall *p___std_bulk_submit_threadpool_work)(PTP_WORK, size_t); @@ -39,6 +55,8 @@ static BOOL (__stdcall *p___std_atomic_wait_direct)(volatile void*, void*, size_ static void (__stdcall *p___std_atomic_notify_one_direct)(void*); static shared_mutex* (__stdcall *p___std_acquire_shared_mutex_for_instance)(void*); static void (__stdcall *p___std_release_shared_mutex_for_instance)(void*); +static struct tzdb_time_zones * (__stdcall *p___std_tzdb_get_time_zones)(void); +static void (__stdcall *p___std_tzdb_delete_time_zones)(struct tzdb_time_zones *); #define SETNOFAIL(x,y) x = (void*)GetProcAddress(msvcp,y) #define SET(x,y) do { SETNOFAIL(x,y); ok(x != NULL, "Export '%s' not found\n", y); } while(0) @@ -60,6 +78,8 @@ static HMODULE init(void) SET(p___std_atomic_notify_one_direct, "__std_atomic_notify_one_direct"); SET(p___std_acquire_shared_mutex_for_instance, "__std_acquire_shared_mutex_for_instance"); SET(p___std_release_shared_mutex_for_instance, "__std_release_shared_mutex_for_instance"); + SET(p___std_tzdb_get_time_zones, "__std_tzdb_get_time_zones"); + SET(p___std_tzdb_delete_time_zones, "__std_tzdb_delete_time_zones"); return msvcp; } @@ -269,6 +289,38 @@ static void test___std_acquire_shared_mutex_for_instance(void) p___std_release_shared_mutex_for_instance(NULL); } +static void test___std_tzdb(void) +{ + struct tzdb_time_zones *z; + unsigned int i; + + if (!p___std_tzdb_get_time_zones) + { + win_skip("__std_tzdb_get_time_zones is not available, skipping tests.\n"); + return; + } + + z = p___std_tzdb_get_time_zones(); + ok(!!z, "got NULL.\n"); + ok(!z->error || broken(z->error == TZDB_ERROR_WIN && !z->ver && !z->count), "got %u.\n", z->error); + if (z->error) + { + win_skip("__std_tzdb_get_time_zones empty result, skipping remaining tests.\n"); + p___std_tzdb_delete_time_zones(z); + return; + } + ok(!!z->ver, "got NULL.\n"); + ok(z->count, "got 0.\n"); + + trace("ver %s.\n", debugstr_a(z->ver)); + for (i = 0; i < z->count; ++i) + { + ok(!!z->names[i], "got NULL.\n"); + } + + p___std_tzdb_delete_time_zones(z); +} + START_TEST(msvcp140_atomic_wait) { HMODULE msvcp; @@ -281,5 +333,6 @@ START_TEST(msvcp140_atomic_wait) test_threadpool_work(); test___std_atomic_wait_direct(); test___std_acquire_shared_mutex_for_instance(); + test___std_tzdb(); FreeLibrary(msvcp); } From 3806e23c36e9fb0ff4cd40a6e561876c49240fe5 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 19 Aug 2025 20:29:44 -0600 Subject: [PATCH 354/454] msvcp140_atomic_wait: Semi-stub __std_tzdb_get_current_zone() / __std_tzdb_delete_current_zone(). (cherry picked from commit e0a80caad470484c0d1cff0c4d10136b345daed8) CW-Bug-Id: #25816 --- dlls/msvcp140_atomic_wait/main.c | 37 +++++++++++++++++++ .../msvcp140_atomic_wait.spec | 4 +- .../tests/msvcp140_atomic_wait.c | 20 +++++++++- 3 files changed, 58 insertions(+), 3 deletions(-) diff --git a/dlls/msvcp140_atomic_wait/main.c b/dlls/msvcp140_atomic_wait/main.c index c683ea7f8cda..918088b6e086 100644 --- a/dlls/msvcp140_atomic_wait/main.c +++ b/dlls/msvcp140_atomic_wait/main.c @@ -192,6 +192,12 @@ struct tzdb_time_zones char **links; }; +struct tzdb_current_zone +{ + enum tzdb_error error; + char *name; +}; + struct tzdb_time_zones * __stdcall __std_tzdb_get_time_zones(void) { DYNAMIC_TIME_ZONE_INFORMATION tzd; @@ -237,3 +243,34 @@ void __stdcall __std_tzdb_delete_time_zones(struct tzdb_time_zones *z) free(z->links); free(z); } + +struct tzdb_current_zone * __stdcall __std_tzdb_get_current_zone(void) +{ + DYNAMIC_TIME_ZONE_INFORMATION tzd; + struct tzdb_current_zone *c; + unsigned int i; + + FIXME("returning Windows time zone name.\n"); + + c = calloc(1, sizeof(*c)); + + if (GetDynamicTimeZoneInformation(&tzd) == TIME_ZONE_ID_INVALID) + { + c->error = TZDB_ERROR_WIN; + return c; + } + + c->name = malloc(wcslen(tzd.StandardName) + 1); + i = 0; + while ((c->name[i] = tzd.StandardName[i])) + ++i; + return c; +} + +void __stdcall __std_tzdb_delete_current_zone(struct tzdb_current_zone *c) +{ + TRACE("(%p)\n", c); + + free(c->name); + free(c); +} diff --git a/dlls/msvcp140_atomic_wait/msvcp140_atomic_wait.spec b/dlls/msvcp140_atomic_wait/msvcp140_atomic_wait.spec index 08ccb27fcf7a..714420f4b5c1 100644 --- a/dlls/msvcp140_atomic_wait/msvcp140_atomic_wait.spec +++ b/dlls/msvcp140_atomic_wait/msvcp140_atomic_wait.spec @@ -21,11 +21,11 @@ @ stdcall __std_parallel_algorithms_hw_threads() @ stdcall __std_release_shared_mutex_for_instance(ptr) @ stdcall __std_submit_threadpool_work(ptr) -@ stub __std_tzdb_delete_current_zone +@ stdcall __std_tzdb_delete_current_zone(ptr) @ stub __std_tzdb_delete_leap_seconds @ stub __std_tzdb_delete_sys_info @ stdcall __std_tzdb_delete_time_zones(ptr) -@ stub __std_tzdb_get_current_zone +@ stdcall __std_tzdb_get_current_zone() @ stub __std_tzdb_get_leap_seconds @ stub __std_tzdb_get_sys_info @ stdcall __std_tzdb_get_time_zones() diff --git a/dlls/msvcp140_atomic_wait/tests/msvcp140_atomic_wait.c b/dlls/msvcp140_atomic_wait/tests/msvcp140_atomic_wait.c index a1c062686418..a679aa3de301 100644 --- a/dlls/msvcp140_atomic_wait/tests/msvcp140_atomic_wait.c +++ b/dlls/msvcp140_atomic_wait/tests/msvcp140_atomic_wait.c @@ -44,6 +44,12 @@ struct tzdb_time_zones char **links; }; +struct tzdb_current_zone +{ + enum tzdb_error error; + char *name; +}; + static unsigned int (__stdcall *p___std_parallel_algorithms_hw_threads)(void); static void (__stdcall *p___std_bulk_submit_threadpool_work)(PTP_WORK, size_t); @@ -57,6 +63,8 @@ static shared_mutex* (__stdcall *p___std_acquire_shared_mutex_for_instance)(void static void (__stdcall *p___std_release_shared_mutex_for_instance)(void*); static struct tzdb_time_zones * (__stdcall *p___std_tzdb_get_time_zones)(void); static void (__stdcall *p___std_tzdb_delete_time_zones)(struct tzdb_time_zones *); +static struct tzdb_current_zone * (__stdcall *p___std_tzdb_get_current_zone)(void); +static void (__stdcall *p___std_tzdb_delete_current_zone)(struct tzdb_current_zone *); #define SETNOFAIL(x,y) x = (void*)GetProcAddress(msvcp,y) #define SET(x,y) do { SETNOFAIL(x,y); ok(x != NULL, "Export '%s' not found\n", y); } while(0) @@ -80,6 +88,8 @@ static HMODULE init(void) SET(p___std_release_shared_mutex_for_instance, "__std_release_shared_mutex_for_instance"); SET(p___std_tzdb_get_time_zones, "__std_tzdb_get_time_zones"); SET(p___std_tzdb_delete_time_zones, "__std_tzdb_delete_time_zones"); + SET(p___std_tzdb_get_current_zone, "__std_tzdb_get_current_zone"); + SET(p___std_tzdb_delete_current_zone, "__std_tzdb_delete_current_zone"); return msvcp; } @@ -291,6 +301,7 @@ static void test___std_acquire_shared_mutex_for_instance(void) static void test___std_tzdb(void) { + struct tzdb_current_zone *c; struct tzdb_time_zones *z; unsigned int i; @@ -313,11 +324,18 @@ static void test___std_tzdb(void) ok(z->count, "got 0.\n"); trace("ver %s.\n", debugstr_a(z->ver)); + c = p___std_tzdb_get_current_zone(); + ok(!!c, "got NULL.\n"); + ok(!!c->name, "got NULL.\n"); + ok(!c->error, "got %u.\n", c->error); for (i = 0; i < z->count; ++i) { - ok(!!z->names[i], "got NULL.\n"); + if (!strcmp(c->name, z->names[i])) + break; } + ok(i < z->count, "current zone %s not found.\n", c->name); + p___std_tzdb_delete_current_zone(c); p___std_tzdb_delete_time_zones(z); } From 19e679e767c7b2fb1473de57c8c127e52ee20e82 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 19 Aug 2025 20:17:39 -0600 Subject: [PATCH 355/454] msvcp140_atomic_wait: Stub __std_tzdb_get_leap_seconds() / __std_tzdb_delete_leap_seconds(). (cherry picked from commit 76c74b362e64580d5bcaa77e5cfd103621b16795) CW-Bug-Id: #25816 --- dlls/msvcp140_atomic_wait/main.c | 26 +++++++++++++++++++ .../msvcp140_atomic_wait.spec | 4 +-- .../tests/msvcp140_atomic_wait.c | 23 ++++++++++++++++ 3 files changed, 51 insertions(+), 2 deletions(-) diff --git a/dlls/msvcp140_atomic_wait/main.c b/dlls/msvcp140_atomic_wait/main.c index 918088b6e086..b144da3f04c4 100644 --- a/dlls/msvcp140_atomic_wait/main.c +++ b/dlls/msvcp140_atomic_wait/main.c @@ -17,6 +17,7 @@ */ #include +#include "stdint.h" #include "windef.h" #include "winbase.h" #include "wine/debug.h" @@ -198,6 +199,16 @@ struct tzdb_current_zone char *name; }; +struct tzdb_leap_second +{ + uint16_t year; + uint16_t month; + uint16_t day; + uint16_t hour; + uint16_t negative; + uint16_t reserved; +}; + struct tzdb_time_zones * __stdcall __std_tzdb_get_time_zones(void) { DYNAMIC_TIME_ZONE_INFORMATION tzd; @@ -274,3 +285,18 @@ void __stdcall __std_tzdb_delete_current_zone(struct tzdb_current_zone *c) free(c->name); free(c); } + +struct tzdb_leap_second * __stdcall __std_tzdb_get_leap_seconds(size_t prev_size, size_t *new_size) +{ + FIXME("(%#Ix %p) stub\n", prev_size, new_size); + + *new_size = 0; + return NULL; +} + +void __stdcall __std_tzdb_delete_leap_seconds(struct tzdb_leap_second *l) +{ + TRACE("(%p)\n", l); + + free(l); +} diff --git a/dlls/msvcp140_atomic_wait/msvcp140_atomic_wait.spec b/dlls/msvcp140_atomic_wait/msvcp140_atomic_wait.spec index 714420f4b5c1..e5c9aeec6772 100644 --- a/dlls/msvcp140_atomic_wait/msvcp140_atomic_wait.spec +++ b/dlls/msvcp140_atomic_wait/msvcp140_atomic_wait.spec @@ -22,11 +22,11 @@ @ stdcall __std_release_shared_mutex_for_instance(ptr) @ stdcall __std_submit_threadpool_work(ptr) @ stdcall __std_tzdb_delete_current_zone(ptr) -@ stub __std_tzdb_delete_leap_seconds +@ stdcall __std_tzdb_delete_leap_seconds(ptr) @ stub __std_tzdb_delete_sys_info @ stdcall __std_tzdb_delete_time_zones(ptr) @ stdcall __std_tzdb_get_current_zone() -@ stub __std_tzdb_get_leap_seconds +@ stdcall __std_tzdb_get_leap_seconds(ptr ptr) @ stub __std_tzdb_get_sys_info @ stdcall __std_tzdb_get_time_zones() @ stdcall __std_wait_for_threadpool_work_callbacks(ptr long) diff --git a/dlls/msvcp140_atomic_wait/tests/msvcp140_atomic_wait.c b/dlls/msvcp140_atomic_wait/tests/msvcp140_atomic_wait.c index a679aa3de301..dd1c8a187737 100644 --- a/dlls/msvcp140_atomic_wait/tests/msvcp140_atomic_wait.c +++ b/dlls/msvcp140_atomic_wait/tests/msvcp140_atomic_wait.c @@ -19,6 +19,7 @@ #include #include +#include "stdint.h" #include "windef.h" #include "winbase.h" #include "wine/test.h" @@ -50,6 +51,16 @@ struct tzdb_current_zone char *name; }; +struct tzdb_leap_second +{ + uint16_t year; + uint16_t month; + uint16_t day; + uint16_t hour; + uint16_t negative; + uint16_t reserved; +}; + static unsigned int (__stdcall *p___std_parallel_algorithms_hw_threads)(void); static void (__stdcall *p___std_bulk_submit_threadpool_work)(PTP_WORK, size_t); @@ -65,6 +76,8 @@ static struct tzdb_time_zones * (__stdcall *p___std_tzdb_get_time_zones)(void); static void (__stdcall *p___std_tzdb_delete_time_zones)(struct tzdb_time_zones *); static struct tzdb_current_zone * (__stdcall *p___std_tzdb_get_current_zone)(void); static void (__stdcall *p___std_tzdb_delete_current_zone)(struct tzdb_current_zone *); +static struct tzdb_leap_second * (__stdcall *p___std_tzdb_get_leap_seconds)(size_t, size_t *); +static void (__stdcall *p___std_tzdb_delete_leap_seconds)(struct tzdb_leap_second *); #define SETNOFAIL(x,y) x = (void*)GetProcAddress(msvcp,y) #define SET(x,y) do { SETNOFAIL(x,y); ok(x != NULL, "Export '%s' not found\n", y); } while(0) @@ -90,6 +103,8 @@ static HMODULE init(void) SET(p___std_tzdb_delete_time_zones, "__std_tzdb_delete_time_zones"); SET(p___std_tzdb_get_current_zone, "__std_tzdb_get_current_zone"); SET(p___std_tzdb_delete_current_zone, "__std_tzdb_delete_current_zone"); + SET(p___std_tzdb_get_leap_seconds, "__std_tzdb_get_leap_seconds"); + SET(p___std_tzdb_delete_leap_seconds, "__std_tzdb_delete_leap_seconds"); return msvcp; } @@ -303,7 +318,9 @@ static void test___std_tzdb(void) { struct tzdb_current_zone *c; struct tzdb_time_zones *z; + void *leap_seconds; unsigned int i; + size_t size; if (!p___std_tzdb_get_time_zones) { @@ -337,6 +354,12 @@ static void test___std_tzdb(void) p___std_tzdb_delete_current_zone(c); p___std_tzdb_delete_time_zones(z); + + size = 0xdeadbeef; + leap_seconds = p___std_tzdb_get_leap_seconds(100, &size); + ok(!leap_seconds, "got %p.\n", leap_seconds); + ok(!size, "got %#Ix.\n", size); + p___std_tzdb_delete_leap_seconds(leap_seconds); } START_TEST(msvcp140_atomic_wait) From 27292f727a52f27cbdd008da9ca70afb71387985 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 21 Jul 2025 12:31:53 -0600 Subject: [PATCH 356/454] ntdll/tests: Add more tests for NtQueryVirtualMemory( MemoryRegionInformation ). (cherry picked from commit 212be298dbc0454dbe8cf119dd9b9c137b0d0793) --- dlls/ntdll/tests/virtual.c | 134 ++++++++++++++++++++++++++++++++++++- 1 file changed, 133 insertions(+), 1 deletion(-) diff --git a/dlls/ntdll/tests/virtual.c b/dlls/ntdll/tests/virtual.c index 48ba57fd7ad7..d8e8bbc79a13 100644 --- a/dlls/ntdll/tests/virtual.c +++ b/dlls/ntdll/tests/virtual.c @@ -293,6 +293,7 @@ static void check_region_size_(void *p, SIZE_T s, unsigned int line) static void test_NtAllocateVirtualMemoryEx(void) { + MEMORY_REGION_INFORMATION mri; MEMORY_BASIC_INFORMATION mbi; MEM_EXTENDED_PARAMETER ext[2]; char *p, *p1, *p2, *p3; @@ -610,6 +611,35 @@ static void test_NtAllocateVirtualMemoryEx(void) ok(p2 == p1 + size / 2, "Unexpected addr %p, expected %p.\n", p2, p1 + size / 2); check_region_size(p1, size / 2); check_region_size(p2, size / 2); + + status = NtQueryVirtualMemory( NtCurrentProcess(), p1, MemoryBasicInformation, &mbi, sizeof(mbi), NULL ); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status ); + ok( mbi.AllocationBase == p1, "got %p.\n", mbi.AllocationBase ); + ok( mbi.Type == MEM_PRIVATE, "got %#lx.\n", mbi.Type ); + ok( mbi.State == MEM_RESERVE, "got %#lx.\n", mbi.State ); + ok( mbi.RegionSize == size / 2, "Unexpected size %Iu, expected %Iu.\n", mbi.RegionSize, size / 2 ); + ok( mbi.AllocationProtect == PAGE_NOACCESS, "got %#lx.\n", mbi.AllocationProtect ); + status = NtQueryVirtualMemory( NtCurrentProcess(), p1, MemoryRegionInformation, &mri, sizeof(mri), NULL ); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status ); + ok( mri.AllocationBase == p1, "got %p.\n", mri.AllocationBase ); + ok( mri.RegionSize == size / 2, "Unexpected size %Iu, expected %Iu.\n", mri.RegionSize, size / 2 ); + ok( !mri.CommitSize, "Unexpected size %Iu.\n", mri.CommitSize ); + ok( mri.AllocationProtect == PAGE_NOACCESS, "got %#lx.\n", mri.AllocationProtect ); + + status = NtQueryVirtualMemory( NtCurrentProcess(), p2, MemoryBasicInformation, &mbi, sizeof(mbi), NULL ); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status ); + ok( mbi.AllocationBase == p2, "got %p.\n", mbi.AllocationBase ); + ok( mbi.Type == MEM_PRIVATE, "got %#lx.\n", mbi.Type ); + ok( mbi.State == MEM_RESERVE, "got %#lx.\n", mbi.State ); + ok( mbi.RegionSize == size / 2, "Unexpected size %Iu, expected %Iu.\n", mbi.RegionSize, size / 2 ); + ok( mbi.AllocationProtect == PAGE_NOACCESS, "got %#lx.\n", mbi.AllocationProtect ); + status = NtQueryVirtualMemory( NtCurrentProcess(), p2, MemoryRegionInformation, &mri, sizeof(mri), NULL ); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status ); + ok( mri.AllocationBase == p2, "got %p.\n", mri.AllocationBase ); + ok( mri.RegionSize == size / 2, "Unexpected size %Iu, expected %Iu.\n", mri.RegionSize, size / 2 ); + ok( !mri.CommitSize, "Unexpected size %Iu.\n", mri.CommitSize ); + ok( mri.AllocationProtect == PAGE_NOACCESS, "got %#lx.\n", mri.AllocationProtect ); + status = NtFreeVirtualMemory(NtCurrentProcess(), (void **)&p1, &size2, MEM_RELEASE); ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); ok(size2 == 0x8000, "Unexpected size %#Ix.\n", size2); @@ -2430,7 +2460,8 @@ static void test_query_region_information(void) SIZE_T len, size; NTSTATUS status; HANDLE mapping; - void *ptr; + void *ptr, *addr; + ULONG old; size = 0x10000; ptr = NULL; @@ -2462,6 +2493,7 @@ static void test_query_region_information(void) ok(!info.MappedPhysical, "Unexpected flag %d.\n", info.MappedPhysical); ok(!info.DirectMapped, "Unexpected flag %d.\n", info.DirectMapped); ok(info.RegionSize == size, "Unexpected region size.\n"); + ok(!info.CommitSize, "Unexpected commit size %#Ix.\n", info.CommitSize); size = 0; status = NtFreeVirtualMemory(NtCurrentProcess(), &ptr, &size, MEM_RELEASE); @@ -2485,11 +2517,49 @@ static void test_query_region_information(void) ok(!info.MappedPhysical, "Unexpected flag %d.\n", info.MappedPhysical); ok(!info.DirectMapped, "Unexpected flag %d.\n", info.DirectMapped); ok(info.RegionSize == size, "Unexpected region size.\n"); + ok(info.CommitSize == size, "Unexpected commit size %#Ix.\n", info.CommitSize); + + addr = (char *)ptr + 0x1000; + size = 0x1000; + status = NtProtectVirtualMemory(NtCurrentProcess(), &addr, &size, PAGE_NOACCESS, &old ); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + status = NtQueryVirtualMemory(NtCurrentProcess(), ptr, MemoryRegionInformation, &info, sizeof(info), &len); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + ok(info.AllocationBase == ptr, "Unexpected base %p.\n", info.AllocationBase); + ok(info.AllocationProtect == PAGE_READWRITE, "Unexpected protection %lu.\n", info.AllocationProtect); + ok(!info.Private, "Unexpected flag %d.\n", info.Private); + ok(!info.MappedDataFile, "Unexpected flag %d.\n", info.MappedDataFile); + ok(!info.MappedImage, "Unexpected flag %d.\n", info.MappedImage); + ok(!info.MappedPageFile, "Unexpected flag %d.\n", info.MappedPageFile); + ok(!info.MappedPhysical, "Unexpected flag %d.\n", info.MappedPhysical); + ok(!info.DirectMapped, "Unexpected flag %d.\n", info.DirectMapped); + todo_wine ok(info.RegionSize == 0x10000, "Unexpected region size %#Ix.\n", info.RegionSize); + todo_wine ok(info.CommitSize == 0x10000, "Unexpected commit size %#Ix.\n", info.CommitSize); + + status = NtQueryVirtualMemory(NtCurrentProcess(), (char *)ptr + 0x1000, MemoryRegionInformation, &info, sizeof(info), &len); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + ok(info.AllocationBase == ptr, "Unexpected base %p.\n", info.AllocationBase); + ok(info.AllocationProtect == PAGE_READWRITE, "Unexpected protection %lu.\n", info.AllocationProtect); + ok(!info.Private, "Unexpected flag %d.\n", info.Private); + ok(!info.MappedDataFile, "Unexpected flag %d.\n", info.MappedDataFile); + ok(!info.MappedImage, "Unexpected flag %d.\n", info.MappedImage); + ok(!info.MappedPageFile, "Unexpected flag %d.\n", info.MappedPageFile); + ok(!info.MappedPhysical, "Unexpected flag %d.\n", info.MappedPhysical); + ok(!info.DirectMapped, "Unexpected flag %d.\n", info.DirectMapped); + todo_wine ok(info.RegionSize == 0x10000, "Unexpected region size %#Ix.\n", info.RegionSize); + todo_wine ok(info.CommitSize == 0x10000, "Unexpected commit size %#Ix.\n", info.CommitSize); size = 0; status = NtFreeVirtualMemory(NtCurrentProcess(), &ptr, &size, MEM_RELEASE); ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + memset(&info, 0xcc, sizeof(info)); + status = NtQueryVirtualMemory(NtCurrentProcess(), ptr, MemoryRegionInformation, &info, sizeof(info), &len); + todo_wine ok(status == STATUS_INVALID_ADDRESS, "Unexpected status %08lx.\n", status); + todo_wine ok(info.AllocationBase == (void *)(ULONG_PTR)0xcccccccccccccccc, "got %p.\n", info.AllocationBase); + todo_wine ok(info.AllocationProtect == 0xcccccccc, "Unexpected protection %lu.\n", info.AllocationProtect); + todo_wine ok(info.RegionType == 0xcccccccc, "got %#lx.\n", info.RegionType); + /* Pagefile mapping */ mapping = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, 4096, NULL); ok(mapping != 0, "CreateFileMapping failed\n"); @@ -2512,6 +2582,68 @@ static void test_query_region_information(void) ok(!info.MappedPhysical, "Unexpected flag %d.\n", info.MappedPhysical); ok(!info.DirectMapped, "Unexpected flag %d.\n", info.DirectMapped); ok(info.RegionSize == 4096, "Unexpected region size.\n"); + todo_wine ok(!info.CommitSize, "Unexpected commit size %#Ix.\n", info.CommitSize); + + status = NtUnmapViewOfSection(NtCurrentProcess(), ptr); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + + ptr = NULL; + size = 0; + offset.QuadPart = 0; + status = NtMapViewOfSection(mapping, NtCurrentProcess(), &ptr, 0, 0, &offset, &size, 1, 0, PAGE_WRITECOPY); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + + memset(&info, 0x11, sizeof(info)); + status = NtQueryVirtualMemory(NtCurrentProcess(), ptr, MemoryRegionInformation, &info, sizeof(info), &len); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + ok(info.AllocationBase == ptr, "Unexpected base %p.\n", info.AllocationBase); + ok(info.AllocationProtect == PAGE_WRITECOPY, "Unexpected protection %lu.\n", info.AllocationProtect); + ok(!info.Private, "Unexpected flag %d.\n", info.Private); + ok(!info.MappedDataFile, "Unexpected flag %d.\n", info.MappedDataFile); + ok(!info.MappedImage, "Unexpected flag %d.\n", info.MappedImage); + ok(!info.MappedPageFile, "Unexpected flag %d.\n", info.MappedPageFile); + ok(!info.MappedPhysical, "Unexpected flag %d.\n", info.MappedPhysical); + ok(!info.DirectMapped, "Unexpected flag %d.\n", info.DirectMapped); + ok(info.RegionSize == 4096, "Unexpected region size.\n"); + ok(info.CommitSize == 4096, "Unexpected commit size %#Ix.\n", info.CommitSize); + + *(int *)ptr = 1; + memset(&info, 0x11, sizeof(info)); + status = NtQueryVirtualMemory(NtCurrentProcess(), ptr, MemoryRegionInformation, &info, sizeof(info), &len); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + ok(info.AllocationBase == ptr, "Unexpected base %p.\n", info.AllocationBase); + ok(info.AllocationProtect == PAGE_WRITECOPY, "Unexpected protection %lu.\n", info.AllocationProtect); + ok(!info.Private, "Unexpected flag %d.\n", info.Private); + ok(!info.MappedDataFile, "Unexpected flag %d.\n", info.MappedDataFile); + ok(!info.MappedImage, "Unexpected flag %d.\n", info.MappedImage); + ok(!info.MappedPageFile, "Unexpected flag %d.\n", info.MappedPageFile); + ok(!info.MappedPhysical, "Unexpected flag %d.\n", info.MappedPhysical); + ok(!info.DirectMapped, "Unexpected flag %d.\n", info.DirectMapped); + ok(info.RegionSize == 4096, "Unexpected region size.\n"); + ok(info.CommitSize == 4096, "Unexpected commit size %#Ix.\n", info.CommitSize); + + status = NtUnmapViewOfSection(NtCurrentProcess(), ptr); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + + ptr = NULL; + size = 0; + offset.QuadPart = 0; + status = NtMapViewOfSection(mapping, NtCurrentProcess(), &ptr, 0, 0, &offset, &size, 1, 0, PAGE_READWRITE); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + + memset(&info, 0x11, sizeof(info)); + status = NtQueryVirtualMemory(NtCurrentProcess(), ptr, MemoryRegionInformation, &info, sizeof(info), &len); + ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + ok(info.AllocationBase == ptr, "Unexpected base %p.\n", info.AllocationBase); + ok(info.AllocationProtect == PAGE_READWRITE, "Unexpected protection %lu.\n", info.AllocationProtect); + ok(!info.Private, "Unexpected flag %d.\n", info.Private); + ok(!info.MappedDataFile, "Unexpected flag %d.\n", info.MappedDataFile); + ok(!info.MappedImage, "Unexpected flag %d.\n", info.MappedImage); + ok(!info.MappedPageFile, "Unexpected flag %d.\n", info.MappedPageFile); + ok(!info.MappedPhysical, "Unexpected flag %d.\n", info.MappedPhysical); + ok(!info.DirectMapped, "Unexpected flag %d.\n", info.DirectMapped); + ok(info.RegionSize == 4096, "Unexpected region size.\n"); + todo_wine ok(!info.CommitSize, "Unexpected commit size %#Ix.\n", info.CommitSize); status = NtUnmapViewOfSection(NtCurrentProcess(), ptr); ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); From 98461da0e6fef488f21274eb6bff5f6981a7d0b9 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 21 Jul 2025 15:48:49 -0600 Subject: [PATCH 357/454] ntdll: Factor out get_memory_region_size() function. (cherry picked from commit a0aef2322b8077184591ee436450921d49a6d6f7) --- dlls/ntdll/unix/virtual.c | 145 +++++++++++++++++++++----------------- 1 file changed, 80 insertions(+), 65 deletions(-) diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c index 6684f53ef4fc..d38dd4f80fb5 100644 --- a/dlls/ntdll/unix/virtual.c +++ b/dlls/ntdll/unix/virtual.c @@ -5701,105 +5701,120 @@ NTSTATUS WINAPI NtProtectVirtualMemory( HANDLE process, PVOID *addr_ptr, SIZE_T } -static unsigned int fill_basic_memory_info( const void *addr, MEMORY_BASIC_INFORMATION *info ) +static struct file_view *get_memory_region_size( char *base, char **region_start, char **region_end, + BOOL *fake_reserved ) { - char *base, *alloc_base = 0, *alloc_end = working_set_limit; struct wine_rb_entry *ptr; struct file_view *view; - sigset_t sigset; - base = ROUND_ADDR( addr, page_mask ); - - if (is_beyond_limit( base, 1, working_set_limit )) return STATUS_INVALID_PARAMETER; + *fake_reserved = FALSE; + *region_start = NULL; + *region_end = working_set_limit; - /* Find the view containing the address */ - - server_enter_uninterrupted_section( &virtual_mutex, &sigset ); ptr = views_tree.root; while (ptr) { view = WINE_RB_ENTRY_VALUE( ptr, struct file_view, entry ); if ((char *)view->base > base) { - alloc_end = view->base; + *region_end = view->base; ptr = ptr->left; } else if ((char *)view->base + view->size <= base) { - alloc_base = (char *)view->base + view->size; + *region_start = (char *)view->base + view->size; ptr = ptr->right; } else { - alloc_base = view->base; - alloc_end = (char *)view->base + view->size; - break; + *region_start = view->base; + *region_end = (char *)view->base + view->size; + return view; } } - - /* Fill the info structure */ - - info->BaseAddress = base; - info->RegionSize = alloc_end - base; - - if (!ptr) +#ifdef __i386__ { - info->State = MEM_FREE; - info->Protect = PAGE_NOACCESS; - info->AllocationBase = 0; - info->AllocationProtect = 0; - info->Type = 0; + struct reserved_area *area; -#ifdef __i386__ /* on i386, pretend that space outside of a reserved area is allocated, * so that the app doesn't believe it's fully available */ + LIST_FOR_EACH_ENTRY( area, &reserved_areas, struct reserved_area, entry ) { - struct reserved_area *area; - BOOL in_reserved = FALSE; + char *area_start = area->base; + char *area_end = area_start + area->size; - LIST_FOR_EACH_ENTRY( area, &reserved_areas, struct reserved_area, entry ) + if (area_end <= base) + { + if (*region_start < area_end) *region_start = area_end; + continue; + } + if (area_start <= base || area_start <= (char *)address_space_start) { - char *area_start = area->base; - char *area_end = (char *)area_start + area->size; + if (area_end < *region_end) *region_end = area_end; + return NULL; + } + /* report the remaining part of the 64K after the view as free */ + if ((UINT_PTR)*region_start & granularity_mask) + { + char *next = (char *)ROUND_ADDR( *region_start, granularity_mask ) + granularity_mask + 1; - if (area_end <= base) - { - if (alloc_base < area_end) alloc_base = area_end; - continue; - } - if (area_start <= base || area_start <= (char *)address_space_start) - { - if (area_end < alloc_end) info->RegionSize = area_end - base; - in_reserved = TRUE; - break; - } - /* report the remaining part of the 64K after the view as free */ - if ((UINT_PTR)alloc_base & granularity_mask) + if (base < next) { - char *next = (char *)ROUND_ADDR( alloc_base, granularity_mask ) + granularity_mask + 1; - - if (base < next) - { - info->RegionSize = min( next, alloc_end ) - base; - in_reserved = TRUE; - break; - } - else alloc_base = base; + *region_end = min( next, *region_end ); + return NULL; } - /* pretend it's allocated */ - if (area_start < alloc_end) info->RegionSize = area_start - base; - break; - } - if (!in_reserved) - { - info->State = MEM_RESERVE; - info->Protect = PAGE_NOACCESS; - info->AllocationBase = alloc_base; - info->AllocationProtect = PAGE_NOACCESS; - info->Type = MEM_PRIVATE; + else *region_start = base; } + /* pretend it's allocated */ + if (area_start < *region_end) *region_end = area_start; + break; } + *fake_reserved = TRUE; + } #endif + return NULL; +} + + +static unsigned int fill_basic_memory_info( const void *addr, MEMORY_BASIC_INFORMATION *info ) +{ + char *base, *alloc_base, *alloc_end; + struct file_view *view; + BOOL fake_reserved; + sigset_t sigset; + + base = ROUND_ADDR( addr, page_mask ); + + if (is_beyond_limit( base, 1, working_set_limit )) return STATUS_INVALID_PARAMETER; + + /* Find the view containing the address */ + + server_enter_uninterrupted_section( &virtual_mutex, &sigset ); + view = get_memory_region_size( base, &alloc_base, &alloc_end, &fake_reserved ); + + /* Fill the info structure */ + + info->BaseAddress = base; + info->RegionSize = alloc_end - base; + + if (!view) + { + if (fake_reserved) + { + info->State = MEM_RESERVE; + info->Protect = PAGE_NOACCESS; + info->AllocationBase = alloc_base; + info->AllocationProtect = PAGE_NOACCESS; + info->Type = MEM_PRIVATE; + } + else + { + info->State = MEM_FREE; + info->Protect = PAGE_NOACCESS; + info->AllocationBase = 0; + info->AllocationProtect = 0; + info->Type = 0; + } } else { From aaa14cea86ef622090c640cfa9c6edf1935c1186 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 21 Jul 2025 16:20:43 -0600 Subject: [PATCH 358/454] ntdll: Reimplement get_memory_region_info() on top of get_memory_region_size(). (cherry picked from commit e019c52a3b8670709a4ecb7a8b7f6d06c1b50313) --- dlls/ntdll/tests/virtual.c | 25 +++++++++-------- dlls/ntdll/unix/virtual.c | 57 +++++++++++++++++++++++++++++++------- 2 files changed, 60 insertions(+), 22 deletions(-) diff --git a/dlls/ntdll/tests/virtual.c b/dlls/ntdll/tests/virtual.c index d8e8bbc79a13..014c4ec07f5d 100644 --- a/dlls/ntdll/tests/virtual.c +++ b/dlls/ntdll/tests/virtual.c @@ -2533,8 +2533,8 @@ static void test_query_region_information(void) ok(!info.MappedPageFile, "Unexpected flag %d.\n", info.MappedPageFile); ok(!info.MappedPhysical, "Unexpected flag %d.\n", info.MappedPhysical); ok(!info.DirectMapped, "Unexpected flag %d.\n", info.DirectMapped); - todo_wine ok(info.RegionSize == 0x10000, "Unexpected region size %#Ix.\n", info.RegionSize); - todo_wine ok(info.CommitSize == 0x10000, "Unexpected commit size %#Ix.\n", info.CommitSize); + ok(info.RegionSize == 0x10000, "Unexpected region size %#Ix.\n", info.RegionSize); + ok(info.CommitSize == 0x10000, "Unexpected commit size %#Ix.\n", info.CommitSize); status = NtQueryVirtualMemory(NtCurrentProcess(), (char *)ptr + 0x1000, MemoryRegionInformation, &info, sizeof(info), &len); ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); @@ -2546,8 +2546,8 @@ static void test_query_region_information(void) ok(!info.MappedPageFile, "Unexpected flag %d.\n", info.MappedPageFile); ok(!info.MappedPhysical, "Unexpected flag %d.\n", info.MappedPhysical); ok(!info.DirectMapped, "Unexpected flag %d.\n", info.DirectMapped); - todo_wine ok(info.RegionSize == 0x10000, "Unexpected region size %#Ix.\n", info.RegionSize); - todo_wine ok(info.CommitSize == 0x10000, "Unexpected commit size %#Ix.\n", info.CommitSize); + ok(info.RegionSize == 0x10000, "Unexpected region size %#Ix.\n", info.RegionSize); + ok(info.CommitSize == 0x10000, "Unexpected commit size %#Ix.\n", info.CommitSize); size = 0; status = NtFreeVirtualMemory(NtCurrentProcess(), &ptr, &size, MEM_RELEASE); @@ -2555,13 +2555,13 @@ static void test_query_region_information(void) memset(&info, 0xcc, sizeof(info)); status = NtQueryVirtualMemory(NtCurrentProcess(), ptr, MemoryRegionInformation, &info, sizeof(info), &len); - todo_wine ok(status == STATUS_INVALID_ADDRESS, "Unexpected status %08lx.\n", status); - todo_wine ok(info.AllocationBase == (void *)(ULONG_PTR)0xcccccccccccccccc, "got %p.\n", info.AllocationBase); - todo_wine ok(info.AllocationProtect == 0xcccccccc, "Unexpected protection %lu.\n", info.AllocationProtect); - todo_wine ok(info.RegionType == 0xcccccccc, "got %#lx.\n", info.RegionType); + ok(status == STATUS_INVALID_ADDRESS, "Unexpected status %08lx.\n", status); + ok(info.AllocationBase == (void *)(ULONG_PTR)0xcccccccccccccccc, "got %p.\n", info.AllocationBase); + ok(info.AllocationProtect == 0xcccccccc, "Unexpected protection %lu.\n", info.AllocationProtect); + ok(info.RegionType == 0xcccccccc, "got %#lx.\n", info.RegionType); /* Pagefile mapping */ - mapping = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, 4096, NULL); + mapping = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE | SEC_COMMIT, 0, 4096, NULL); ok(mapping != 0, "CreateFileMapping failed\n"); ptr = NULL; @@ -2582,7 +2582,7 @@ static void test_query_region_information(void) ok(!info.MappedPhysical, "Unexpected flag %d.\n", info.MappedPhysical); ok(!info.DirectMapped, "Unexpected flag %d.\n", info.DirectMapped); ok(info.RegionSize == 4096, "Unexpected region size.\n"); - todo_wine ok(!info.CommitSize, "Unexpected commit size %#Ix.\n", info.CommitSize); + ok(!info.CommitSize, "Unexpected commit size %#Ix.\n", info.CommitSize); status = NtUnmapViewOfSection(NtCurrentProcess(), ptr); ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); @@ -2607,7 +2607,7 @@ static void test_query_region_information(void) ok(info.RegionSize == 4096, "Unexpected region size.\n"); ok(info.CommitSize == 4096, "Unexpected commit size %#Ix.\n", info.CommitSize); - *(int *)ptr = 1; + *(volatile int *)ptr = 1; memset(&info, 0x11, sizeof(info)); status = NtQueryVirtualMemory(NtCurrentProcess(), ptr, MemoryRegionInformation, &info, sizeof(info), &len); ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); @@ -2630,6 +2630,7 @@ static void test_query_region_information(void) offset.QuadPart = 0; status = NtMapViewOfSection(mapping, NtCurrentProcess(), &ptr, 0, 0, &offset, &size, 1, 0, PAGE_READWRITE); ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + *(volatile int *)ptr = 1; memset(&info, 0x11, sizeof(info)); status = NtQueryVirtualMemory(NtCurrentProcess(), ptr, MemoryRegionInformation, &info, sizeof(info), &len); @@ -2643,7 +2644,7 @@ static void test_query_region_information(void) ok(!info.MappedPhysical, "Unexpected flag %d.\n", info.MappedPhysical); ok(!info.DirectMapped, "Unexpected flag %d.\n", info.DirectMapped); ok(info.RegionSize == 4096, "Unexpected region size.\n"); - todo_wine ok(!info.CommitSize, "Unexpected commit size %#Ix.\n", info.CommitSize); + ok(!info.CommitSize, "Unexpected commit size %#Ix.\n", info.CommitSize); status = NtUnmapViewOfSection(NtCurrentProcess(), ptr); ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c index d38dd4f80fb5..64e56c36a782 100644 --- a/dlls/ntdll/unix/virtual.c +++ b/dlls/ntdll/unix/virtual.c @@ -5881,8 +5881,12 @@ static unsigned int get_basic_memory_info( HANDLE process, LPCVOID addr, static unsigned int get_memory_region_info( HANDLE process, LPCVOID addr, MEMORY_REGION_INFORMATION *info, SIZE_T len, SIZE_T *res_len ) { - MEMORY_BASIC_INFORMATION basic_info; - unsigned int status; + char *base, *region_start, *region_end; + struct file_view *view; + BYTE vprot, vprot_mask; + BOOL fake_reserved; + sigset_t sigset; + SIZE_T size; if (len < FIELD_OFFSET(MEMORY_REGION_INFORMATION, CommitSize)) return STATUS_INFO_LENGTH_MISMATCH; @@ -5893,15 +5897,48 @@ static unsigned int get_memory_region_info( HANDLE process, LPCVOID addr, MEMORY return STATUS_NOT_IMPLEMENTED; } - if ((status = fill_basic_memory_info( addr, &basic_info ))) return status; + base = ROUND_ADDR( addr, page_mask ); + + if (is_beyond_limit( base, 1, working_set_limit )) return STATUS_INVALID_PARAMETER; + + server_enter_uninterrupted_section( &virtual_mutex, &sigset ); + + if ((view = get_memory_region_size( base, ®ion_start, ®ion_end, &fake_reserved ))) + { + info->AllocationBase = view->base; + info->AllocationProtect = get_win32_prot( view->protect, view->protect ); + info->RegionType = 0; /* FIXME */ + if (len >= FIELD_OFFSET(MEMORY_REGION_INFORMATION, CommitSize)) + info->RegionSize = view->size; + if (len >= FIELD_OFFSET(MEMORY_REGION_INFORMATION, PartitionId)) + { + base = region_start; + info->CommitSize = 0; + vprot_mask = VPROT_COMMITTED; + if (!is_view_valloc( view )) vprot_mask |= PAGE_WRITECOPY; + while (base != region_end && + (size = get_committed_size( view, base, ~(size_t)0, &vprot, vprot_mask ))) + { + if ((vprot & vprot_mask) == vprot_mask) info->CommitSize += size; + base += size; + } + } + } + else + { + if (!fake_reserved) + { + server_leave_uninterrupted_section( &virtual_mutex, &sigset ); + return STATUS_INVALID_ADDRESS; + } + info->AllocationBase = region_start; + info->AllocationProtect = PAGE_NOACCESS; + info->RegionType = 0; /* FIXME */ + info->RegionSize = region_end - region_start; + info->CommitSize = 0; + } - info->AllocationBase = basic_info.AllocationBase; - info->AllocationProtect = basic_info.AllocationProtect; - info->RegionType = 0; /* FIXME */ - if (len >= FIELD_OFFSET(MEMORY_REGION_INFORMATION, CommitSize)) - info->RegionSize = basic_info.RegionSize; - if (len >= FIELD_OFFSET(MEMORY_REGION_INFORMATION, PartitionId)) - info->CommitSize = basic_info.State == MEM_COMMIT ? basic_info.RegionSize : 0; + server_leave_uninterrupted_section( &virtual_mutex, &sigset ); if (res_len) *res_len = sizeof(*info); return STATUS_SUCCESS; From 47cff9e6973f1b2fe17cac50f5398ac050164242 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 12 Aug 2025 16:24:47 -0600 Subject: [PATCH 359/454] ntdll: Factor out chksum_add() function. (cherry picked from commit 0bdb575ff0a7f327b6d620b1735b85a5e3e693eb) --- dlls/ntdll/unix/socket.c | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/dlls/ntdll/unix/socket.c b/dlls/ntdll/unix/socket.c index 74ff284e9208..23a82c760b5e 100644 --- a/dlls/ntdll/unix/socket.c +++ b/dlls/ntdll/unix/socket.c @@ -590,12 +590,12 @@ struct icmp_hdr } un; }; -/* rfc 1071 checksum */ -static unsigned short chksum(BYTE *data, unsigned int count) +static unsigned int chksum_add( BYTE *data, unsigned int count, unsigned int sum ) { - unsigned int sum = 0, carry = 0; - unsigned short check, s; + unsigned int carry = 0; + unsigned short s; + assert( !(count % 2) ); while (count > 1) { s = *(unsigned short *)data; @@ -606,9 +606,18 @@ static unsigned short chksum(BYTE *data, unsigned int count) count -= 2; } sum += carry; /* This won't produce another carry */ + return sum; +} + +/* rfc 1071 checksum */ +static unsigned short chksum( BYTE *data, unsigned int count, unsigned int sum ) +{ + unsigned short check; + + sum = chksum_add( data, count & ~1u, sum ); sum = (sum & 0xffff) + (sum >> 16); - if (count) sum += *data; /* LE-only */ + if (count % 2) sum += data[count - 1]; /* LE-only */ sum = (sum & 0xffff) + (sum >> 16); /* fold in any carry */ @@ -702,10 +711,10 @@ static ssize_t fixup_icmp_over_dgram( struct msghdr *hdr, union unix_sockaddr *u if (!fixup_status) { icmp_h->checksum = 0; - icmp_h->checksum = chksum( (BYTE *)icmp_h, recv_len - sizeof(ip_h) ); + icmp_h->checksum = chksum( (BYTE *)icmp_h, recv_len - sizeof(ip_h), 0 ); } } - ip_h.checksum = chksum( (BYTE *)&ip_h, sizeof(ip_h) ); + ip_h.checksum = chksum( (BYTE *)&ip_h, sizeof(ip_h), 0 ); memcpy( buf, &ip_h, min( sizeof(ip_h), buf_len )); return recv_len; From 71a512f3a693df59d5cc6ba9a222d84a5c1068d2 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 12 Aug 2025 16:23:30 -0600 Subject: [PATCH 360/454] ntdll: Support SOCK_RAW / IPPROTO_ICMPV6 fallback over SOCK_DGRAM. (cherry picked from commit 32598719671917655832d95402461c93919ec79b) --- dlls/ntdll/unix/socket.c | 68 +++++++++++++++++++++++++++++++++++++++- server/sock.c | 22 +++++++++---- 2 files changed, 83 insertions(+), 7 deletions(-) diff --git a/dlls/ntdll/unix/socket.c b/dlls/ntdll/unix/socket.c index 23a82c760b5e..df786711f324 100644 --- a/dlls/ntdll/unix/socket.c +++ b/dlls/ntdll/unix/socket.c @@ -575,6 +575,15 @@ struct ip_hdr ULONG daddr; }; +struct ipv6_pseudo_header +{ + struct in6_addr src; + struct in6_addr dst; + UINT32 next_len; /* incapsulated packet length in network byte order */ + BYTE zero[3]; + BYTE next_header; +}; + struct icmp_hdr { BYTE type; @@ -627,6 +636,60 @@ static unsigned short chksum( BYTE *data, unsigned int count, unsigned int sum ) return check; } +static void set_ipv6_addr_from_pktinfo( struct msghdr *hdr, struct in6_addr *addr ) +{ +#ifdef IPV6_PKTINFO + struct in6_pktinfo *info; + struct cmsghdr *cmsg; + + for (cmsg = CMSG_FIRSTHDR( hdr ); cmsg; cmsg = CMSG_NXTHDR( hdr, cmsg )) + { + if (cmsg->cmsg_level != IPPROTO_IPV6) continue; + if (cmsg->cmsg_type != IPV6_PKTINFO) continue; + info = (struct in6_pktinfo *)CMSG_DATA( cmsg ); + *addr = info->ipi6_addr; + return; + } +#endif +} + +static ssize_t fixup_icmpv6_over_dgram( struct msghdr *hdr, void *buf, union unix_sockaddr *unix_addr, + HANDLE handle, ssize_t recv_len ) +{ + struct ipv6_pseudo_header ip_h; + struct icmp_hdr *icmp_h = buf; + unsigned int fixup_status; + unsigned int sum; + + if (recv_len < sizeof(*icmp_h)) return recv_len; + + SERVER_START_REQ( socket_get_icmp_id ) + { + req->handle = wine_server_obj_handle( handle ); + req->icmp_seq = icmp_h->un.echo.sequence; + if (!(fixup_status = wine_server_call( req ))) + icmp_h->un.echo.id = reply->icmp_id; + } + SERVER_END_REQ; + + if (fixup_status) + { + WARN( "socket_get_icmp_id returned %#x.\n", fixup_status ); + return recv_len; + } + + memset( &ip_h, 0, sizeof(ip_h) ); + ip_h.src = unix_addr->in6.sin6_addr; + set_ipv6_addr_from_pktinfo( hdr, &ip_h.dst ); + ip_h.next_len = htonl( recv_len ); + ip_h.next_header = IPPROTO_ICMPV6; + sum = chksum_add( (BYTE *)&ip_h, sizeof(ip_h), 0 ); + icmp_h->checksum = 0; + icmp_h->checksum = chksum( (BYTE *)icmp_h, recv_len, sum ); + + return recv_len; +} + static ssize_t fixup_icmp_over_dgram( struct msghdr *hdr, union unix_sockaddr *unix_addr, HANDLE handle, ssize_t recv_len, NTSTATUS *status ) { @@ -647,6 +710,9 @@ static ssize_t fixup_icmp_over_dgram( struct msghdr *hdr, union unix_sockaddr *u buf = hdr->msg_iov[0].iov_base; buf_len = hdr->msg_iov[0].iov_len; + if (unix_addr->addr.sa_family == AF_INET6) + return fixup_icmpv6_over_dgram( hdr, buf, unix_addr, handle, recv_len ); + if (recv_len + sizeof(ip_h) > buf_len) *status = STATUS_BUFFER_OVERFLOW; @@ -838,7 +904,7 @@ static BOOL is_icmp_over_dgram( int fd ) int val; len = sizeof(val); - if (getsockopt( fd, SOL_SOCKET, SO_PROTOCOL, (char *)&val, &len ) || val != IPPROTO_ICMP) + if (getsockopt( fd, SOL_SOCKET, SO_PROTOCOL, (char *)&val, &len ) || (val != IPPROTO_ICMP && val != IPPROTO_ICMPV6)) return FALSE; len = sizeof(val); diff --git a/server/sock.c b/server/sock.c index 21b4621bb0c9..73409c5a1dff 100644 --- a/server/sock.c +++ b/server/sock.c @@ -1821,6 +1821,7 @@ static int get_unix_protocol( int protocol ) switch (protocol) { case WS_IPPROTO_ICMP: return IPPROTO_ICMP; + case WS_IPPROTO_ICMPV6: return IPPROTO_ICMPV6; case WS_IPPROTO_IGMP: return IPPROTO_IGMP; case WS_IPPROTO_IP: return IPPROTO_IP; case WS_IPPROTO_IPV4: return IPPROTO_IPIP; @@ -1890,19 +1891,28 @@ static int init_socket( struct sock *sock, int family, int type, int protocol ) } sockfd = socket( unix_family, unix_type, unix_protocol ); - #ifdef linux - if (sockfd == -1 && errno == EPERM && unix_family == AF_INET - && unix_type == SOCK_RAW && unix_protocol == IPPROTO_ICMP) + if (sockfd == -1 && errno == EPERM && unix_type == SOCK_RAW + && ((unix_family == AF_INET && unix_protocol == IPPROTO_ICMP) + || (unix_family == AF_INET6 && unix_protocol == IPPROTO_ICMPV6))) { sockfd = socket( unix_family, SOCK_DGRAM, unix_protocol ); if (sockfd != -1) { const int val = 1; - setsockopt( sockfd, IPPROTO_IP, IP_RECVTTL, (const char *)&val, sizeof(val) ); - setsockopt( sockfd, IPPROTO_IP, IP_RECVTOS, (const char *)&val, sizeof(val) ); - setsockopt( sockfd, IPPROTO_IP, IP_PKTINFO, (const char *)&val, sizeof(val) ); + if (unix_family == AF_INET6) + { +#ifdef IPV6_RECVPKTINFO + setsockopt( sockfd, IPPROTO_IPV6, IPV6_RECVPKTINFO, (const char *)&val, sizeof(val) ); +#endif + } + else + { + setsockopt( sockfd, IPPROTO_IP, IP_RECVTTL, (const char *)&val, sizeof(val) ); + setsockopt( sockfd, IPPROTO_IP, IP_RECVTOS, (const char *)&val, sizeof(val) ); + setsockopt( sockfd, IPPROTO_IP, IP_PKTINFO, (const char *)&val, sizeof(val) ); + } } } #endif From 11d9be40ee96716780eae9a6efd84b60cd4fabff Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 12 Aug 2025 16:22:14 -0600 Subject: [PATCH 361/454] ws2_32/tests: Test ICMPv6 ping. (cherry picked from commit 585e9aba9eff05f3765fc89409ca97d0be674355) --- dlls/ws2_32/tests/sock.c | 78 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/dlls/ws2_32/tests/sock.c b/dlls/ws2_32/tests/sock.c index 0f822402c713..c3383517acdc 100644 --- a/dlls/ws2_32/tests/sock.c +++ b/dlls/ws2_32/tests/sock.c @@ -14006,6 +14006,83 @@ static void test_icmp(void) closesocket(s); } +struct ipv6_pseudo_header +{ + struct in6_addr src; + struct in6_addr dst; + UINT32 next_len; /* incapsulated packet length in network byte order */ + BYTE zero[3]; + BYTE next_header; +}; + +static void test_icmpv6(void) +{ + static const unsigned int ping_data = 0xdeadbeef; + + BYTE send_buf[sizeof(struct icmp_hdr) + sizeof(ping_data)]; + struct ipv6_pseudo_header *ip_h; + UINT16 recv_checksum, checksum; + struct icmp_hdr *icmp_h; + unsigned int reply_data; + struct sockaddr_in6 sa; + BYTE chksum_buf[256]; + BYTE recv_buf[256]; + SOCKET s; + int ret; + + s = WSASocketA(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6, NULL, 0, 0); + if (s == INVALID_SOCKET) + { + ret = WSAGetLastError(); + ok(ret == WSAEACCES, "Expected 10013, received %d\n", ret); + skip("SOCK_RAW is not supported\n"); + return; + } + + memset(&sa, 0, sizeof(sa)); + sa.sin6_family = AF_INET6; + ret = inet_pton( AF_INET6, "::1", &sa.sin6_addr); + ok(ret, "got error %u.\n", WSAGetLastError()); + + icmp_h = (struct icmp_hdr *)send_buf; + icmp_h->type = ICMP6_ECHO_REQUEST; + icmp_h->code = 0; + icmp_h->checksum = 0; + icmp_h->un.echo.id = 0xbeaf; /* will be overwritten for linux ping socks */ + icmp_h->un.echo.sequence = 2; + *(unsigned int *)(icmp_h + 1) = ping_data; + icmp_h->checksum = 0; + + ret = sendto(s, (char *)send_buf, sizeof(send_buf), 0, (struct sockaddr*)&sa, sizeof(sa)); + ok(ret != SOCKET_ERROR, "got error %d.\n", WSAGetLastError()); + memset(recv_buf, 0xcc, sizeof(recv_buf)); + ret = recv(s, (char *)recv_buf, sizeof(recv_buf), 0); + ok(ret == sizeof(send_buf), "got %d\n", ret); + + icmp_h = (struct icmp_hdr *)recv_buf; + reply_data = *(unsigned int *)(icmp_h + 1); + + ok(icmp_h->type == ICMP6_ECHO_REPLY, "got type %#x.\n", icmp_h->type); + ok(!icmp_h->code, "got code %#x.\n", icmp_h->code); + ok(icmp_h->un.echo.id == 0xbeaf, "got echo id %#x.\n", icmp_h->un.echo.id); + ok(icmp_h->un.echo.sequence == 2, "got echo sequence %#x.\n", icmp_h->un.echo.sequence); + + recv_checksum = icmp_h->checksum; + ip_h = (struct ipv6_pseudo_header *)chksum_buf; + memset(ip_h, 0, sizeof(*ip_h)); + ip_h->dst = sa.sin6_addr; + ip_h->src = sa.sin6_addr; + ip_h->next_len = htonl(sizeof(send_buf)); + ip_h->next_header = IPPROTO_ICMPV6; + icmp_h->checksum = 0; + memcpy(ip_h + 1, icmp_h, sizeof(send_buf)); + checksum = chksum((BYTE *)ip_h, sizeof(*ip_h) + sizeof(send_buf)); + ok(recv_checksum == checksum, "got checksum %#x, expected %#x.\n", recv_checksum, checksum); + ok(reply_data == ping_data, "got reply_data %#x.\n", reply_data); + + closesocket(s); +} + static void test_connect_time(void) { struct sockaddr_in addr = {.sin_family = AF_INET, .sin_addr.s_addr = htonl(INADDR_LOOPBACK)}; @@ -14463,6 +14540,7 @@ START_TEST( sock ) test_timeout(); test_tcp_reset(); test_icmp(); + test_icmpv6(); test_connect_udp(); test_tcp_sendto_recvfrom(); test_broadcast(); From 6b9a4bd0f22675a2f07eaec51ed96160bad74107 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 21 Aug 2025 15:01:02 -0600 Subject: [PATCH 362/454] kernelbase: Preserve last error in OutputDebugStringA(). CW-Bug-Id: #25756 --- dlls/kernelbase/debug.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dlls/kernelbase/debug.c b/dlls/kernelbase/debug.c index 6d24abc80ad7..81d9a9127ea6 100644 --- a/dlls/kernelbase/debug.c +++ b/dlls/kernelbase/debug.c @@ -181,6 +181,7 @@ static LONG WINAPI debug_exception_handler( EXCEPTION_POINTERS *eptr ) */ void WINAPI DECLSPEC_HOTPATCH OutputDebugStringA( LPCSTR str ) { + DWORD last_error = GetLastError(); static HANDLE DBWinMutex = NULL; static BOOL mutex_inited = FALSE; BOOL caught_by_dbg = TRUE; @@ -263,6 +264,7 @@ void WINAPI DECLSPEC_HOTPATCH OutputDebugStringA( LPCSTR str ) CloseHandle( mapping ); } } + SetLastError( last_error ); } static LONG WINAPI debug_exception_handler_wide( EXCEPTION_POINTERS *eptr ) From 74a6bf89af9568d84e4376a2ce84c8340a26e8a6 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 21 Aug 2025 14:30:33 -0600 Subject: [PATCH 363/454] kernel32: Preserve last error in OutputDebugStringA(). CW-Bug-Id: #25756 --- dlls/kernel32/debugger.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dlls/kernel32/debugger.c b/dlls/kernel32/debugger.c index 6ccce02a8f3f..a1317464e67a 100644 --- a/dlls/kernel32/debugger.c +++ b/dlls/kernel32/debugger.c @@ -52,6 +52,7 @@ void WINAPI DECLSPEC_HOTPATCH OutputDebugStringA( LPCSTR str ) static HANDLE DBWinMutex = NULL; static BOOL mutex_inited = FALSE; BOOL caught_by_dbg = TRUE; + DWORD last_error = GetLastError(); if (!str) str = ""; WARN( "%s\n", debugstr_a(str) ); @@ -131,6 +132,7 @@ void WINAPI DECLSPEC_HOTPATCH OutputDebugStringA( LPCSTR str ) CloseHandle( mapping ); } } + SetLastError( last_error ); } From 309fd770670b5f066a654727c82d754a4836093b Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 21 Aug 2025 15:07:29 -0600 Subject: [PATCH 364/454] kernel32/tests: Test last error preservation in OutputDebugString(). CW-Bug-Id: #25756 --- dlls/kernel32/tests/debugger.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/dlls/kernel32/tests/debugger.c b/dlls/kernel32/tests/debugger.c index 0782b8f21ebf..e799187d8f54 100644 --- a/dlls/kernel32/tests/debugger.c +++ b/dlls/kernel32/tests/debugger.c @@ -2420,6 +2420,27 @@ static void test_kill_on_exit(const char *argv0) heap_free(cmd); } +static void test_OutputDebugString(void) +{ + static void (WINAPI *pOutputDebugStringA)(const char *); + + pOutputDebugStringA = (void *)GetProcAddress(GetModuleHandleW(L"kernel32.dll"), "OutputDebugStringA"); + ok(!!pOutputDebugStringA, "got NULL"); + SetLastError(0xdeadbeef); + pOutputDebugStringA("test"); + ok(GetLastError() == 0xdeadbeef, "got %ld.\n", GetLastError()); + + pOutputDebugStringA = (void *)GetProcAddress(GetModuleHandleW(L"kernelbase.dll"), "OutputDebugStringA"); + ok(!!pOutputDebugStringA, "got NULL"); + SetLastError(0xdeadbeef); + pOutputDebugStringA("test"); + ok(GetLastError() == 0xdeadbeef, "got %ld.\n", GetLastError()); + + SetLastError(0xdeadbeef); + OutputDebugStringW(L"test"); + ok(GetLastError() == 0xdeadbeef, "got %ld.\n", GetLastError()); +} + START_TEST(debugger) { HMODULE hdll; @@ -2485,5 +2506,6 @@ START_TEST(debugger) test_debug_children(myARGV[0], DEBUG_ONLY_THIS_PROCESS, FALSE, TRUE); test_debugger(myARGV[0]); test_kill_on_exit(myARGV[0]); + test_OutputDebugString(); } } From 99640b6ed4361af127b9cdb8f0bd0659d9d5c045 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 21 Aug 2025 15:02:20 -0600 Subject: [PATCH 365/454] ntdll/tests: Test last error preservation in OutputDebugString() with debugger. CW-Bug-Id: #25756 --- dlls/ntdll/tests/exception.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c index 190b82dd2fbc..aca3744142ef 100644 --- a/dlls/ntdll/tests/exception.c +++ b/dlls/ntdll/tests/exception.c @@ -8971,10 +8971,12 @@ static void test_outputdebugstring(BOOL unicode, DWORD numexc_ansi, BOOL todo_an outputdebugstring_exceptions_ansi = outputdebugstring_exceptions_unicode = 0; + SetLastError(0xdeadbeef); if (unicode) OutputDebugStringW(L"Hello World"); else OutputDebugStringA("Hello World"); + ok(GetLastError() == 0xdeadbeef, "got %#lx.\n", GetLastError()); todo_wine_if(todo_ansi) ok(outputdebugstring_exceptions_ansi == numexc_ansi, @@ -9058,10 +9060,12 @@ static void test_outputdebugstring_newmodel(void) outputdebugstring_exceptions_newmodel_order = 0; outputdebugstring_newmodel_return = tests[i].ret_code; + SetLastError(0xdeadbeef); if (tests[i].unicode) OutputDebugStringW(L"Hello World"); else OutputDebugStringA("Hello World"); + ok(GetLastError() == 0xdeadbeef, "got %#lx.\n", GetLastError()); ok(outputdebugstring_exceptions_newmodel_order == tests[i].exceptions_order, "OutputDebugString%c/%u generated exceptions %04lxs, expected %04lx\n", From 9c52ffb5f02900aa287e22ed2ba3fc197a3f8b5a Mon Sep 17 00:00:00 2001 From: Zhiyi Zhang Date: Thu, 21 Aug 2025 11:19:24 +0800 Subject: [PATCH 366/454] Revert "winex11.drv: Limit fullscreen visible rects to the virtual screen rect." This reverts commit 4ac5abcfdaa50e453bc0d2ecdae347588af81d41. --- dlls/winex11.drv/window.c | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index 3d8d4d301ab2..14da3e97b8b9 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -3481,23 +3481,8 @@ void X11DRV_WindowPosChanged( HWND hwnd, HWND insert_after, HWND owner_hint, UIN { struct x11drv_win_data *data; UINT new_style = NtUserGetWindowLongW( hwnd, GWL_STYLE ), old_style; - struct window_rects old_rects, tmp_rects; + struct window_rects old_rects; BOOL was_fullscreen, activate = !(swp_flags & SWP_NOACTIVATE); - RECT virtual; - - /* If the visible rect is fullscreen on any one of the monitors, limit the visible rect to the - * virtual screen rect. This is needed because adding __NET_WM_STATE_FULLSCREEN will make WMs - * move the window to cover exactly the monitor rect. If the application sets a visible rect - * slightly larger than the monitor rect and insists on changing to the rect that it previously - * set when the rect is changed by the WM, then the window rect will be repeatedly changed by - * the WM and the application, causing a flickering effect */ - if (fullscreen) - { - virtual = NtUserGetVirtualScreenRect( MDT_RAW_DPI ); - tmp_rects = *new_rects; - intersect_rect( &tmp_rects.visible, &tmp_rects.visible, &virtual ); - new_rects = &tmp_rects; - } set_surface_window_rects( surface, new_rects ); From 7a44ea7fbffdf706c90887546c6e3513ec6bbe9c Mon Sep 17 00:00:00 2001 From: Zhiyi Zhang Date: Thu, 21 Aug 2025 11:43:01 +0800 Subject: [PATCH 367/454] win32u: Limit fullscreen visible rects to the monitor rects. Fix a regression from 31fda1f4, which allowed the visible rect to be larger than the monitor rect. After 31fda1f4, CHRONO TRIGGER (613830) sets a window rect slightly larger than the monitor rect and will change the window rect to the rect it previously set if the game detects a different window rect. Adding __NET_WM_STATE_FULLSCREEN will cause WMs to move the window to cover exactly the monitor rect. So the window rect will be repeatedly changed by the WM and the game, causing a flickering effect. Limit fullscreen visible rects to the monitor rects so that adding __NET_WM_STATE_FULLSCREEN in winex11.drv won't trigger a size change. CW-Bug-Id: #25747 --- dlls/win32u/sysparams.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/dlls/win32u/sysparams.c b/dlls/win32u/sysparams.c index 7bc30b32b473..a8ad5d338c54 100644 --- a/dlls/win32u/sysparams.c +++ b/dlls/win32u/sysparams.c @@ -2767,8 +2767,8 @@ RECT map_rect_virt_to_raw_for_monitor( HMONITOR handle, RECT rect, UINT dpi_from struct window_rects map_window_rects_virt_to_raw( struct window_rects rects, UINT dpi_from ) { RECT rect, monitor_rect, virt_visible_rect = rects.visible; + BOOL is_fullscreen, first_fullscreen = TRUE; struct monitor *monitor; - BOOL is_fullscreen; if (!lock_display_devices( FALSE )) return rects; if ((monitor = get_monitor_from_rect( rects.window, MONITOR_DEFAULTTONEAREST, dpi_from, MDT_DEFAULT ))) @@ -2786,6 +2786,18 @@ struct window_rects map_window_rects_virt_to_raw( struct window_rects rects, UIN is_fullscreen = intersect_rect( &rect, &monitor_rect, &virt_visible_rect ) && EqualRect( &rect, &monitor_rect ); if (is_fullscreen) { + /* If the visible rect is fullscreen on any one of the monitors, limit the visible rect + * to the monitor rects. This is needed because adding __NET_WM_STATE_FULLSCREEN will + * make WMs move the window to cover exactly the monitor rect. If the application sets a + * visible rect slightly larger than the monitor rect and insists on changing to the rect + * that it previously set when the rect is changed by the WM, then the window rect will + * be repeatedly changed by :wthe WM and the application, causing a flickering effect */ + if (first_fullscreen) + { + SetRectEmpty( &rects.visible ); + first_fullscreen = FALSE; + } + rect = monitor_get_rect( monitor, 0, MDT_RAW_DPI ); union_rect( &rects.visible, &rects.visible, &rect ); } From 8b48ba80274ac1e7bc53dc0a994fad80eb4647e6 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 25 Aug 2025 10:47:08 -0600 Subject: [PATCH 368/454] Revert "iphlpapi: Add stub for Icmp6ParseReplies()." This reverts commit 2063ab56e36a7ab4b70f8ca16a16b4d082d6e8cd. CW-Bug-Id: #25798 --- dlls/iphlpapi/iphlpapi.spec | 2 +- dlls/iphlpapi/iphlpapi_main.c | 8 -------- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/dlls/iphlpapi/iphlpapi.spec b/dlls/iphlpapi/iphlpapi.spec index ed2214c99ce6..c04560ad656d 100644 --- a/dlls/iphlpapi/iphlpapi.spec +++ b/dlls/iphlpapi/iphlpapi.spec @@ -153,7 +153,7 @@ @ stdcall GetUnicastIpAddressTable(long ptr) @ stdcall GetUniDirectionalAdapterInfo( ptr ptr ) @ stdcall Icmp6CreateFile() -@ stdcall Icmp6ParseReplies( ptr long ) +#@ stub Icmp6ParseReplies @ stdcall Icmp6SendEcho2(ptr ptr ptr ptr ptr ptr ptr long ptr ptr long long) @ stdcall IcmpCloseHandle(ptr) @ stdcall IcmpCreateFile() diff --git a/dlls/iphlpapi/iphlpapi_main.c b/dlls/iphlpapi/iphlpapi_main.c index dd409fff2f74..94769ed09fe4 100644 --- a/dlls/iphlpapi/iphlpapi_main.c +++ b/dlls/iphlpapi/iphlpapi_main.c @@ -4963,14 +4963,6 @@ DWORD WINAPI Icmp6SendEcho2( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc_ro return 0; } -DWORD WINAPI Icmp6ParseReplies( void *buffer, DWORD size ) -{ - FIXME( "(%p, %lu) stub.\n", buffer, size ); - - SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); - return 0; -} - /*********************************************************************** * GetCurrentThreadCompartmentId (IPHLPAPI.@) */ From b2df813abaddc29b3a3f3bab2bc9e6260838c3da Mon Sep 17 00:00:00 2001 From: Brendan Shanks Date: Tue, 13 May 2025 15:44:32 -0700 Subject: [PATCH 369/454] nsiproxy: Set the name of internal threads. (cherry picked from commit 84deba0d1a3e5e15ba4803d3001f80900d5d9aa4) CW-Bug-Id: #25798 --- dlls/nsiproxy.sys/device.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/dlls/nsiproxy.sys/device.c b/dlls/nsiproxy.sys/device.c index c64f61ed0fe5..fe12ba6544bd 100644 --- a/dlls/nsiproxy.sys/device.c +++ b/dlls/nsiproxy.sys/device.c @@ -457,6 +457,8 @@ static DWORD WINAPI request_thread_proc( void *arg ) { LIST_ENTRY *entry; + SetThreadDescription( GetCurrentThread(), L"wine_nsi_request" ); + while (WaitForSingleObject( request_event, INFINITE ) == WAIT_OBJECT_0) { TRACE( "request_event triggered\n" ); @@ -486,6 +488,8 @@ static DWORD WINAPI notification_thread_proc( void *arg ) LIST_ENTRY *entry, *next; NTSTATUS status; + SetThreadDescription( GetCurrentThread(), L"wine_nsi_notification" ); + while (!(status = nsiproxy_call( nsi_get_notification, ¶ms ))) { EnterCriticalSection( &nsiproxy_cs ); From 1de6608c47faf1dda48baa758cb9c688eabd6854 Mon Sep 17 00:00:00 2001 From: Brendan Shanks Date: Tue, 13 May 2025 16:36:02 -0700 Subject: [PATCH 370/454] nsiproxy: Implement change notifications for NSI_IP_UNICAST_TABLE on macOS. (cherry picked from commit 343954a86d5ab5f7a094a48d9d75128fe734d88d) CW-Bug-Id: #25798 --- dlls/nsiproxy.sys/nsi.c | 84 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 81 insertions(+), 3 deletions(-) diff --git a/dlls/nsiproxy.sys/nsi.c b/dlls/nsiproxy.sys/nsi.c index 2f6d2d595734..a13d77a47a6c 100644 --- a/dlls/nsiproxy.sys/nsi.c +++ b/dlls/nsiproxy.sys/nsi.c @@ -31,6 +31,10 @@ #ifdef HAVE_LINUX_RTNETLINK_H #include #endif +#ifdef __APPLE__ +#include +#include +#endif #include "ntstatus.h" #define WIN32_NO_STATUS @@ -156,7 +160,7 @@ static NTSTATUS unix_nsi_get_parameter_ex( void *args ) return nsi_get_parameter_ex( params ); } -#ifdef HAVE_LINUX_RTNETLINK_H +#if defined(HAVE_LINUX_RTNETLINK_H) || defined(__APPLE__) static struct { const NPI_MODULEID *module; @@ -182,7 +186,8 @@ static NTSTATUS add_notification( const NPI_MODULEID *module, UINT32 table ) return STATUS_SUCCESS; } -static NTSTATUS poll_netlink(void) +#if defined(HAVE_LINUX_RTNETLINK_H) +static NTSTATUS poll_events(void) { static int netlink_fd = -1; char buffer[PIPE_BUF]; @@ -243,13 +248,86 @@ static NTSTATUS poll_netlink(void) } return STATUS_SUCCESS; } +#elif defined(__APPLE__) +static NTSTATUS poll_events(void) +{ + static int sock = -1; + + if (sock == -1) + { + static const struct kev_request req = + { + .vendor_code = KEV_VENDOR_APPLE, + .kev_class = KEV_NETWORK_CLASS, + .kev_subclass = KEV_ANY_SUBCLASS, + }; + + if ((sock = socket( PF_SYSTEM, SOCK_RAW, SYSPROTO_EVENT )) == -1) + { + ERR( "PF_SYSTEM socket creation failed, errno %d.\n", errno ); + return STATUS_NOT_IMPLEMENTED; + } + + if (ioctl( sock, SIOCSKEVFILT, &req ) == -1) + { + close( sock ); + sock = -1; + ERR( "SIOCSKEVFILT failed, errno %d.\n", errno ); + return STATUS_NOT_IMPLEMENTED; + } + } + + while (1) + { + struct kern_event_msg msg; + NTSTATUS status; + int len; + + len = recv( sock, &msg, sizeof(msg), 0 ); + if (len < sizeof(msg)) + { + if (errno == EINTR) continue; + ERR( "error receiving, len %d, errno %d.\n", len, errno ); + return STATUS_UNSUCCESSFUL; + } + + if (msg.kev_subclass == KEV_INET_SUBCLASS) + { + switch (msg.event_code) + { + case KEV_INET_NEW_ADDR: + case KEV_INET_CHANGED_ADDR: + case KEV_INET_ADDR_DELETED: + if ((status = add_notification( &NPI_MS_IPV4_MODULEID, NSI_IP_UNICAST_TABLE))) return status; + break; + } + } + else if (msg.kev_subclass == KEV_INET6_SUBCLASS) + { + switch (msg.event_code) + { + case KEV_INET6_NEW_USER_ADDR: + case KEV_INET6_CHANGED_ADDR: + case KEV_INET6_ADDR_DELETED: + case KEV_INET6_NEW_LL_ADDR: + case KEV_INET6_NEW_RTADV_ADDR: + if ((status = add_notification( &NPI_MS_IPV6_MODULEID, NSI_IP_UNICAST_TABLE))) return status; + break; + } + } + if (queued_notification_count) break; + } + + return STATUS_SUCCESS; +} +#endif static NTSTATUS unix_nsi_get_notification( void *args ) { struct nsi_get_notification_params *params = (struct nsi_get_notification_params *)args; NTSTATUS status; - if (!queued_notification_count && (status = poll_netlink())) return status; + if (!queued_notification_count && (status = poll_events())) return status; assert( queued_notification_count ); params->module = *queued_notifications[0].module; params->table = queued_notifications[0].table; From 73efdb295bbe2d95381a4555f57c1831e1804cf3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20=C3=9Cbelacker?= Date: Fri, 16 May 2025 00:11:16 +0200 Subject: [PATCH 371/454] nsiproxy: Avoid buffer overflow in ipv4_neighbour_enumerate_all. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=57613 (cherry picked from commit 0e9e1c2d13600739771b5bb1792628656b406b2c) CW-Bug-Id: #25798 --- dlls/nsiproxy.sys/ip.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/nsiproxy.sys/ip.c b/dlls/nsiproxy.sys/ip.c index 098abbd11629..a04598f9f3f5 100644 --- a/dlls/nsiproxy.sys/ip.c +++ b/dlls/nsiproxy.sys/ip.c @@ -1392,7 +1392,7 @@ static NTSTATUS ipv4_neighbour_enumerate_all( void *key_data, UINT key_size, voi for (j = 0; j < ARRAY_SIZE(ipv4_multicast_addresses); ++j) { if (iface_static[i].unk & (1 << j)) continue; - if (num <= *count) + if (num < *count) { entry.addr.s_addr = ipv4_multicast_addresses[j]; ipv4_neighbour_fill_entry( &entry, key_data, rw_data, dynamic_data, static_data ); From ccfe42805763ce67638f4f7aa1c6354ee1a33aa2 Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Mon, 9 Jun 2025 18:15:17 +0100 Subject: [PATCH 372/454] nsiproxy.sys: Fix size mismatch writing into ipstatlist. Both of long and long long are 8 bytes on 64-bit unix, so this if does nothing and we will write 8 bytes into a 4-byte UINT. (cherry picked from commit d19869341c633d6c5e59b5bd598b0215ba129c0b) CW-Bug-Id: #25798 --- dlls/nsiproxy.sys/ip.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dlls/nsiproxy.sys/ip.c b/dlls/nsiproxy.sys/ip.c index a04598f9f3f5..be43d32557d8 100644 --- a/dlls/nsiproxy.sys/ip.c +++ b/dlls/nsiproxy.sys/ip.c @@ -761,10 +761,10 @@ static NTSTATUS ipv6_ipstats_get_all_parameters( const void *key, UINT key_size, for (i = 0; i < ARRAY_SIZE(ipstatlist); i++) if (!ascii_strcasecmp( buf, ipstatlist[i].name )) { - if (ipstatlist[i].size == sizeof(long)) - *(long *)ipstatlist[i].elem = strtoul( value, NULL, 10 ); + if (ipstatlist[i].size == sizeof(UINT)) + *(UINT *)ipstatlist[i].elem = strtoul( value, NULL, 10 ); else - *(long long *)ipstatlist[i].elem = strtoull( value, NULL, 10 ); + *(ULONGLONG *)ipstatlist[i].elem = strtoull( value, NULL, 10 ); status = STATUS_SUCCESS; } } From 0cec0e0cfa41a31970c7755a580bfa87eb0a0e3c Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 18 Aug 2025 17:18:19 -0600 Subject: [PATCH 373/454] nsiproxy.sys: Get rid of echo request thread. (cherry picked from commit 6dcb31fc9d41290aef725cfbf22eb383990b29f3) CW-Bug-Id: #25798 --- dlls/nsiproxy.sys/device.c | 205 +++++++++++++++---------------------- 1 file changed, 81 insertions(+), 124 deletions(-) diff --git a/dlls/nsiproxy.sys/device.c b/dlls/nsiproxy.sys/device.c index fe12ba6544bd..9db77f73c89c 100644 --- a/dlls/nsiproxy.sys/device.c +++ b/dlls/nsiproxy.sys/device.c @@ -37,8 +37,6 @@ WINE_DEFAULT_DEBUG_CHANNEL(nsi); -static HANDLE request_event; - #define DECLARE_CRITICAL_SECTION(cs) \ static CRITICAL_SECTION cs; \ static CRITICAL_SECTION_DEBUG cs##_debug = \ @@ -48,7 +46,6 @@ static HANDLE request_event; DECLARE_CRITICAL_SECTION( nsiproxy_cs ); #define LIST_ENTRY_INIT( list ) { .Flink = &(list), .Blink = &(list) } -static LIST_ENTRY request_queue = LIST_ENTRY_INIT( request_queue ); static LIST_ENTRY notification_queue = LIST_ENTRY_INIT( notification_queue ); struct notification_data @@ -226,14 +223,92 @@ static int icmp_echo_reply_struct_len( ULONG family, ULONG bits ) return 0; } +static DWORD WINAPI listen_thread_proc( void *arg ) +{ + IRP *irp = arg; + IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation( irp ); + struct nsiproxy_icmp_echo *in = irp->AssociatedIrp.SystemBuffer; + struct icmp_close_params close_params; + struct icmp_listen_params params; + NTSTATUS status; + + TRACE( "\n" ); + + params.user_reply_ptr = in->user_reply_ptr; + params.handle = irp_get_icmp_handle( irp ); + params.timeout = in->timeout; + params.bits = in->bits; + params.reply = irp->AssociatedIrp.SystemBuffer; + params.reply_len = irpsp->Parameters.DeviceIoControl.OutputBufferLength; + + status = nsiproxy_call( icmp_listen, ¶ms ); + TRACE( "icmp_listen rets %08lx\n", status ); + + EnterCriticalSection( &nsiproxy_cs ); + + close_params.handle = irp_set_icmp_handle( irp, 0 ); + nsiproxy_call( icmp_close, &close_params ); + + irp->IoStatus.Status = status; + if (status == STATUS_SUCCESS) + irp->IoStatus.Information = params.reply_len; + else + irp->IoStatus.Information = 0; + IoCompleteRequest( irp, IO_NO_INCREMENT ); + + LeaveCriticalSection( &nsiproxy_cs ); + + return 0; +} + +static NTSTATUS handle_send_echo( IRP *irp ) +{ + struct nsiproxy_icmp_echo *in = (struct nsiproxy_icmp_echo *)irp->AssociatedIrp.SystemBuffer; + struct icmp_send_echo_params params; + icmp_handle handle; + NTSTATUS status; + + TRACE( "\n" ); + params.request = in->data + ((in->opt_size + 3) & ~3); + params.request_size = in->req_size; + params.reply = irp->AssociatedIrp.SystemBuffer; + params.bits = in->bits; + params.ttl = in->ttl; + params.tos = in->tos; + params.dst = &in->dst; + params.handle = &handle; + + status = nsiproxy_call( icmp_send_echo, ¶ms ); + TRACE( "icmp_send_echo status %#lx\n", status ); + + if (status != STATUS_PENDING) + { + irp->IoStatus.Status = status; + if (status == STATUS_SUCCESS) + irp->IoStatus.Information = params.reply_len; + return status; + } + IoSetCancelRoutine( irp, icmp_echo_cancel ); + if (irp->Cancel && !IoSetCancelRoutine( irp, NULL )) + { + /* IRP was canceled before we set cancel routine */ + return STATUS_CANCELLED; + } + IoMarkIrpPending( irp ); + irp_set_icmp_handle( irp, handle ); + RtlQueueWorkItem( listen_thread_proc, irp, WT_EXECUTELONGFUNCTION ); + return STATUS_PENDING; +} + static NTSTATUS nsiproxy_icmp_echo( IRP *irp ) { IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation( irp ); struct nsiproxy_icmp_echo *in = (struct nsiproxy_icmp_echo *)irp->AssociatedIrp.SystemBuffer; DWORD in_len = irpsp->Parameters.DeviceIoControl.InputBufferLength; DWORD out_len = irpsp->Parameters.DeviceIoControl.OutputBufferLength; + NTSTATUS ret; - TRACE( "\n" ); + TRACE( ".\n" ); if (in_len < offsetof(struct nsiproxy_icmp_echo, data[0]) || in_len < offsetof(struct nsiproxy_icmp_echo, data[((in->opt_size + 3) & ~3) + in->req_size]) || @@ -250,23 +325,9 @@ static NTSTATUS nsiproxy_icmp_echo( IRP *irp ) } EnterCriticalSection( &nsiproxy_cs ); - - IoSetCancelRoutine( irp, icmp_echo_cancel ); - if (irp->Cancel && !IoSetCancelRoutine( irp, NULL )) - { - /* IRP was canceled before we set cancel routine */ - InitializeListHead( &irp->Tail.Overlay.ListEntry ); - LeaveCriticalSection( &nsiproxy_cs ); - return STATUS_CANCELLED; - } - - InsertTailList( &request_queue, &irp->Tail.Overlay.ListEntry ); - IoMarkIrpPending( irp ); - + ret = handle_send_echo( irp ); LeaveCriticalSection( &nsiproxy_cs ); - SetEvent( request_event ); - - return STATUS_PENDING; + return ret; } static void WINAPI change_notification_cancel( DEVICE_OBJECT *device, IRP *irp ) @@ -381,107 +442,6 @@ static int add_device( DRIVER_OBJECT *driver ) return 1; } -static DWORD WINAPI listen_thread_proc( void *arg ) -{ - IRP *irp = arg; - IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation( irp ); - struct nsiproxy_icmp_echo *in = irp->AssociatedIrp.SystemBuffer; - struct icmp_close_params close_params; - struct icmp_listen_params params; - NTSTATUS status; - - TRACE( "\n" ); - - params.user_reply_ptr = in->user_reply_ptr; - params.handle = irp_get_icmp_handle( irp ); - params.timeout = in->timeout; - params.bits = in->bits; - params.reply = irp->AssociatedIrp.SystemBuffer; - params.reply_len = irpsp->Parameters.DeviceIoControl.OutputBufferLength; - - status = nsiproxy_call( icmp_listen, ¶ms ); - TRACE( "icmp_listen rets %08lx\n", status ); - - EnterCriticalSection( &nsiproxy_cs ); - - close_params.handle = irp_set_icmp_handle( irp, 0 ); - nsiproxy_call( icmp_close, &close_params ); - - irp->IoStatus.Status = status; - if (status == STATUS_SUCCESS) - irp->IoStatus.Information = params.reply_len; - else - irp->IoStatus.Information = 0; - IoCompleteRequest( irp, IO_NO_INCREMENT ); - - LeaveCriticalSection( &nsiproxy_cs ); - - return 0; -} - -static void handle_queued_send_echo( IRP *irp ) -{ - struct nsiproxy_icmp_echo *in = (struct nsiproxy_icmp_echo *)irp->AssociatedIrp.SystemBuffer; - struct icmp_send_echo_params params; - icmp_handle handle; - NTSTATUS status; - - TRACE( "\n" ); - params.request = in->data + ((in->opt_size + 3) & ~3); - params.request_size = in->req_size; - params.reply = irp->AssociatedIrp.SystemBuffer; - params.bits = in->bits; - params.ttl = in->ttl; - params.tos = in->tos; - params.dst = &in->dst; - params.handle = &handle; - - status = nsiproxy_call( icmp_send_echo, ¶ms ); - TRACE( "icmp_send_echo rets %08lx\n", status ); - - if (status != STATUS_PENDING) - { - irp->IoStatus.Status = status; - if (status == STATUS_SUCCESS) - irp->IoStatus.Information = params.reply_len; - IoCompleteRequest( irp, IO_NO_INCREMENT ); - } - else - { - irp_set_icmp_handle( irp, handle ); - RtlQueueWorkItem( listen_thread_proc, irp, WT_EXECUTELONGFUNCTION ); - } -} - -static DWORD WINAPI request_thread_proc( void *arg ) -{ - LIST_ENTRY *entry; - - SetThreadDescription( GetCurrentThread(), L"wine_nsi_request" ); - - while (WaitForSingleObject( request_event, INFINITE ) == WAIT_OBJECT_0) - { - TRACE( "request_event triggered\n" ); - EnterCriticalSection( &nsiproxy_cs ); - while ((entry = RemoveHeadList( &request_queue )) != &request_queue ) - { - IRP *irp = CONTAINING_RECORD( entry, IRP, Tail.Overlay.ListEntry ); - - if (irp->Cancel) - { - irp->IoStatus.Status = STATUS_CANCELLED; - TRACE( "already cancelled\n" ); - IoCompleteRequest( irp, IO_NO_INCREMENT ); - continue; - } - - handle_queued_send_echo( irp ); - } - LeaveCriticalSection( &nsiproxy_cs ); - } - return 0; -} - static DWORD WINAPI notification_thread_proc( void *arg ) { struct nsi_get_notification_params params; @@ -536,9 +496,6 @@ NTSTATUS WINAPI DriverEntry( DRIVER_OBJECT *driver, UNICODE_STRING *path ) add_device( driver ); - request_event = CreateEventW( NULL, FALSE, FALSE, NULL ); - thread = CreateThread( NULL, 0, request_thread_proc, NULL, 0, NULL ); - CloseHandle( thread ); thread = CreateThread( NULL, 0, notification_thread_proc, NULL, 0, NULL ); CloseHandle( thread ); From b29aab3cd542f93d4b9d3775e09a66de4c01d90e Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 18 Aug 2025 17:28:00 -0600 Subject: [PATCH 374/454] nsiproxy.sys: Bind to source address in icmp_send_echo(). (cherry picked from commit 3700cfbd8b56a695c5c38278c36d62e2330661ca) CW-Bug-Id: #25798 --- dlls/iphlpapi/tests/iphlpapi.c | 6 ++++++ dlls/nsiproxy.sys/device.c | 1 + dlls/nsiproxy.sys/icmp_echo.c | 26 ++++++++++++++++++++------ dlls/nsiproxy.sys/nsiproxy_private.h | 1 + 4 files changed, 28 insertions(+), 6 deletions(-) diff --git a/dlls/iphlpapi/tests/iphlpapi.c b/dlls/iphlpapi/tests/iphlpapi.c index e189fd795f87..ea7ec0e5e702 100644 --- a/dlls/iphlpapi/tests/iphlpapi.c +++ b/dlls/iphlpapi/tests/iphlpapi.c @@ -1240,6 +1240,12 @@ static void testIcmpSendEcho(void) ret = IcmpSendEcho2(icmp, NULL, NULL, NULL, address, senddata, ICMP_MINLEN, NULL, replydata2, replysz, 1000); ok(ret, "IcmpSendEcho2 failed unexpectedly with error %ld\n", GetLastError()); + SetLastError(0xdeadbeef); + ret = IcmpSendEcho2Ex(icmp, NULL, NULL, NULL, 0x01010101, address, senddata, ICMP_MINLEN, NULL, replydata2, replysz, 1000); + ok(!ret, "IcmpSendEcho2 succeded unexpectedly\n"); + error = GetLastError(); + ok(error == ERROR_INVALID_NETNAME, "got %ld\n", error); + SetLastError(0xdeadbeef); replysz = sizeof(replydata2); ret = IcmpSendEcho2(icmp, NULL, NULL, NULL, address, senddata, sizeof(senddata), NULL, replydata2, replysz, 1000); diff --git a/dlls/nsiproxy.sys/device.c b/dlls/nsiproxy.sys/device.c index 9db77f73c89c..b3d00fd5187a 100644 --- a/dlls/nsiproxy.sys/device.c +++ b/dlls/nsiproxy.sys/device.c @@ -275,6 +275,7 @@ static NTSTATUS handle_send_echo( IRP *irp ) params.bits = in->bits; params.ttl = in->ttl; params.tos = in->tos; + params.src = &in->src; params.dst = &in->dst; params.handle = &handle; diff --git a/dlls/nsiproxy.sys/icmp_echo.c b/dlls/nsiproxy.sys/icmp_echo.c index e92fa4394c14..8fc97a858db5 100644 --- a/dlls/nsiproxy.sys/icmp_echo.c +++ b/dlls/nsiproxy.sys/icmp_echo.c @@ -113,6 +113,10 @@ struct icmp_data unsigned short id; unsigned short seq; const struct family_ops *ops; + struct sockaddr_storage src_storage; + struct sockaddr_storage dst_storage; + int src_len; + int dst_len; }; #define MAX_HANDLES 256 /* Max number of simultaneous pings - could become dynamic if need be */ @@ -625,14 +629,26 @@ NTSTATUS icmp_send_echo( void *args ) { struct icmp_send_echo_params *params = args; struct icmp_hdr *icmp_hdr; /* this is the same for both ipv4 and ipv6 */ - struct sockaddr_storage dst_storage; - struct sockaddr *dst = (struct sockaddr *)&dst_storage; struct icmp_data *data; - int dst_len, ret; + int ret; + struct sockaddr *src, *dst; + NTSTATUS status; status = icmp_data_create( params->dst->si_family, &data ); if (status) return status; + + src = (struct sockaddr *)&data->src_storage; + dst = (struct sockaddr *)&data->dst_storage; + data->src_len = SOCKADDR_INET_to_sockaddr( params->src, src, sizeof(data->src_storage) ); + data->dst_len = SOCKADDR_INET_to_sockaddr( params->dst, dst, sizeof(data->dst_storage) ); + + if (bind( data->socket, src, data->src_len )) + { + icmp_data_free( data ); + return STATUS_INVALID_ADDRESS_COMPONENT; + } + data->ops->set_socket_opts( data, params ); icmp_hdr = malloc( sizeof(*icmp_hdr) + params->request_size ); @@ -645,10 +661,8 @@ NTSTATUS icmp_send_echo( void *args ) memcpy( icmp_hdr + 1, params->request, params->request_size ); icmp_hdr->checksum = data->ops->chksum( (BYTE *)icmp_hdr, sizeof(*icmp_hdr) + params->request_size ); - dst_len = SOCKADDR_INET_to_sockaddr( params->dst, dst, sizeof(dst_storage) ); - NtQueryPerformanceCounter( &data->send_time, NULL ); - ret = sendto( data->socket, icmp_hdr, sizeof(*icmp_hdr) + params->request_size, 0, dst, dst_len ); + ret = sendto( data->socket, icmp_hdr, sizeof(*icmp_hdr) + params->request_size, 0, dst, data->dst_len ); free( icmp_hdr ); if (ret < 0) diff --git a/dlls/nsiproxy.sys/nsiproxy_private.h b/dlls/nsiproxy.sys/nsiproxy_private.h index 1b6eacf7d08e..e7cc707f729c 100644 --- a/dlls/nsiproxy.sys/nsiproxy_private.h +++ b/dlls/nsiproxy.sys/nsiproxy_private.h @@ -41,6 +41,7 @@ struct icmp_listen_params struct icmp_send_echo_params { + SOCKADDR_INET *src; SOCKADDR_INET *dst; void *request, *reply; UINT request_size, reply_len; From baed77c560aae319cd5e8bd68418709a249ce025 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 18 Aug 2025 17:50:34 -0600 Subject: [PATCH 375/454] nsiproxy.sys: Store socket type in struct icmp_data. (cherry picked from commit d59ed1539881aea0a400ccd7aaf990f0a8fc549f) CW-Bug-Id: #25798 --- dlls/nsiproxy.sys/icmp_echo.c | 44 +++++++++++------------------------ 1 file changed, 14 insertions(+), 30 deletions(-) diff --git a/dlls/nsiproxy.sys/icmp_echo.c b/dlls/nsiproxy.sys/icmp_echo.c index 8fc97a858db5..9b9a203cc59f 100644 --- a/dlls/nsiproxy.sys/icmp_echo.c +++ b/dlls/nsiproxy.sys/icmp_echo.c @@ -117,6 +117,7 @@ struct icmp_data struct sockaddr_storage dst_storage; int src_len; int dst_len; + BOOL ping_socket; }; #define MAX_HANDLES 256 /* Max number of simultaneous pings - could become dynamic if need be */ @@ -189,11 +190,13 @@ static void ipv4_init_icmp_hdr( struct icmp_data *data, struct icmp_hdr *icmp_hd } /* rfc 1071 checksum */ -static unsigned short chksum( BYTE *data, unsigned int count ) +static unsigned short chksum( struct icmp_data *icmp_data, BYTE *data, unsigned int count ) { unsigned int sum = 0, carry = 0; unsigned short check, s; + if (icmp_data->ping_socket) return 0; + while (count > 1) { s = *(unsigned short *)data; @@ -216,13 +219,6 @@ static unsigned short chksum( BYTE *data, unsigned int count ) return check; } -#ifdef __linux__ -static unsigned short null_chksum( BYTE *data, unsigned int count ) -{ - return 0; -} -#endif - static int ipv4_set_reply_ip_status( IP_STATUS ip_status, unsigned int bits, void *out ) { if (bits == 32) @@ -326,8 +322,8 @@ static BOOL ipv4_linux_ping_parse_ip_hdr( struct msghdr *msg, int recvd, int *ip } #endif -static int ipv4_parse_icmp_hdr_( struct icmp_data *data, struct icmp_hdr *icmp, int icmp_size, - struct icmp_reply_ctx *ctx, int ping_socket ) +static int ipv4_parse_icmp_hdr( struct icmp_data *data, struct icmp_hdr *icmp, int icmp_size, + struct icmp_reply_ctx *ctx ) { static const IP_STATUS unreach_codes[] = { @@ -356,7 +352,7 @@ static int ipv4_parse_icmp_hdr_( struct icmp_data *data, struct icmp_hdr *icmp, switch (icmp->type) { case ICMP4_ECHO_REPLY: - if ((!ping_socket && icmp->un.echo.id != data->id) || + if ((!data->ping_socket && icmp->un.echo.id != data->id) || icmp->un.echo.sequence != data->seq) return -1; ctx->status = IP_SUCCESS; @@ -399,27 +395,13 @@ static int ipv4_parse_icmp_hdr_( struct icmp_data *data, struct icmp_hdr *icmp, orig_icmp_hdr = (const struct icmp_hdr *)((const BYTE *)orig_ip_hdr + orig_ip_hdr_len); if (orig_icmp_hdr->type != ICMP4_ECHO_REQUEST || orig_icmp_hdr->code != 0 || - (!ping_socket && orig_icmp_hdr->un.echo.id != data->id) || + (!data->ping_socket && orig_icmp_hdr->un.echo.id != data->id) || orig_icmp_hdr->un.echo.sequence != data->seq) return -1; ctx->status = status; return 0; } -static int ipv4_parse_icmp_hdr( struct icmp_data *data, struct icmp_hdr *icmp, - int icmp_size, struct icmp_reply_ctx *ctx) -{ - return ipv4_parse_icmp_hdr_( data, icmp, icmp_size, ctx, 0 ); -} - -#ifdef __linux__ -static int ipv4_linux_ping_parse_icmp_hdr( struct icmp_data *data, struct icmp_hdr *icmp, - int icmp_size, struct icmp_reply_ctx *ctx ) -{ - return ipv4_parse_icmp_hdr_( data, icmp, icmp_size, ctx, 1 ); -} -#endif - static void ipv4_fill_reply( struct icmp_listen_params *params, struct icmp_reply_ctx *ctx) { void *options_data; @@ -472,7 +454,7 @@ struct family_ops int family; int icmp_protocol; void (*init_icmp_hdr)( struct icmp_data *data, struct icmp_hdr *icmp_hdr ); - unsigned short (*chksum)( BYTE *data, unsigned int count ); + unsigned short (*chksum)( struct icmp_data *icmp_data, BYTE *data, unsigned int count ); int (*set_reply_ip_status)( IP_STATUS ip_status, unsigned int bits, void *out ); void (*set_socket_opts)( struct icmp_data *data, struct icmp_send_echo_params *params ); int (*reply_buffer_len)( struct icmp_listen_params *params ); @@ -502,12 +484,12 @@ static const struct family_ops ipv4_linux_ping = AF_INET, IPPROTO_ICMP, ipv4_init_icmp_hdr, - null_chksum, + chksum, ipv4_set_reply_ip_status, ipv4_linux_ping_set_socket_opts, ipv4_linux_ping_reply_buffer_len, ipv4_linux_ping_parse_ip_hdr, - ipv4_linux_ping_parse_icmp_hdr, + ipv4_parse_icmp_hdr, ipv4_fill_reply, }; #endif @@ -590,6 +572,7 @@ static NTSTATUS icmp_data_create( ADDRESS_FAMILY win_family, struct icmp_data ** data = malloc( sizeof(*data) ); if (!data) return STATUS_NO_MEMORY; + data->ping_socket = FALSE; data->socket = socket( ops->family, SOCK_RAW, ops->icmp_protocol ); if (data->socket < 0) /* Try a ping-socket */ { @@ -603,6 +586,7 @@ static NTSTATUS icmp_data_create( ADDRESS_FAMILY win_family, struct icmp_data ** } #ifdef __linux__ if (ops->family == AF_INET) ops = &ipv4_linux_ping; + data->ping_socket = TRUE; #endif } if (pipe( data->cancel_pipe )) @@ -659,7 +643,7 @@ NTSTATUS icmp_send_echo( void *args ) } data->ops->init_icmp_hdr( data, icmp_hdr ); memcpy( icmp_hdr + 1, params->request, params->request_size ); - icmp_hdr->checksum = data->ops->chksum( (BYTE *)icmp_hdr, sizeof(*icmp_hdr) + params->request_size ); + icmp_hdr->checksum = data->ops->chksum( data, (BYTE *)icmp_hdr, sizeof(*icmp_hdr) + params->request_size ); NtQueryPerformanceCounter( &data->send_time, NULL ); ret = sendto( data->socket, icmp_hdr, sizeof(*icmp_hdr) + params->request_size, 0, dst, data->dst_len ); From d7aefff58a8f8ed8ebd087a46ee0d9c9fb0a98e7 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 18 Aug 2025 17:57:25 -0600 Subject: [PATCH 376/454] iphlpapi: Factor out icmp_send_echo() function. (cherry picked from commit 383aaf546967b258cef78aeb21d844d9e86dad8a) CW-Bug-Id: #25798 --- dlls/iphlpapi/iphlpapi_main.c | 57 +++++++++++++++++++++++------------ 1 file changed, 37 insertions(+), 20 deletions(-) diff --git a/dlls/iphlpapi/iphlpapi_main.c b/dlls/iphlpapi/iphlpapi_main.c index 94769ed09fe4..03bb376c35a0 100644 --- a/dlls/iphlpapi/iphlpapi_main.c +++ b/dlls/iphlpapi/iphlpapi_main.c @@ -21,6 +21,8 @@ #include #define IPHLPAPI_DLL_LINKAGE +#include "ntstatus.h" +#define WIN32_NO_STATUS #include "windef.h" #include "winbase.h" #include "winreg.h" @@ -4863,17 +4865,15 @@ void WINAPI icmp_apc_routine( void *context, IO_STATUS_BLOCK *iosb, ULONG reserv heap_free( ctxt ); } -/*********************************************************************** - * IcmpSendEcho2Ex (IPHLPAPI.@) - */ -DWORD WINAPI IcmpSendEcho2Ex( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc_routine, void *apc_ctxt, - IPAddr src, IPAddr dst, void *request, WORD request_size, IP_OPTION_INFORMATION *opts, - void *reply, DWORD reply_size, DWORD timeout ) +static NTSTATUS icmp_send_echo( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc_routine, void *apc_ctxt, + SOCKADDR_INET *src_addr, SOCKADDR_INET *dst_addr, void *request, + WORD request_size, IP_OPTION_INFORMATION *opts, void *reply, DWORD reply_size, + DWORD timeout ) { struct icmp_handle_data *data = (struct icmp_handle_data *)handle; struct icmp_apc_ctxt *ctxt = heap_alloc( sizeof(*ctxt) ); IO_STATUS_BLOCK *iosb = &ctxt->iosb; - DWORD opt_size, in_size, ret = 0; + DWORD opt_size, in_size; struct nsiproxy_icmp_echo *in; HANDLE request_event; NTSTATUS status; @@ -4881,8 +4881,7 @@ DWORD WINAPI IcmpSendEcho2Ex( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc_r if (handle == INVALID_HANDLE_VALUE || !reply) { heap_free( ctxt ); - SetLastError( ERROR_INVALID_PARAMETER ); - return 0; + return STATUS_INVALID_PARAMETER; } ctxt->apc_routine = apc_routine; @@ -4895,16 +4894,13 @@ DWORD WINAPI IcmpSendEcho2Ex( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc_r if (!in) { heap_free( ctxt ); - SetLastError( IP_NO_RESOURCES ); - return 0; + return STATUS_NO_MEMORY; } in->user_reply_ptr = (ULONG_PTR)reply; in->bits = sizeof(void*) * 8; - in->src.Ipv4.sin_family = AF_INET; - in->src.Ipv4.sin_addr.s_addr = src; - in->dst.Ipv4.sin_family = AF_INET; - in->dst.Ipv4.sin_addr.s_addr = dst; + in->src = *src_addr; + in->dst = *dst_addr; if (opts) { in->ttl = opts->Ttl; @@ -4929,15 +4925,36 @@ DWORD WINAPI IcmpSendEcho2Ex( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc_r status = iosb->Status; } - if (!status) - ret = IcmpParseReplies( reply, reply_size ); - if (!event && request_event) CloseHandle( request_event ); if ((!apc_routine && !event) || status != STATUS_PENDING) heap_free( ctxt ); heap_free( in ); + return status; +} - if (status) SetLastError( RtlNtStatusToDosError( status ) ); - return ret; +/*********************************************************************** + * IcmpSendEcho2Ex (IPHLPAPI.@) + */ +DWORD WINAPI IcmpSendEcho2Ex( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc_routine, void *apc_ctxt, + IPAddr src, IPAddr dst, void *request, WORD request_size, IP_OPTION_INFORMATION *opts, + void *reply, DWORD reply_size, DWORD timeout ) +{ + SOCKADDR_INET src_addr, dst_addr; + NTSTATUS status; + + TRACE( "(%p %p %p %p %#lx %#lx %p %u %p %p %lu %lu).\n", handle, event, apc_routine, apc_ctxt, src, dst, + request, request_size, opts, reply, reply_size, timeout ); + + memset( &src_addr, 0, sizeof(src_addr) ); + src_addr.Ipv4.sin_family = AF_INET; + src_addr.Ipv4.sin_addr.s_addr = src; + memset( &dst_addr, 0, sizeof(dst_addr) ); + dst_addr.Ipv4.sin_family = AF_INET; + dst_addr.Ipv4.sin_addr.s_addr = dst; + status = icmp_send_echo( handle, event, apc_routine, apc_ctxt, &src_addr, &dst_addr, request, request_size, + opts, reply, reply_size, timeout ); + if (!status) return IcmpParseReplies( reply, reply_size ); + SetLastError( RtlNtStatusToDosError( status ) ); + return 0; } /*********************************************************************** From fed75e383af7a0f8a26a488c22141de982c9cbdb Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 18 Aug 2025 18:00:09 -0600 Subject: [PATCH 377/454] iphlpapi/tests: Refactor APC testing in testIcmpSendEcho(). (cherry picked from commit 8bc97368494db49a6f57e59d5f6a745a43ec1324) CW-Bug-Id: #25798 --- dlls/iphlpapi/tests/iphlpapi.c | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/dlls/iphlpapi/tests/iphlpapi.c b/dlls/iphlpapi/tests/iphlpapi.c index ea7ec0e5e702..c2945437cf32 100644 --- a/dlls/iphlpapi/tests/iphlpapi.c +++ b/dlls/iphlpapi/tests/iphlpapi.c @@ -943,27 +943,20 @@ static void testSetTcpEntry(void) } static BOOL icmp_send_echo_test_apc_expect; -static void WINAPI icmp_send_echo_test_apc_xp(void *context) -{ - ok(icmp_send_echo_test_apc_expect, "Unexpected APC execution\n"); - ok(context == (void*)0xdeadc0de, "Wrong context: %p\n", context); - icmp_send_echo_test_apc_expect = FALSE; -} +static int icmp_send_echo_test_line; +static IO_STATUS_BLOCK icmp_send_echo_io; static void WINAPI icmp_send_echo_test_apc(void *context, IO_STATUS_BLOCK *io_status, ULONG reserved) { - icmp_send_echo_test_apc_xp(context); - ok(io_status->Status == 0, "Got IO Status 0x%08lx\n", io_status->Status); - ok(io_status->Information == sizeof(ICMP_ECHO_REPLY) + 32 /* sizeof(senddata) */, - "Got IO Information %Iu\n", io_status->Information); + ok_(__FILE__, icmp_send_echo_test_line)(icmp_send_echo_test_apc_expect, "Unexpected APC execution\n"); + ok_(__FILE__, icmp_send_echo_test_line)(context == (void*)0xdeadc0de, "Wrong context: %p\n", context); + icmp_send_echo_test_apc_expect = FALSE; + icmp_send_echo_io = *io_status; } static void testIcmpSendEcho(void) { /* The APC's signature is different pre-Vista */ - const PIO_APC_ROUTINE apc = broken(LOBYTE(LOWORD(GetVersion())) < 6) - ? (PIO_APC_ROUTINE)icmp_send_echo_test_apc_xp - : icmp_send_echo_test_apc; HANDLE icmp; char senddata[32], replydata[sizeof(senddata) + sizeof(ICMP_ECHO_REPLY)]; char replydata2[sizeof(replydata) + sizeof(IO_STATUS_BLOCK)]; @@ -1340,16 +1333,21 @@ static void testIcmpSendEcho(void) address = htonl(INADDR_LOOPBACK); for (i = 0; i < ARRAY_SIZE(senddata); i++) senddata[i] = ~i & 0xff; icmp_send_echo_test_apc_expect = TRUE; + memset(&icmp_send_echo_io, 0xcc, sizeof(icmp_send_echo_io)); + icmp_send_echo_test_line = __LINE__; /* NOTE: Supplying both event and apc has varying behavior across Windows versions, so not tested. */ - ret = IcmpSendEcho2(icmp, NULL, apc, (void*)0xdeadc0de, address, senddata, sizeof(senddata), NULL, replydata2, replysz, 1000); + ret = IcmpSendEcho2(icmp, NULL, icmp_send_echo_test_apc, (void*)0xdeadc0de, address, senddata, sizeof(senddata), + NULL, replydata2, replysz, 1000); error = GetLastError(); ok(!ret, "IcmpSendEcho2 returned success unexpectedly\n"); ok(error == ERROR_IO_PENDING, "Expect last error: 0x%08x, got: 0x%08lx\n", ERROR_IO_PENDING, error); SleepEx(200, TRUE); SleepEx(0, TRUE); ok(icmp_send_echo_test_apc_expect == FALSE, "APC was not executed!\n"); + ok(!icmp_send_echo_io.Status, "got %#lx.\n", icmp_send_echo_io.Status); + ok(icmp_send_echo_io.Information == sizeof(replydata), "got %Iu.\n", icmp_send_echo_io.Information); reply = (ICMP_ECHO_REPLY*)replydata2; ok(ntohl(reply->Address) == INADDR_LOOPBACK, "Address mismatch, expect: %s, got: %s\n", ntoa(INADDR_LOOPBACK), ntoa(reply->Address)); From 6e6e258b73f25eb3efa3338cfd412306ea3a11ab Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 18 Aug 2025 18:05:49 -0600 Subject: [PATCH 378/454] iphlpapi: Only supply APC routine if no event in icmp_send_echo(). (cherry picked from commit ea47fbff127df3faaa10ab0db5448d685e3dd818) CW-Bug-Id: #25798 --- dlls/iphlpapi/iphlpapi_main.c | 2 +- dlls/iphlpapi/tests/iphlpapi.c | 19 ++++++++++++++++--- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/dlls/iphlpapi/iphlpapi_main.c b/dlls/iphlpapi/iphlpapi_main.c index 03bb376c35a0..3ca0ed26ca4c 100644 --- a/dlls/iphlpapi/iphlpapi_main.c +++ b/dlls/iphlpapi/iphlpapi_main.c @@ -4915,7 +4915,7 @@ static NTSTATUS icmp_send_echo( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc request_event = event ? event : (apc_routine ? NULL : CreateEventW( NULL, 0, 0, NULL )); - status = NtDeviceIoControlFile( data->nsi_device, request_event, apc_routine ? icmp_apc_routine : NULL, + status = NtDeviceIoControlFile( data->nsi_device, request_event, apc_routine && !event ? icmp_apc_routine : NULL, apc_routine ? ctxt : apc_ctxt, iosb, IOCTL_NSIPROXY_WINE_ICMP_ECHO, in, in_size, reply, reply_size ); diff --git a/dlls/iphlpapi/tests/iphlpapi.c b/dlls/iphlpapi/tests/iphlpapi.c index c2945437cf32..b2312c76f2f3 100644 --- a/dlls/iphlpapi/tests/iphlpapi.c +++ b/dlls/iphlpapi/tests/iphlpapi.c @@ -1325,6 +1325,22 @@ static void testIcmpSendEcho(void) /* pre-Vista, reply->Data is an offset; otherwise it's a pointer, so hardcode the offset */ ok(!memcmp(senddata, reply + 1, min(sizeof(senddata), reply->DataSize)), "Data mismatch\n"); + SetLastError(0xdeadbeef); + for (i = 0; i < ARRAY_SIZE(senddata); i++) senddata[i] = i & 0xff; + ret = IcmpSendEcho2(icmp, event, icmp_send_echo_test_apc, NULL, address, senddata, sizeof(senddata), + NULL, replydata2, replysz, 1000); + error = GetLastError(); + ok(!ret, "IcmpSendEcho2 returned success unexpectedly\n"); + ok(error == ERROR_IO_PENDING, "Expect last error: 0x%08x, got: 0x%08lx\n", ERROR_IO_PENDING, error); + ret = WaitForSingleObjectEx(event, 2000, TRUE); + ok(ret == WAIT_OBJECT_0, "WaitForSingleObjectEx failed unexpectedly with %lu\n", ret); + reply = (ICMP_ECHO_REPLY*)replydata2; + ok(ntohl(reply->Address) == INADDR_LOOPBACK, "Address mismatch, expect: %s, got: %s\n", ntoa(INADDR_LOOPBACK), + ntoa(reply->Address)); + ok(reply->Status == IP_SUCCESS, "Expect status: 0x%08x, got: 0x%08lx\n", IP_SUCCESS, reply->Status); + ok(reply->DataSize == sizeof(senddata), "Got size: %d\n", reply->DataSize); + ok(!memcmp(senddata, reply + 1, min(sizeof(senddata), reply->DataSize)), "Data mismatch\n"); + CloseHandle(event); /* asynchronous tests with APC */ @@ -1335,9 +1351,6 @@ static void testIcmpSendEcho(void) icmp_send_echo_test_apc_expect = TRUE; memset(&icmp_send_echo_io, 0xcc, sizeof(icmp_send_echo_io)); icmp_send_echo_test_line = __LINE__; - /* - NOTE: Supplying both event and apc has varying behavior across Windows versions, so not tested. - */ ret = IcmpSendEcho2(icmp, NULL, icmp_send_echo_test_apc, (void*)0xdeadc0de, address, senddata, sizeof(senddata), NULL, replydata2, replysz, 1000); error = GetLastError(); From 0949f4b4cfcab317e250de5e579739c31223fc29 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 18 Aug 2025 18:10:32 -0600 Subject: [PATCH 379/454] iphlpapi: Implement Icmp6ParseReplies(). (cherry picked from commit e649a2c8f4f40988d5b91c2d0c25b064e87198d8) CW-Bug-Id: #25798 --- dlls/iphlpapi/iphlpapi.spec | 2 +- dlls/iphlpapi/iphlpapi_main.c | 12 ++++++++++++ dlls/iphlpapi/tests/iphlpapi.c | 19 +++++++++++++++++++ include/ipexport.h | 31 +++++++++++++++++++++++++++++++ 4 files changed, 63 insertions(+), 1 deletion(-) diff --git a/dlls/iphlpapi/iphlpapi.spec b/dlls/iphlpapi/iphlpapi.spec index c04560ad656d..ed2214c99ce6 100644 --- a/dlls/iphlpapi/iphlpapi.spec +++ b/dlls/iphlpapi/iphlpapi.spec @@ -153,7 +153,7 @@ @ stdcall GetUnicastIpAddressTable(long ptr) @ stdcall GetUniDirectionalAdapterInfo( ptr ptr ) @ stdcall Icmp6CreateFile() -#@ stub Icmp6ParseReplies +@ stdcall Icmp6ParseReplies( ptr long ) @ stdcall Icmp6SendEcho2(ptr ptr ptr ptr ptr ptr ptr long ptr ptr long long) @ stdcall IcmpCloseHandle(ptr) @ stdcall IcmpCreateFile() diff --git a/dlls/iphlpapi/iphlpapi_main.c b/dlls/iphlpapi/iphlpapi_main.c index 3ca0ed26ca4c..f68ac36f2ebd 100644 --- a/dlls/iphlpapi/iphlpapi_main.c +++ b/dlls/iphlpapi/iphlpapi_main.c @@ -4967,6 +4967,18 @@ HANDLE WINAPI Icmp6CreateFile( void ) return INVALID_HANDLE_VALUE; } +/****************************************************************** + * Icmp6ParseReplies (IPHLPAPI.@) + */ +DWORD WINAPI Icmp6ParseReplies( void *reply, DWORD reply_size ) +{ + ICMPV6_ECHO_REPLY *icmp_reply = reply; + + if (!icmp_reply->Status) return 1; + SetLastError( icmp_reply->Status ); + return 0; +} + /*********************************************************************** * Icmp6SendEcho2 (IPHLPAPI.@) */ diff --git a/dlls/iphlpapi/tests/iphlpapi.c b/dlls/iphlpapi/tests/iphlpapi.c index b2312c76f2f3..10457a4310f4 100644 --- a/dlls/iphlpapi/tests/iphlpapi.c +++ b/dlls/iphlpapi/tests/iphlpapi.c @@ -1406,6 +1406,24 @@ static void testIcmpParseReplies( void ) ok( !reply.Reserved, "reserved %d\n", reply.Reserved ); } +static void testIcmp6ParseReplies( void ) +{ + ICMPV6_ECHO_REPLY reply = { 0 }; + DWORD ret; + + SetLastError( 0xdeadbeef ); + ret = Icmp6ParseReplies( &reply, sizeof(reply) ); + ok( ret == 1, "got %ld.\n", ret ); + ok( GetLastError() == 0xdeadbeef, "got error %ld.\n", GetLastError() ); + + reply.Status = 12345; + SetLastError( 0xdeadbeef ); + ret = Icmp6ParseReplies( &reply, sizeof(reply) ); + ok( ret == 0, "ret %ld\n", ret ); + ok( GetLastError() == 12345, "got error %ld.\n", GetLastError() ); + ok( reply.Status == 12345, "got %ld,\n", reply.Status ); +} + static void testWinNT4Functions(void) { testGetNumberOfInterfaces(); @@ -1426,6 +1444,7 @@ static void testWinNT4Functions(void) testSetTcpEntry(); testIcmpSendEcho(); testIcmpParseReplies(); + testIcmp6ParseReplies(); } static void testGetInterfaceInfo(void) diff --git a/include/ipexport.h b/include/ipexport.h index a0e30ba15335..1a93a0136b31 100644 --- a/include/ipexport.h +++ b/include/ipexport.h @@ -66,6 +66,23 @@ typedef struct ip_option_information IP_OPTION_INFORMATION, *PIP_OPTION_INFORMAT typedef struct icmp_echo_reply ICMP_ECHO_REPLY, *PICMP_ECHO_REPLY; +#pragma pack(push,1) +typedef struct _IPV6_ADDRESS_EX { + USHORT sin6_port; + ULONG sin6_flowinfo; + USHORT sin6_addr[8]; + ULONG sin6_scope_id; +} IPV6_ADDRESS_EX, *PIPV6_ADDRESS_EX; +#pragma pack(pop) + +struct icmpv6_echo_reply_lh +{ + IPV6_ADDRESS_EX Address; + ULONG Status; + unsigned int RoundTripTime; +}; + +typedef struct icmpv6_echo_reply_lh ICMPV6_ECHO_REPLY, *PICMPV6_ECHO_REPLY; #define IP_STATUS_BASE 11000 @@ -98,6 +115,20 @@ typedef struct icmp_echo_reply ICMP_ECHO_REPLY, *PICMP_ECHO_REPLY; #define MAX_IP_STATUS IP_GENERAL_FAILURE #define IP_PENDING (IP_STATUS_BASE + 255) +/* IPv6 status codes */ +#define IP_DEST_NO_ROUTE (IP_STATUS_BASE + 2) +#define IP_DEST_ADDR_UNREACHABLE (IP_STATUS_BASE + 3) +#define IP_DEST_PROHIBITED (IP_STATUS_BASE + 4) +#define IP_DEST_PORT_UNREACHABLE (IP_STATUS_BASE + 5) +#define IP_HOP_LIMIT_EXCEEDED (IP_STATUS_BASE + 13) +#define IP_REASSEMBLY_TIME_EXCEEDED (IP_STATUS_BASE + 14) +#define IP_PARAMETER_PROBLEM (IP_STATUS_BASE + 15) +#define IP_DEST_UNREACHABLE (IP_STATUS_BASE + 40) +#define IP_TIME_EXCEEDED (IP_STATUS_BASE + 41) +#define IP_BAD_HEADER (IP_STATUS_BASE + 42) +#define IP_UNRECOGNIZED_NEXT_HEADER (IP_STATUS_BASE + 43) +#define IP_ICMP_ERROR (IP_STATUS_BASE + 44) +#define IP_DEST_SCOPE_MISMATCH (IP_STATUS_BASE + 45) #define MAX_ADAPTER_NAME 128 From f13266ee2b196f8211048707691ee7b6167c31b2 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 18 Aug 2025 18:13:52 -0600 Subject: [PATCH 380/454] iphlpapi: Implement Icmp6CreateFile(). (cherry picked from commit 7f9eb518d52da5ada2ff3e4d37fdda8e6efa477e) CW-Bug-Id: #25798 --- dlls/iphlpapi/iphlpapi_main.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dlls/iphlpapi/iphlpapi_main.c b/dlls/iphlpapi/iphlpapi_main.c index f68ac36f2ebd..83413804d727 100644 --- a/dlls/iphlpapi/iphlpapi_main.c +++ b/dlls/iphlpapi/iphlpapi_main.c @@ -4962,9 +4962,9 @@ DWORD WINAPI IcmpSendEcho2Ex( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc_r */ HANDLE WINAPI Icmp6CreateFile( void ) { - FIXME( "stub\n" ); - SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); - return INVALID_HANDLE_VALUE; + TRACE( ".\n" ); + + return IcmpCreateFile(); } /****************************************************************** From b370175f1ca18cbd1d23e724d97ee734cabca666 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 18 Aug 2025 18:28:57 -0600 Subject: [PATCH 381/454] nsiproxy.sys: Don't try to check for original packet for ping socket. (cherry picked from commit d3f07f47be9647a50107760321b03fb6639adfdb) CW-Bug-Id: #25798 --- dlls/nsiproxy.sys/icmp_echo.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dlls/nsiproxy.sys/icmp_echo.c b/dlls/nsiproxy.sys/icmp_echo.c index 9b9a203cc59f..807f4e89e189 100644 --- a/dlls/nsiproxy.sys/icmp_echo.c +++ b/dlls/nsiproxy.sys/icmp_echo.c @@ -384,6 +384,8 @@ static int ipv4_parse_icmp_hdr( struct icmp_data *data, struct icmp_hdr *icmp, i return -1; } + if (data->ping_socket) return 0; + /* Check that the appended packet is really ours - * all handled icmp replies have an 8-byte header * followed by the original ip hdr. */ From d5776cbf6307e859672d772bd1973305d6d32e1e Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 14 Aug 2025 11:56:01 -0600 Subject: [PATCH 382/454] iphlpapi: Implement Icmp6SendEcho2(). (cherry picked from commit 81fc18e2e6cddcdeee674cee2c40bb53087e1c6c) CW-Bug-Id: #25798 --- dlls/iphlpapi/iphlpapi_main.c | 17 +- dlls/iphlpapi/tests/iphlpapi.c | 7 + dlls/nsiproxy.sys/device.c | 8 + dlls/nsiproxy.sys/icmp_echo.c | 231 ++++++++++++++++++++++++++- dlls/nsiproxy.sys/nsiproxy_private.h | 1 + include/wine/nsi.h | 1 + 6 files changed, 262 insertions(+), 3 deletions(-) diff --git a/dlls/iphlpapi/iphlpapi_main.c b/dlls/iphlpapi/iphlpapi_main.c index 83413804d727..595f499c36e3 100644 --- a/dlls/iphlpapi/iphlpapi_main.c +++ b/dlls/iphlpapi/iphlpapi_main.c @@ -4904,11 +4904,13 @@ static NTSTATUS icmp_send_echo( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc if (opts) { in->ttl = opts->Ttl; + in->hop_limit = opts->Ttl; in->tos = opts->Tos; in->flags = opts->Flags; memcpy( in->data, opts->OptionsData, opts->OptionsSize ); in->opt_size = opts->OptionsSize; } + else in->hop_limit = -1; in->req_size = request_size; in->timeout = timeout; memcpy( in->data + opt_size, request, request_size ); @@ -4986,9 +4988,20 @@ DWORD WINAPI Icmp6SendEcho2( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc_ro struct sockaddr_in6 *src, struct sockaddr_in6 *dst, void *request, WORD request_size, IP_OPTION_INFORMATION *opts, void *reply, DWORD reply_size, DWORD timeout ) { - FIXME( "(%p, %p, %p, %p, %p, %p, %p, %d, %p, %p, %ld, %ld): stub\n", handle, event, + SOCKADDR_INET src_addr, dst_addr; + NTSTATUS status; + + TRACE( "(%p, %p, %p, %p, %p, %p, %p, %d, %p, %p, %ld, %ld).\n", handle, event, apc_routine, apc_ctxt, src, dst, request, request_size, opts, reply, reply_size, timeout ); - SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); + + src_addr.Ipv6 = *src; + if (!src_addr.si_family) src_addr.si_family = AF_INET6; + dst_addr.Ipv6 = *dst; + if (!dst_addr.si_family) dst_addr.si_family = AF_INET6; + status = icmp_send_echo( handle, event, apc_routine, apc_ctxt, &src_addr, &dst_addr, request, request_size, + opts, reply, reply_size, timeout ); + if (!status) return Icmp6ParseReplies( reply, reply_size ); + SetLastError( RtlNtStatusToDosError( status ) ); return 0; } diff --git a/dlls/iphlpapi/tests/iphlpapi.c b/dlls/iphlpapi/tests/iphlpapi.c index 10457a4310f4..26a44b2aeaac 100644 --- a/dlls/iphlpapi/tests/iphlpapi.c +++ b/dlls/iphlpapi/tests/iphlpapi.c @@ -961,6 +961,7 @@ static void testIcmpSendEcho(void) char senddata[32], replydata[sizeof(senddata) + sizeof(ICMP_ECHO_REPLY)]; char replydata2[sizeof(replydata) + sizeof(IO_STATUS_BLOCK)]; DWORD ret, error, replysz = sizeof(replydata); + IP_OPTION_INFORMATION opt; IPAddr address; ICMP_ECHO_REPLY *reply; HANDLE event; @@ -1020,6 +1021,12 @@ static void testIcmpSendEcho(void) error = GetLastError(); ok (ret, "IcmpSendEcho failed unexpectedly with error %ld\n", error); + memset(&opt, 0, sizeof(opt)); + SetLastError(0xdeadbeef); + ret = IcmpSendEcho(icmp, address, NULL, 0, &opt, replydata, replysz, 1000); + error = GetLastError(); + ok (ret, "IcmpSendEcho failed unexpectedly with error %ld\n", error); + SetLastError(0xdeadbeef); ret = IcmpSendEcho(icmp, address, senddata, sizeof(senddata), NULL, NULL, replysz, 1000); error = GetLastError(); diff --git a/dlls/nsiproxy.sys/device.c b/dlls/nsiproxy.sys/device.c index b3d00fd5187a..b1c674ca5f34 100644 --- a/dlls/nsiproxy.sys/device.c +++ b/dlls/nsiproxy.sys/device.c @@ -29,6 +29,7 @@ #include "ddk/wdm.h" #include "ifdef.h" #include "netiodef.h" +#include "ipexport.h" #include "wine/nsi.h" #include "wine/debug.h" #include "wine/unixlib.h" @@ -220,6 +221,8 @@ static int icmp_echo_reply_struct_len( ULONG family, ULONG bits ) { if (family == AF_INET) return (bits == 32) ? sizeof(struct icmp_echo_reply_32) : sizeof(struct icmp_echo_reply_64); + if (family == AF_INET6) + return sizeof(ICMPV6_ECHO_REPLY); return 0; } @@ -274,6 +277,7 @@ static NTSTATUS handle_send_echo( IRP *irp ) params.reply = irp->AssociatedIrp.SystemBuffer; params.bits = in->bits; params.ttl = in->ttl; + params.hop_limit = in->hop_limit; params.tos = in->tos; params.src = &in->src; params.dst = &in->dst; @@ -321,6 +325,10 @@ static NTSTATUS nsiproxy_icmp_echo( IRP *irp ) case AF_INET: if (in->dst.Ipv4.sin_addr.s_addr == INADDR_ANY) return STATUS_INVALID_ADDRESS_WILDCARD; break; + case AF_INET6: + if (IN6_IS_ADDR_UNSPECIFIED(&in->dst.Ipv6.sin6_addr)) return STATUS_INVALID_ADDRESS_WILDCARD; + break; + default: return STATUS_INVALID_PARAMETER; } diff --git a/dlls/nsiproxy.sys/icmp_echo.c b/dlls/nsiproxy.sys/icmp_echo.c index 807f4e89e189..7833704ec937 100644 --- a/dlls/nsiproxy.sys/icmp_echo.c +++ b/dlls/nsiproxy.sys/icmp_echo.c @@ -75,6 +75,28 @@ struct ip_hdr uint32_t daddr; }; +struct ipv6_hdr +{ + uint8_t v_prio; /* version << 4 | priority */ + uint8_t flow_lbl[3]; + uint16_t next_len; + uint8_t next_hdr; + uint8_t hop_limit; + struct in6_addr saddr; + struct in6_addr daddr; +}; + +C_ASSERT( sizeof(struct ipv6_hdr) == 40 ); + +struct ipv6_pseudo_header +{ + struct in6_addr src; + struct in6_addr dst; + UINT32 next_len; /* incapsulated packet length in network byte order */ + BYTE zero[3]; + BYTE next_header; +}; + struct icmp_hdr { uint8_t type; @@ -117,6 +139,7 @@ struct icmp_data struct sockaddr_storage dst_storage; int src_len; int dst_len; + int hop_limit; BOOL ping_socket; }; @@ -496,6 +519,203 @@ static const struct family_ops ipv4_linux_ping = }; #endif +static void ipv6_init_icmp_hdr( struct icmp_data *data, struct icmp_hdr *icmp_hdr ) +{ + icmp_hdr->type = ICMP6_ECHO_REQUEST; + icmp_hdr->code = 0; + icmp_hdr->checksum = 0; + icmp_hdr->un.echo.id = data->id = getpid() & 0xffff; /* will be overwritten for linux ping socks */ + icmp_hdr->un.echo.sequence = data->seq = InterlockedIncrement( &icmp_sequence ) & 0xffff; +} + +static unsigned short ipv6_chksum( struct icmp_data *icmp_data, BYTE *data, unsigned int count ) +{ + struct ipv6_pseudo_header *ip_h; + struct sockaddr_in6 addr; + unsigned short sum; + socklen_t slen; + int s, ret; + + if (icmp_data->ping_socket) return 0; + + /* Determine source address. Do it on a separate socket or raw socket won't receive ICMP replies + * originating not from the destination address. */ + s = socket( AF_INET6, SOCK_RAW, IPPROTO_ICMPV6 ); + if (s < 0) + { + TRACE( "failed to open raw sock, trying a dgram sock\n" ); + s = socket( AF_INET6, SOCK_DGRAM, IPPROTO_ICMPV6 ); + if (s < 0) + { + WARN( "Unable to create socket\n" ); + return 0; + } + } + if (bind( s, (void *)&icmp_data->src_storage, icmp_data->src_len )) + { + close( s ); + return 0; + } + if (connect( s, (void *)&icmp_data->dst_storage, icmp_data->dst_len )) + { + close( s ); + return 0; + } + slen = sizeof(addr); + ret = getsockname( s, (void *)&addr, &slen ); + close( s ); + if (ret) return 0; + + ip_h = malloc( sizeof(*ip_h) + count ); + if (!ip_h) return 0; + memset( ip_h, 0, sizeof(*ip_h) ); + ip_h->src = addr.sin6_addr; + ip_h->dst = ((struct sockaddr_in6 *)&icmp_data->dst_storage)->sin6_addr; + ip_h->next_len = htonl( count ); + ip_h->next_header = IPPROTO_ICMPV6; + memcpy( ip_h + 1, data, count ); + sum = chksum( icmp_data, (BYTE *)ip_h, sizeof(*ip_h) + count ); + free( ip_h ); + return sum; +} + +static int ipv6_set_reply_ip_status( IP_STATUS ip_status, unsigned int bits, void *out ) +{ + ICMPV6_ECHO_REPLY *reply = out; + + memset( reply, 0, sizeof(*reply) ); + reply->Status = ip_status; + return sizeof(*reply); +} + +static void ipv6_set_socket_opts( struct icmp_data *data, struct icmp_send_echo_params *params ) +{ +#ifdef IPV6_UNICAST_HOPS +{ + int val = params->hop_limit; + + setsockopt( data->socket, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &val, sizeof(val) ); +} +#endif +} + +static int ipv6_reply_buffer_len( struct icmp_listen_params *params ) +{ + return sizeof(struct icmp_hdr) + params->reply_len - sizeof(ICMPV6_ECHO_REPLY); +} + +static BOOL ipv6_parse_ip_hdr( struct msghdr *msg, int recvd, int *ip_hdr_len, + struct icmp_reply_ctx *ctx ) +{ + *ip_hdr_len = 0; + ctx->options_data = NULL; + ctx->ttl = 0; + ctx->tos = 0; + ctx->flags = 0; + ctx->options_size = 0; + return TRUE; +} + +static int ipv6_parse_icmp_hdr( struct icmp_data *data, struct icmp_hdr *icmp, + int icmp_size, struct icmp_reply_ctx *ctx ) +{ + static const IP_STATUS unreach_codes[] = + { + IP_DEST_NO_ROUTE, + IP_DEST_PROHIBITED, + IP_DEST_SCOPE_MISMATCH, + IP_DEST_ADDR_UNREACHABLE, + IP_DEST_PORT_UNREACHABLE, + IP_ICMP_ERROR, + IP_DEST_UNREACHABLE, + IP_BAD_HEADER, + }; + const struct ipv6_hdr *orig_ip_hdr; + const struct icmp_hdr *orig_icmp_hdr; + IP_STATUS status; + + switch (icmp->type) + { + case ICMP6_ECHO_REPLY: + if ((!data->ping_socket && icmp->un.echo.id != data->id) || + icmp->un.echo.sequence != data->seq) return -1; + + ctx->status = IP_SUCCESS; + return icmp_size - sizeof(*icmp); + + case ICMP6_DST_UNREACH: + if (icmp->code < ARRAY_SIZE(unreach_codes)) + status = unreach_codes[icmp->code]; + else + status = IP_DEST_HOST_UNREACHABLE; + break; + + case ICMP6_PACKET_TOO_BIG: + status = IP_PACKET_TOO_BIG; + break; + + case ICMP6_TIME_EXCEEDED: + switch(icmp->code) + { + case 0: status = IP_HOP_LIMIT_EXCEEDED; break; + case 1: status = IP_REASSEMBLY_TIME_EXCEEDED; break; + default: status = IP_TIME_EXCEEDED; break; + } + break; + + case ICMP6_PARAM_PROB: + status = IP_PARAMETER_PROBLEM; + break; + + default: + return -1; + } + + if (data->ping_socket) return 0; + /* Check that the appended packet is really ours. */ + if (icmp_size < sizeof(*icmp) + sizeof(*orig_ip_hdr)) return -1; + orig_ip_hdr = (struct ipv6_hdr *)(icmp + 1); + if ((orig_ip_hdr->v_prio >> 4) != 6 || orig_ip_hdr->next_hdr != IPPROTO_ICMPV6) return -1; + if (icmp_size < sizeof(*icmp) + sizeof(*orig_ip_hdr) + sizeof(*orig_icmp_hdr)) return -1; + orig_icmp_hdr = (const struct icmp_hdr *)((const BYTE *)orig_ip_hdr + sizeof(*orig_ip_hdr)); + if (orig_icmp_hdr->type != ICMP6_ECHO_REQUEST || + orig_icmp_hdr->code != 0 || + (!data->ping_socket && orig_icmp_hdr->un.echo.id != data->id) || + orig_icmp_hdr->un.echo.sequence != data->seq) return -1; + + ctx->status = status; + + return 0; +} + +static void ipv6_fill_reply( struct icmp_listen_params *params, struct icmp_reply_ctx *ctx) +{ + ICMPV6_ECHO_REPLY *reply = params->reply; + + reply->Status = ctx->status; + memcpy( reply->Address.sin6_addr, &ctx->addr.Ipv6.sin6_addr, sizeof(ctx->addr.Ipv6.sin6_addr) ); + reply->Address.sin6_flowinfo = ctx->addr.Ipv6.sin6_flowinfo; + reply->Address.sin6_port = ctx->addr.Ipv6.sin6_port; + reply->Address.sin6_scope_id = ctx->addr.Ipv6.sin6_scope_id; + reply->RoundTripTime = ctx->round_trip_time; + memcpy( reply + 1, ctx->data, ctx->data_size ); + params->reply_len = sizeof(*reply) + ctx->data_size; +} + +static const struct family_ops ipv6 = +{ + AF_INET6, + IPPROTO_ICMPV6, + ipv6_init_icmp_hdr, + ipv6_chksum, + ipv6_set_reply_ip_status, + ipv6_set_socket_opts, + ipv6_reply_buffer_len, + ipv6_parse_ip_hdr, + ipv6_parse_icmp_hdr, + ipv6_fill_reply, +}; + static IP_STATUS errno_to_ip_status( int err ) { switch( err ) @@ -568,7 +788,8 @@ static NTSTATUS icmp_data_create( ADDRESS_FAMILY win_family, struct icmp_data ** struct icmp_data *data; const struct family_ops *ops; - if (win_family == WS_AF_INET) ops = &ipv4; + if (win_family == WS_AF_INET6) ops = &ipv6; + else if (win_family == WS_AF_INET) ops = &ipv4; else return STATUS_INVALID_PARAMETER; data = malloc( sizeof(*data) ); @@ -624,6 +845,7 @@ NTSTATUS icmp_send_echo( void *args ) status = icmp_data_create( params->dst->si_family, &data ); if (status) return status; + data->hop_limit = params->hop_limit; src = (struct sockaddr *)&data->src_storage; dst = (struct sockaddr *)&data->dst_storage; data->src_len = SOCKADDR_INET_to_sockaddr( params->src, src, sizeof(data->src_storage) ); @@ -745,6 +967,13 @@ NTSTATUS icmp_listen( void *args ) data = handle_data( params->handle ); if (!data) return STATUS_INVALID_PARAMETER; + if (data->dst_storage.ss_family == AF_INET6 && !data->hop_limit) + { + TRACE( "Invalid hop_limit.\n" ); + params->reply_len = data->ops->set_reply_ip_status( IP_GENERAL_FAILURE, params->bits, params->reply ); + return STATUS_SUCCESS; + } + fds[0].fd = data->socket; fds[0].events = POLLIN; fds[1].fd = data->cancel_pipe[0]; diff --git a/dlls/nsiproxy.sys/nsiproxy_private.h b/dlls/nsiproxy.sys/nsiproxy_private.h index e7cc707f729c..ab40fa2057aa 100644 --- a/dlls/nsiproxy.sys/nsiproxy_private.h +++ b/dlls/nsiproxy.sys/nsiproxy_private.h @@ -46,6 +46,7 @@ struct icmp_send_echo_params void *request, *reply; UINT request_size, reply_len; BYTE bits, ttl, tos; + int hop_limit; icmp_handle *handle; }; diff --git a/include/wine/nsi.h b/include/wine/nsi.h index 0df313db624e..e281321b026e 100644 --- a/include/wine/nsi.h +++ b/include/wine/nsi.h @@ -476,6 +476,7 @@ struct nsiproxy_icmp_echo UINT opt_size; UINT req_size; UINT timeout; + int hop_limit; BYTE data[1]; /* ((opt_size + 3) & ~3) + req_size */ }; From 5c4056e1476a75b06c41c47d5182538ea85b2309 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 18 Aug 2025 18:18:34 -0600 Subject: [PATCH 383/454] iphlpapi/tests: Add tests for Icmp6SendEcho2(). (cherry picked from commit 58f8ef30cae824a9b779a278664247fd3388d7b7) CW-Bug-Id: #25798 --- dlls/iphlpapi/tests/iphlpapi.c | 163 +++++++++++++++++++++++++++++++++ 1 file changed, 163 insertions(+) diff --git a/dlls/iphlpapi/tests/iphlpapi.c b/dlls/iphlpapi/tests/iphlpapi.c index 26a44b2aeaac..7aa73744813b 100644 --- a/dlls/iphlpapi/tests/iphlpapi.c +++ b/dlls/iphlpapi/tests/iphlpapi.c @@ -35,6 +35,8 @@ */ #include +#include "ntstatus.h" +#define WIN32_NO_STATUS #include "winsock2.h" #include "windef.h" #include "winbase.h" @@ -1413,6 +1415,166 @@ static void testIcmpParseReplies( void ) ok( !reply.Reserved, "reserved %d\n", reply.Reserved ); } +static void test_Icmp6SendEcho(void) +{ + char senddata[32], replydata[sizeof(senddata) + sizeof(ICMPV6_ECHO_REPLY)]; + struct sockaddr_in6 src_addr, address; + IP_OPTION_INFORMATION opt; + ICMPV6_ECHO_REPLY *reply; + DWORD ret, replysz; + char str[256]; + HANDLE event; + HANDLE icmp; + + icmp = Icmp6CreateFile(); + ok(icmp != INVALID_HANDLE_VALUE, "got error %ld.\n", GetLastError()); + + memset(senddata, 0xdd, sizeof(senddata)); + + memset(&src_addr, 0, sizeof(src_addr)); + memset(&address, 0, sizeof(address)); + ret = inet_pton( AF_INET6, "::1", &address.sin6_addr); + ok(ret, "got error %u.\n", WSAGetLastError()); + + SetLastError(0xdeadbeef); + replysz = sizeof(replydata); + memset(replydata, 0xcc, sizeof(replydata)); + reply = (ICMPV6_ECHO_REPLY*)replydata; + ret = Icmp6SendEcho2(icmp, NULL, NULL, NULL, &src_addr, &address, senddata, sizeof(senddata), NULL, + replydata, replysz, 1000); + todo_wine_if(!ret && GetLastError() == ERROR_ACCESS_DENIED) + ok(ret == 1, "got ret %lu, error %ld.\n", ret, GetLastError()); + if (!ret) + { + skip( "ipv6 ping is prohibited.\n" ); + IcmpCloseHandle(icmp); + return; + } + ok(!GetLastError(), "got %ld.\n", GetLastError()); + ok(!reply->Status, "got %lu.\n", reply->Status); + ok(!memcmp(reply->Address.sin6_addr, &address.sin6_addr, sizeof(address.sin6_addr)), "got %s.\n", + inet_ntop(AF_INET6, (void *)&reply->Address.sin6_addr, str, sizeof(str))); + ok(!reply->Address.sin6_port, "got %#x.\n", reply->Address.sin6_port); + ok(!reply->Address.sin6_flowinfo, "got %#lx.\n", reply->Address.sin6_flowinfo); + ok(!reply->Address.sin6_scope_id, "got %#lx.\n", reply->Address.sin6_scope_id); + ok(!memcmp(reply + 1, senddata, sizeof(senddata)), "reply data does not match.\n"); + + memset(&src_addr, 0, sizeof(src_addr)); + memset(&address, 0, sizeof(address)); + memset(&opt, 0, sizeof(opt)); + ret = inet_pton( AF_INET6, "::1", &address.sin6_addr); + ok(ret, "got error %u.\n", WSAGetLastError()); + ret = inet_pton( AF_INET6, "::1", &src_addr.sin6_addr); + ok(ret, "got error %u.\n", WSAGetLastError()); + memset(replydata, 0xcc, sizeof(replydata)); + ret = Icmp6SendEcho2(icmp, NULL, NULL, NULL, &src_addr, &address, senddata, sizeof(senddata), &opt, + replydata, replysz, 1000); + ok(!ret && GetLastError() == IP_GENERAL_FAILURE, "got ret %#lx, error %ld.\n", ret, GetLastError()); + ok(reply->Status == IP_GENERAL_FAILURE, "got %#lx.\n", reply->Status); + + opt.Ttl = 1; + ret = Icmp6SendEcho2(icmp, NULL, NULL, NULL, &src_addr, &address, senddata, sizeof(senddata), &opt, + replydata, replysz, 1000); + ok(ret == 1, "got ret %lu, error %ld.\n", ret, GetLastError()); + ok(!GetLastError(), "got %ld.\n", GetLastError()); + ok(!reply->Status, "got %#lx.\n", reply->Status); + ok(!memcmp(reply->Address.sin6_addr, &address.sin6_addr, sizeof(address.sin6_addr)), "got %s.\n", + inet_ntop(AF_INET6, (void *)&reply->Address.sin6_addr, str, sizeof(str))); + ok(!reply->Address.sin6_port, "got %#x.\n", reply->Address.sin6_port); + ok(!reply->Address.sin6_flowinfo, "got %#lx.\n", reply->Address.sin6_flowinfo); + ok(!reply->Address.sin6_scope_id, "got %#lx.\n", reply->Address.sin6_scope_id); + ok(!memcmp(reply + 1, senddata, sizeof(senddata)), "reply data does not match.\n"); + + memset(&src_addr, 0, sizeof(src_addr)); + memset(&address, 0, sizeof(address)); + event = CreateEventW(NULL, FALSE, FALSE, NULL); + + icmp_send_echo_test_apc_expect = FALSE; + icmp_send_echo_test_line = __LINE__; + memset(&icmp_send_echo_io, 0xcc, sizeof(icmp_send_echo_io)); + memset(replydata, 0xcc, sizeof(replydata)); + replysz = sizeof(replydata); + ret = Icmp6SendEcho2(icmp, NULL, icmp_send_echo_test_apc, (void*)0xdeadc0de, &src_addr, &address, senddata, + sizeof(senddata), NULL, replydata, replysz, 1000); + ok(!ret && GetLastError() == ERROR_INVALID_NETNAME, "got ret %lu, error %ld.\n", ret, GetLastError()); + + ret = inet_pton( AF_INET6, "::1", &address.sin6_addr); + ok(ret, "got error %u.\n", WSAGetLastError()); + + icmp_send_echo_test_apc_expect = TRUE; + icmp_send_echo_test_line = __LINE__; + memset(&icmp_send_echo_io, 0xcc, sizeof(icmp_send_echo_io)); + memset(replydata, 0xcc, sizeof(replydata)); + replysz = sizeof(replydata); + ret = Icmp6SendEcho2(icmp, NULL, icmp_send_echo_test_apc, (void*)0xdeadc0de, &src_addr, &address, senddata, + sizeof(senddata), NULL, replydata, replysz, 1000); + ok(!ret && GetLastError() == ERROR_IO_PENDING, "got ret %lu, error %ld.\n", ret, GetLastError()); + ret = WaitForSingleObjectEx(event, INFINITE, TRUE); + ok(ret == WAIT_IO_COMPLETION, "got %#lx.\n", ret); + ok(!icmp_send_echo_io.Status, "got %#lx.\n", icmp_send_echo_io.Status); + ok(icmp_send_echo_io.Information == sizeof(replydata), "got %Iu.\n", icmp_send_echo_io.Information); + ok(!reply->Status, "got %#lx.\n", reply->Status); + ok(!icmp_send_echo_test_apc_expect, "APC was not called.\n"); + ok(!memcmp(reply->Address.sin6_addr, &address.sin6_addr, sizeof(address.sin6_addr)), "got %s.\n", + inet_ntop(AF_INET6, (void *)&reply->Address.sin6_addr, str, sizeof(str))); + ok(!reply->Address.sin6_port, "got %#x.\n", reply->Address.sin6_port); + ok(!reply->Address.sin6_flowinfo, "got %#lx.\n", reply->Address.sin6_flowinfo); + ok(!reply->Address.sin6_scope_id, "got %#lx.\n", reply->Address.sin6_scope_id); + ok(!memcmp(reply + 1, senddata, sizeof(senddata)), "reply data does not match.\n"); + + ret = inet_pton( AF_INET6, "::1", &address.sin6_addr); + icmp_send_echo_test_apc_expect = FALSE; + icmp_send_echo_test_line = __LINE__; + memset(replydata, 0xcc, sizeof(replydata)); + replysz = sizeof(replydata); + ret = Icmp6SendEcho2(icmp, event, icmp_send_echo_test_apc, (void*)0xdeadc0de, &src_addr, &address, senddata, + sizeof(senddata), NULL, replydata, replysz, 1000); + ok(!ret && GetLastError() == ERROR_IO_PENDING, "got ret %lu, error %ld.\n", ret, GetLastError()); + ret = WaitForSingleObjectEx(event, INFINITE, TRUE); + ok(ret == WAIT_OBJECT_0, "got %#lx.\n", ret); + icmp_send_echo_test_line = __LINE__; + SleepEx(0, TRUE); + ok(!reply->Status, "got %#lx.\n", reply->Status); + ok(!memcmp(reply->Address.sin6_addr, &address.sin6_addr, sizeof(address.sin6_addr)), "got %s.\n", + inet_ntop(AF_INET6, (void *)&reply->Address.sin6_addr, str, sizeof(str))); + ok(!reply->Address.sin6_port, "got %#x.\n", reply->Address.sin6_port); + ok(!reply->Address.sin6_flowinfo, "got %#lx.\n", reply->Address.sin6_flowinfo); + ok(!reply->Address.sin6_scope_id, "got %#lx.\n", reply->Address.sin6_scope_id); + ok(!memcmp(reply + 1, senddata, sizeof(senddata)), "reply data does not match.\n"); + + memset(replydata, 0xcc, sizeof(replydata)); + replysz = sizeof(replydata); + opt.Ttl = 0; + icmp_send_echo_test_apc_expect = TRUE; + icmp_send_echo_test_line = __LINE__; + memset(&icmp_send_echo_io, 0xcc, sizeof(icmp_send_echo_io)); + ret = Icmp6SendEcho2(icmp, NULL, icmp_send_echo_test_apc, (void*)0xdeadc0de, &src_addr, &address, senddata, + sizeof(senddata), &opt, replydata, replysz, 1000); + ok(!ret && GetLastError() == ERROR_IO_PENDING, "got ret %lu, error %ld.\n", ret, GetLastError()); + ret = WaitForSingleObjectEx(event, INFINITE, TRUE); + ok(ret == WAIT_IO_COMPLETION, "got %#lx.\n", ret); + ok(!icmp_send_echo_test_apc_expect, "APC was not called.\n"); + todo_wine ok(icmp_send_echo_io.Status == STATUS_INVALID_PARAMETER, "got %#lx.\n", icmp_send_echo_io.Status); + ok(icmp_send_echo_io.Information == sizeof(ICMPV6_ECHO_REPLY), "got %Iu.\n", icmp_send_echo_io.Information); + ok(reply->Status == IP_GENERAL_FAILURE, "got %#lx.\n", reply->Status); + ok(IN6_IS_ADDR_UNSPECIFIED((IN6_ADDR *)&reply->Address.sin6_addr), "got %s.\n", + inet_ntop(AF_INET6, (void *)&reply->Address.sin6_addr, str, sizeof(str))); + ok(!reply->Address.sin6_port, "got %#x.\n", reply->Address.sin6_port); + ok(!reply->Address.sin6_flowinfo, "got %#lx.\n", reply->Address.sin6_flowinfo); + ok(!reply->Address.sin6_scope_id, "got %#lx.\n", reply->Address.sin6_scope_id); + + ResetEvent(event); + ret = inet_pton( AF_INET6, "::ffff", &src_addr.sin6_addr); + ok(ret, "got error %u.\n", WSAGetLastError()); + ret = Icmp6SendEcho2(icmp, event, NULL, NULL, &src_addr, &address, senddata, + sizeof(senddata), NULL, replydata, replysz, 1000); + ok(!ret && GetLastError() == ERROR_INVALID_NETNAME, "got ret %lu, error %ld.\n", ret, GetLastError()); + + CloseHandle(event); + ret = IcmpCloseHandle(icmp); + ok(ret, "got error %u.\n", WSAGetLastError()); +} + static void testIcmp6ParseReplies( void ) { ICMPV6_ECHO_REPLY reply = { 0 }; @@ -1451,6 +1613,7 @@ static void testWinNT4Functions(void) testSetTcpEntry(); testIcmpSendEcho(); testIcmpParseReplies(); + test_Icmp6SendEcho(); testIcmp6ParseReplies(); } From 5bbba3d9b1f0d59834dfce61a32af89094d58e7c Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 25 Aug 2025 10:18:00 -0600 Subject: [PATCH 384/454] iphlpapi: Avoid leaking APC context in icmp_send_echo(). CW-Bug-Id: #25798 --- dlls/iphlpapi/iphlpapi_main.c | 38 ++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/dlls/iphlpapi/iphlpapi_main.c b/dlls/iphlpapi/iphlpapi_main.c index 595f499c36e3..135e1dc4cddb 100644 --- a/dlls/iphlpapi/iphlpapi_main.c +++ b/dlls/iphlpapi/iphlpapi_main.c @@ -4870,32 +4870,22 @@ static NTSTATUS icmp_send_echo( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc WORD request_size, IP_OPTION_INFORMATION *opts, void *reply, DWORD reply_size, DWORD timeout ) { + static IO_STATUS_BLOCK iosb_placeholder; struct icmp_handle_data *data = (struct icmp_handle_data *)handle; - struct icmp_apc_ctxt *ctxt = heap_alloc( sizeof(*ctxt) ); - IO_STATUS_BLOCK *iosb = &ctxt->iosb; + struct icmp_apc_ctxt *ctxt = NULL; + IO_STATUS_BLOCK *iosb; DWORD opt_size, in_size; struct nsiproxy_icmp_echo *in; HANDLE request_event; NTSTATUS status; - if (handle == INVALID_HANDLE_VALUE || !reply) - { - heap_free( ctxt ); - return STATUS_INVALID_PARAMETER; - } - - ctxt->apc_routine = apc_routine; - ctxt->apc_ctxt = apc_ctxt; + if (handle == INVALID_HANDLE_VALUE || !reply) return STATUS_INVALID_PARAMETER; opt_size = opts ? (opts->OptionsSize + 3) & ~3 : 0; in_size = FIELD_OFFSET(struct nsiproxy_icmp_echo, data[opt_size + request_size]); in = heap_alloc_zero( in_size ); - if (!in) - { - heap_free( ctxt ); - return STATUS_NO_MEMORY; - } + if (!in) return STATUS_NO_MEMORY; in->user_reply_ptr = (ULONG_PTR)reply; in->bits = sizeof(void*) * 8; @@ -4917,8 +4907,24 @@ static NTSTATUS icmp_send_echo( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc request_event = event ? event : (apc_routine ? NULL : CreateEventW( NULL, 0, 0, NULL )); + if (event) + { + /* Async completion without calling APC routine, IOSB is not delivered anywhere. */ + iosb = &iosb_placeholder; + } + else + { + if (!(ctxt = heap_alloc( sizeof(*ctxt) ))) + { + heap_free( in ); + return STATUS_NO_MEMORY; + } + iosb = &ctxt->iosb; + ctxt->apc_routine = apc_routine; + ctxt->apc_ctxt = apc_ctxt; + } status = NtDeviceIoControlFile( data->nsi_device, request_event, apc_routine && !event ? icmp_apc_routine : NULL, - apc_routine ? ctxt : apc_ctxt, iosb, IOCTL_NSIPROXY_WINE_ICMP_ECHO, + ctxt, iosb, IOCTL_NSIPROXY_WINE_ICMP_ECHO, in, in_size, reply, reply_size ); if (status == STATUS_PENDING) From 6124a7da6b0588a074e1112285c2b86932680224 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 25 Aug 2025 19:59:17 -0600 Subject: [PATCH 385/454] dbghelp: Use GetCurrentProcess() special handle in EnumerateLoadedModulesW64() if possible. --- dlls/dbghelp/module.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/dlls/dbghelp/module.c b/dlls/dbghelp/module.c index 79a0de42a817..e8f2dfeae3bc 100644 --- a/dlls/dbghelp/module.c +++ b/dlls/dbghelp/module.c @@ -1346,6 +1346,14 @@ BOOL WINAPI EnumerateLoadedModulesW64(HANDLE process, WCHAR* wowdir = NULL; size_t sysdir_len = 0, wowdir_len = 0; + TRACE("process %p.\n", process); + + if (GetCurrentProcessId() == GetProcessId(process)) + { + TRACE("same process.\n"); + process = GetCurrentProcess(); + } + /* process might not be a handle to a live process */ if (!IsWow64Process2(process, &pcs_machine, &native_machine)) { @@ -1426,6 +1434,7 @@ BOOL WINAPI EnumerateLoadedModulesW64(HANDLE process, HeapFree(GetProcessHeap(), 0, hmods); HeapFree(GetProcessHeap(), 0, sysdir); + TRACE("done, count %d.\n", count); return count != 0; } From b076c794c0facfba40763d03a0bdabc0969461b9 Mon Sep 17 00:00:00 2001 From: Piotr Caban Date: Mon, 22 Jul 2024 13:12:41 +0200 Subject: [PATCH 386/454] ntdll: Optimize NtReadVirtualMemory for in-process reads. (cherry picked from commit 3ad5250b19f3903bbabecb6419fec09bb0c97ebd, modified to account for current Proton diff) --- dlls/ntdll/unix/virtual.c | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c index 64e56c36a782..51981550a949 100644 --- a/dlls/ntdll/unix/virtual.c +++ b/dlls/ntdll/unix/virtual.c @@ -6926,20 +6926,29 @@ NTSTATUS WINAPI NtReadVirtualMemory( HANDLE process, const void *addr, void *buf if (process == NtCurrentProcess()) { - unix_pid = getpid(); - status = STATUS_SUCCESS; - } - else - { - SERVER_START_REQ( read_process_memory ) + __TRY { - req->handle = wine_server_obj_handle( process ); - status = wine_server_call( req ); - unix_pid = reply->unix_pid; + memmove( buffer, addr, size ); + status = STATUS_SUCCESS; } - SERVER_END_REQ; + __EXCEPT + { + status = STATUS_PARTIAL_COPY; + size = 0; + } + __ENDTRY + if (bytes_read) *bytes_read = size; + return status; } + SERVER_START_REQ( read_process_memory ) + { + req->handle = wine_server_obj_handle( process ); + status = wine_server_call( req ); + unix_pid = reply->unix_pid; + } + SERVER_END_REQ; + if (status) { WARN( "Could not get unix_pid for process %p, status %#x.\n", process, status ); From 9223bd7ee183d56e1c5a19ba8323c41653faef6a Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 26 Aug 2025 10:51:30 -0600 Subject: [PATCH 387/454] amend! ntdll: Optimize NtReadVirtualMemory for in-process reads. ntdll: Optimize NtReadVirtualMemory for in-process reads. (cherry picked from commit 3ad5250b19f3903bbabecb6419fec09bb0c97ebd, modified to account for current Proton diff) CW-Bug-Id: #25855 From 8fe197f37b83149566b7f2f03a22a966a8a0656d Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 26 Aug 2025 10:51:30 -0600 Subject: [PATCH 388/454] amend! dbghelp: Use GetCurrentProcess() special handle in EnumerateLoadedModulesW64() if possible. dbghelp: Use GetCurrentProcess() special handle in EnumerateLoadedModulesW64() if possible. CW-Bug-Id: #25855 From 53d0b25ccce12e532fc06a6ff95b9858166685ea Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 21 Aug 2025 19:00:18 -0600 Subject: [PATCH 389/454] windows.media.speech: Set OPENBLAS_NUM_THREADS to 1. CW-Bug-Id: #25825 --- dlls/windows.media.speech/unixlib.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dlls/windows.media.speech/unixlib.c b/dlls/windows.media.speech/unixlib.c index 55e48a550ff1..f068fa0c1507 100644 --- a/dlls/windows.media.speech/unixlib.c +++ b/dlls/windows.media.speech/unixlib.c @@ -67,6 +67,9 @@ MAKE_FUNCPTR(vosk_recognizer_reset); static NTSTATUS process_attach( void *args ) { + TRACE("setting OPENBLAS_NUM_THREADS to 1.\n"); + setenv("OPENBLAS_NUM_THREADS", "1", 1); + if (!(vosk_handle = dlopen(SONAME_LIBVOSK, RTLD_NOW))) { ERR_(winediag)("Wine is unable to load the Unix side dependencies for speech recognition. " @@ -232,7 +235,6 @@ static NTSTATUS find_model_by_locale_and_path( const char *path, const char *loc sprintf(model_path, "%s/%s", path, best_match); TRACE("trying to load Vosk model %s.\n", debugstr_a(model_path)); - if ((*model = p_vosk_model_new(model_path)) != NULL) status = STATUS_SUCCESS; From 62493a6f569cd6a002737ee66ca0f30da6855359 Mon Sep 17 00:00:00 2001 From: Esme Povirk Date: Fri, 13 Jun 2025 19:51:07 +0000 Subject: [PATCH 390/454] mscoree: Update Wine Mono to 10.1.0. (cherry picked from commit 4538017cab7f07c151ea16fc2f8d095d97d57959) --- dlls/appwiz.cpl/addons.c | 4 ++-- dlls/mscoree/mscoree_private.h | 2 +- tools/gitlab/test.yml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/dlls/appwiz.cpl/addons.c b/dlls/appwiz.cpl/addons.c index 1feaf77103d9..e55f0dda47c9 100644 --- a/dlls/appwiz.cpl/addons.c +++ b/dlls/appwiz.cpl/addons.c @@ -56,10 +56,10 @@ WINE_DEFAULT_DEBUG_CHANNEL(appwizcpl); #define GECKO_SHA "???" #endif -#define MONO_VERSION "10.0.0" +#define MONO_VERSION "10.1.0" #if defined(__i386__) || defined(__x86_64__) #define MONO_ARCH "x86" -#define MONO_SHA "dbaca73e5d09f7a3a7c157ad04289af9ca47c3ced7012d46544a607046902b87" +#define MONO_SHA "c88c2431890bc32cacec8d7ea70e53a5ae4b95c8c55ca6e75ef8db0e4ccf1070" #else #define MONO_ARCH "" #define MONO_SHA "???" diff --git a/dlls/mscoree/mscoree_private.h b/dlls/mscoree/mscoree_private.h index fe1abb872b0d..7044cb968d9c 100644 --- a/dlls/mscoree/mscoree_private.h +++ b/dlls/mscoree/mscoree_private.h @@ -45,7 +45,7 @@ extern HRESULT assembly_get_runtime_version(ASSEMBLY *assembly, LPSTR *version); extern HRESULT assembly_get_vtable_fixups(ASSEMBLY *assembly, VTableFixup **fixups, DWORD *count); extern HRESULT assembly_get_native_entrypoint(ASSEMBLY *assembly, NativeEntryPointFunc *func); -#define WINE_MONO_VERSION "10.0.0" +#define WINE_MONO_VERSION "10.1.0" /* Mono embedding */ typedef struct _MonoDomain MonoDomain; diff --git a/tools/gitlab/test.yml b/tools/gitlab/test.yml index 9ed9ed610b85..c3c15a3b2b56 100644 --- a/tools/gitlab/test.yml +++ b/tools/gitlab/test.yml @@ -7,7 +7,7 @@ variables: GIT_STRATEGY: none GECKO_VER: 2.47.4 - MONO_VER: 10.0.0 + MONO_VER: 10.1.0 cache: - key: wine-gecko-$GECKO_VER paths: From 7220f6d092a1637cb83432f165be80cf740f0375 Mon Sep 17 00:00:00 2001 From: Etaash Mathamsetty <45927311+Etaash-mathamsetty@users.noreply.github.com> Date: Tue, 15 Jul 2025 15:52:48 -0400 Subject: [PATCH 391/454] amdxc64: Add initial DLL. --- configure.ac | 1 + dlls/amdxc64/Makefile.in | 9 ++ dlls/amdxc64/amdxc64.spec | 5 + dlls/amdxc64/amdxc_interfaces.idl | 63 ++++++++ dlls/amdxc64/main.c | 253 ++++++++++++++++++++++++++++++ 5 files changed, 331 insertions(+) create mode 100644 dlls/amdxc64/Makefile.in create mode 100644 dlls/amdxc64/amdxc64.spec create mode 100644 dlls/amdxc64/amdxc_interfaces.idl create mode 100644 dlls/amdxc64/main.c diff --git a/configure.ac b/configure.ac index 9acc7ce2845b..dff8f27cedc7 100644 --- a/configure.ac +++ b/configure.ac @@ -2456,6 +2456,7 @@ WINE_CONFIG_MAKEFILE(dlls/advpack) WINE_CONFIG_MAKEFILE(dlls/advpack/tests) WINE_CONFIG_MAKEFILE(dlls/amsi) WINE_CONFIG_MAKEFILE(dlls/amd_ags_x64) +WINE_CONFIG_MAKEFILE(dlls/amdxc64) WINE_CONFIG_MAKEFILE(dlls/amstream) WINE_CONFIG_MAKEFILE(dlls/amstream/tests) WINE_CONFIG_MAKEFILE(dlls/apisetschema) diff --git a/dlls/amdxc64/Makefile.in b/dlls/amdxc64/Makefile.in new file mode 100644 index 000000000000..a27f1da40ff6 --- /dev/null +++ b/dlls/amdxc64/Makefile.in @@ -0,0 +1,9 @@ +MODULE = amdxc64.dll +IMPORTS = version vulkan-1 user32 gdi32 +IMPORTLIB = amdxc64 + +EXTRADLLFLAGS = -mno-cygwin + +SOURCES = \ + main.c \ + amdxc_interfaces.idl \ diff --git a/dlls/amdxc64/amdxc64.spec b/dlls/amdxc64/amdxc64.spec new file mode 100644 index 000000000000..5c6ecb267df3 --- /dev/null +++ b/dlls/amdxc64/amdxc64.spec @@ -0,0 +1,5 @@ +@ cdecl AmdExtD3DCreateInterface(ptr ptr ptr) +@ stdcall AmdGetDxcModuleHandle() +@ stub GetSettingsBlobsAll +@ stub OpenAdapter12 +@ stub OpenShimInterface diff --git a/dlls/amdxc64/amdxc_interfaces.idl b/dlls/amdxc64/amdxc_interfaces.idl new file mode 100644 index 000000000000..0c88ec175409 --- /dev/null +++ b/dlls/amdxc64/amdxc_interfaces.idl @@ -0,0 +1,63 @@ +/* + * Copyright 2025 Etaash Mathamsetty + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ +#pragma makedep register + +import "wtypes.idl"; +import "unknwn.idl"; + +[ + object, + uuid(b58d6601-7401-4234-8180-6febfc0e484c), + local +] +interface IAmdExtFfxApi : IUnknown +{ + HRESULT UpdateFfxApiProvider([in, out] void* pData, [in] unsigned int dataSizeInBytes); +} + +[ + object, + uuid(44085fbe-e839-40c5-bf38-0ebc5ab4d0a6), + local +] +interface IAmdExtAntiLagApi : IUnknown +{ + HRESULT UpdateAntiLagState([in, out] void* pData); +} + +[ + object, + uuid(014937ec-9288-446f-a9ac-d75a8e3a984f), + local +] +interface IAmdExtStub1 : IUnknown +{ + HRESULT QueryInterface2([in, out] void* unk, [in] REFIID iid, [in, out] void** out); +} + +[ + object, + uuid(ba019d53-ccab-4cbd-b56a-7230ed4330ad), + local +] +interface IAmdExtStub2 : IUnknown +{ + void stub(); + void stub2([in] unsigned int unk); + void stub3(); +} diff --git a/dlls/amdxc64/main.c b/dlls/amdxc64/main.c new file mode 100644 index 000000000000..b4eb97f34a99 --- /dev/null +++ b/dlls/amdxc64/main.c @@ -0,0 +1,253 @@ +/* + * amdxc implementation + * + * Copyright 2023 Etaash Mathamsetty + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include + +#include "ntstatus.h" +#include "winerror.h" +#define WIN32_NO_STATUS +#include "windef.h" +#include "winbase.h" +#include "winternl.h" +#include "wine/debug.h" +#include "wine/heap.h" + +#define COBJMACROS +#include "initguid.h" +#include "d3d12.h" + +#include "amdxc_interfaces.h" + +WINE_DEFAULT_DEBUG_CHANNEL(amdxc); + +struct AMDFSR4FFX +{ + IAmdExtFfxApi IAmdExtFfxApi_iface; + LONG ref; +}; + +static struct AMDFSR4FFX* impl_from_IAmdExtFfxApi(IAmdExtFfxApi* iface) +{ + return CONTAINING_RECORD(iface, struct AMDFSR4FFX, IAmdExtFfxApi_iface); +} + +ULONG STDMETHODCALLTYPE AMDFSR4FFX_AddRef(IAmdExtFfxApi *iface) +{ + struct AMDFSR4FFX* data = impl_from_IAmdExtFfxApi(iface); + return InterlockedIncrement(&data->ref); +} + +ULONG STDMETHODCALLTYPE AMDFSR4FFX_Release(IAmdExtFfxApi *iface) +{ + struct AMDFSR4FFX* data = impl_from_IAmdExtFfxApi(iface); + ULONG ret = InterlockedDecrement(&data->ref); + if (!ret) free(data); + return ret; +} + +HRESULT STDMETHODCALLTYPE AMDFSR4FFX_QueryInterface(IAmdExtFfxApi *iface, REFIID iid, void **obj) +{ + FIXME("%p %s %p", iface, debugstr_guid(iid), obj); + + return E_NOINTERFACE; +} + +typedef HRESULT (__stdcall *updateffxapi_pfn)(void*, unsigned int); + +HRESULT STDMETHODCALLTYPE AMDFSR4FFX_UpdateFfxApiProvider(IAmdExtFfxApi *iface, void* data, unsigned int size) +{ + static int once; + const char *env; + updateffxapi_pfn pfn; + HMODULE amdffx; + + TRACE("%p %p %u\n", iface, data, size); + + env = getenv("FSR4_UPGRADE"); + + if (env && !strcmp(env, "1")) + { + amdffx = LoadLibraryA("amdxcffx64"); + if (!amdffx) + { + ERR("Failed to load FSR4 dll (amdxcffx)!\n"); + return E_NOINTERFACE; + } + + pfn = (updateffxapi_pfn)GetProcAddress(amdffx, "UpdateFfxApiProvider"); + + if (pfn) + { + if (!once++) WARN("Replaced FSR3 with FSR4!\n"); + return pfn(data, size); + } + } + + return E_NOINTERFACE; +} + +static const struct IAmdExtFfxApiVtbl AMDFSR4FFX_vtable = { + AMDFSR4FFX_QueryInterface, + AMDFSR4FFX_AddRef, + AMDFSR4FFX_Release, + AMDFSR4FFX_UpdateFfxApiProvider +}; + +struct AMDExtStub2 +{ + IAmdExtStub2 IAmdExtStub2_iface; + LONG ref; +}; + +struct AMDExtStub2* impl_from_IAMDExtStub2(IAmdExtStub2 *iface) +{ + return CONTAINING_RECORD(iface, struct AMDExtStub2, IAmdExtStub2_iface); +} + +ULONG STDMETHODCALLTYPE AMDExtStub2_AddRef(IAmdExtStub2 *iface) +{ + struct AMDExtStub2 *this = impl_from_IAMDExtStub2(iface); + return InterlockedIncrement(&this->ref); +} + +ULONG STDMETHODCALLTYPE AMDExtStub2_Release(IAmdExtStub2 *iface) +{ + struct AMDExtStub2 *this = impl_from_IAMDExtStub2(iface); + ULONG ret = InterlockedDecrement(&this->ref); + if (!ret) free(this); + return ret; +} + +HRESULT STDMETHODCALLTYPE AMDExtStub2_QueryInterface(IAmdExtStub2 *iface, REFIID iid, void **out) +{ + FIXME("%p %s %p stub!\n", iface, debugstr_guid(iid), out); + return E_NOINTERFACE; +} + +void STDMETHODCALLTYPE AMDExtStub2_stub1(IAmdExtStub2 *iface) +{ + FIXME("%p stub!\n", iface); +} + +void STDMETHODCALLTYPE AMDExtStub2_stub2(IAmdExtStub2 *iface, unsigned int unk) +{ + FIXME("%p %u stub!\n", iface, unk); +} + +void STDMETHODCALLTYPE AMDExtStub2_stub3(IAmdExtStub2 *iface) +{ + FIXME("%p stub!\n", iface); +} + +const static struct IAmdExtStub2Vtbl AMDSTUB2_vtable = { + AMDExtStub2_QueryInterface, + AMDExtStub2_AddRef, + AMDExtStub2_Release, + AMDExtStub2_stub1, + AMDExtStub2_stub2, + AMDExtStub2_stub3 +}; + +struct AMDExtStub1 +{ + IAmdExtStub1 IAmdExtStub1_iface; + LONG ref; +}; + +struct AMDExtStub1* impl_from_IAMDExtStub1(IAmdExtStub1 *iface) +{ + return CONTAINING_RECORD(iface, struct AMDExtStub1, IAmdExtStub1_iface); +} + +ULONG STDMETHODCALLTYPE AMDExtStub1_AddRef(IAmdExtStub1 *iface) +{ + struct AMDExtStub1 *this = impl_from_IAMDExtStub1(iface); + return InterlockedIncrement(&this->ref); +} + +ULONG STDMETHODCALLTYPE AMDExtStub1_Release(IAmdExtStub1 *iface) +{ + struct AMDExtStub1 *this = impl_from_IAMDExtStub1(iface); + ULONG ret = InterlockedDecrement(&this->ref); + if (!ret) free(this); + return ret; +} + +HRESULT STDMETHODCALLTYPE AmdExtStub1_QueryInterface2(IAmdExtStub1 *iface, void* unk, REFIID iid, void **out) +{ + TRACE("%p %p %s %p\n", iface, unk, debugstr_guid(iid), out); + + if(IsEqualGUID(iid, &IID_IAmdExtStub2)) + { + struct AMDExtStub2 *this = calloc(1, sizeof(struct AMDExtStub2)); + + this->IAmdExtStub2_iface.lpVtbl = &AMDSTUB2_vtable; + this->ref = 1; + *out = &this->IAmdExtStub2_iface; + return S_OK; + } else { + FIXME("unknown guid %s\n", debugstr_guid(iid)); + } + + return E_NOINTERFACE; +} + +HRESULT STDMETHODCALLTYPE AmdExtStub1_QueryInterface(IAmdExtStub1 *iface, REFIID iid, void **out) +{ + return AmdExtStub1_QueryInterface2(iface, NULL, iid, out); +} + +static const struct IAmdExtStub1Vtbl AMDSTUB1_vtable = { + AmdExtStub1_QueryInterface, + AMDExtStub1_AddRef, + AMDExtStub1_Release, + AmdExtStub1_QueryInterface2 +}; + +HRESULT CDECL AmdExtD3DCreateInterface(IUnknown *outer, REFIID iid, void **obj) +{ + TRACE("outer %p, iid %s, obj %p\n", outer, debugstr_guid(iid), obj); + + if (IsEqualGUID(iid, &IID_IAmdExtFfxApi)) + { + struct AMDFSR4FFX* ffx = calloc(1, sizeof(struct AMDFSR4FFX)); + ffx->IAmdExtFfxApi_iface.lpVtbl = &AMDFSR4FFX_vtable; + ffx->ref = 1; + *obj = &ffx->IAmdExtFfxApi_iface; + return S_OK; + } else if (IsEqualGUID(iid, &IID_IAmdExtAntiLagApi)) { + return ID3D12Device_QueryInterface((ID3D12Device *)outer, &IID_IAmdExtAntiLagApi, obj); + } else if(IsEqualGUID(iid, &IID_IAmdExtStub1)) { + struct AMDExtStub1 *this = calloc(1, sizeof(struct AMDExtStub1)); + this->IAmdExtStub1_iface.lpVtbl = &AMDSTUB1_vtable; + this->ref = 1; + *obj = &this->IAmdExtStub1_iface; + return S_OK; + } else { + FIXME("unknown guid: %s\n", debugstr_guid(iid)); + } + + return E_NOINTERFACE; +} + +HMODULE WINAPI AmdGetDxcModuleHandle(void) +{ + return GetModuleHandleA(NULL); +} From 73dd927b3d85076c7a2532fb52b4ee752ddbf9f2 Mon Sep 17 00:00:00 2001 From: Anna Lasky Date: Tue, 5 Aug 2025 10:11:26 -0500 Subject: [PATCH 392/454] ntdll: Remove LIBGL_ALWAYS_SOFTWARE overrides for Witcher 3. --- dlls/ntdll/unix/loader.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c index c958665ede2c..8b661c8ea06a 100644 --- a/dlls/ntdll/unix/loader.c +++ b/dlls/ntdll/unix/loader.c @@ -2254,11 +2254,6 @@ static void hacks_init(void) ERR("HACK: setting WINE_ENABLE_GST_LIVE_LATENCY.\n"); setenv("WINE_ENABLE_GST_LIVE_LATENCY", "1", 0); } - if (sgi && !strcmp(sgi, "292030")) - { - ERR("HACK: setting LIBGL_ALWAYS_SOFTWARE.\n"); - setenv("LIBGL_ALWAYS_SOFTWARE", "1", 0); - } if (sgi && !strcmp(sgi, "2379390")) { From ca59ecc33c4d7063a0413497729bcf6032ccef09 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 28 Aug 2025 15:26:57 -0600 Subject: [PATCH 393/454] ntdll: Zero aligned size in initialize_block(). CW-Bug-Id: #25868 --- dlls/kernel32/tests/heap.c | 60 ++++++++++++++++++++++++++++++++++++++ dlls/ntdll/heap.c | 8 +++-- 2 files changed, 65 insertions(+), 3 deletions(-) diff --git a/dlls/kernel32/tests/heap.c b/dlls/kernel32/tests/heap.c index c62eb4491924..3e13c9ef9d5c 100644 --- a/dlls/kernel32/tests/heap.c +++ b/dlls/kernel32/tests/heap.c @@ -82,6 +82,21 @@ static void load_functions(void) #undef LOAD_FUNC } +static BOOL check_win_version(int min_major, int min_minor) +{ + HMODULE hntdll = GetModuleHandleA("ntdll.dll"); + NTSTATUS (WINAPI *pRtlGetVersion)(RTL_OSVERSIONINFOEXW *); + RTL_OSVERSIONINFOEXW rtlver; + + rtlver.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOEXW); + pRtlGetVersion = (void *)GetProcAddress(hntdll, "RtlGetVersion"); + pRtlGetVersion(&rtlver); + return rtlver.dwMajorVersion > min_major || + (rtlver.dwMajorVersion == min_major && + rtlver.dwMinorVersion >= min_minor); +} +#define is_win8_plus() check_win_version(6, 2) + struct heap { UINT_PTR unknown1[2]; @@ -3456,6 +3471,49 @@ static void test_heap_layout( HANDLE handle, DWORD global_flag, DWORD heap_flags } } +static void test_heap_tail_zeroing( DWORD heap_flags ) +{ + static const ULONG_PTR large_block_min_size = 65536 * (2 * sizeof(void *)); + BOOL before_win8 = !is_win8_plus(); + HANDLE heap = GetProcessHeap(); + size_t size, size_aligned; + ULONG_PTR v, expected; + char *p1, *p2; + + if (heap_flags & HEAP_PAGE_ALLOCS) + { + /* This behaves differently, no support yet. */ + skip( "Skipping test with HEAP_PAGE_ALLOCS.\n" ); + return; + } + + for (size = 1; size <= 1048576 * 2; size *= 2) + { + winetest_push_context( "heap_flags %#lx, size %Iu", heap_flags, size ); + p1 = HeapAlloc( heap, 0, size + 1 ); + ok( !!p1, "got NULL.\n" ); + size_aligned = (size + 1 + sizeof(ULONG_PTR) - 1) & ~(sizeof(ULONG_PTR) - 1); + /* This and read access below is going to make valgrind or ASAN unhappy but the purpose of this test is + * to specifically check what happens with the tail bytes following the allocation. */ + if (!(heap_flags & (HEAP_VALIDATE_PARAMS | HEAP_VALIDATE_ALL))) + memset( p1, 0xcc, size_aligned ); + HeapFree( heap, 0, p1 ); + + /* We are not guarenteed to get the same pointer here but that often happens, especially when the test + * is run first, and spoling the data before that adds certainity to the results. */ + p2 = HeapAlloc( heap, HEAP_ZERO_MEMORY, size + 1 ); + ok( !!p2, "got NULL.\n" ); + v = 0; + memcpy( &v, p2 + size, size_aligned - size ); + expected = 0; + if (size_aligned - size > 1 && size + 1 < large_block_min_size && heap_flags & HEAP_TAIL_CHECKING_ENABLED) + memset( (char *)&expected + 1, 0xab, size_aligned - size - 1 ); + ok( v == expected || broken( before_win8 && !expected ), "got %#Ix, expected %#Ix.\n", v, expected ); + HeapFree( heap, 0, p2 ); + winetest_pop_context(); + } +} + static void test_child_heap( const char *arg ) { char buffer[32]; @@ -3535,6 +3593,7 @@ static void test_child_heap( const char *arg ) ok( ret, "HeapDestroy failed, error %lu\n", GetLastError() ); test_heap_checks( heap_flags ); + test_heap_tail_zeroing( heap_flags ); } static void test_GetPhysicallyInstalledSystemMemory(void) @@ -3760,6 +3819,7 @@ START_TEST(heap) test_GetPhysicallyInstalledSystemMemory(); test_GlobalMemoryStatus(); + test_heap_tail_zeroing( 0 ); if (pRtlGetNtGlobalFlags) { diff --git a/dlls/ntdll/heap.c b/dlls/ntdll/heap.c index 8c2a96aca1ad..0a401b4cfaf7 100644 --- a/dlls/ntdll/heap.c +++ b/dlls/ntdll/heap.c @@ -507,14 +507,16 @@ static inline void mark_block_tail( struct block *block, DWORD flags ) static inline void initialize_block( struct block *block, SIZE_T old_size, SIZE_T size, DWORD flags ) { char *data = (char *)(block + 1); - SIZE_T i; + SIZE_T i, aligned_size; if (size <= old_size) return; if (flags & HEAP_ZERO_MEMORY) { - valgrind_make_writable( data + old_size, size - old_size ); - memset( data + old_size, 0, size - old_size ); + aligned_size = ROUND_SIZE( size, sizeof(void *) - 1 ); + valgrind_make_writable( data + old_size, aligned_size - old_size ); + memset( data + old_size, 0, aligned_size - old_size ); + valgrind_make_noaccess( data + size, aligned_size - size ); } else if (flags & HEAP_FREE_CHECKING_ENABLED) { From b837e94733e44c7f49c3ab156adca811b24dcf13 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 1 Sep 2025 13:27:38 -0600 Subject: [PATCH 394/454] nsiproxy.sys: Use a separate critical section to sync icmp echo IRP cancel and listen. --- dlls/nsiproxy.sys/device.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/dlls/nsiproxy.sys/device.c b/dlls/nsiproxy.sys/device.c index b1c674ca5f34..3e806ca9835b 100644 --- a/dlls/nsiproxy.sys/device.c +++ b/dlls/nsiproxy.sys/device.c @@ -196,6 +196,8 @@ static inline icmp_handle irp_set_icmp_handle( IRP *irp, icmp_handle handle ) ULongToPtr( handle ) ) ); } +DECLARE_CRITICAL_SECTION( icmp_echo_completion_cs ); + static void WINAPI icmp_echo_cancel( DEVICE_OBJECT *device, IRP *irp ) { struct icmp_cancel_listen_params params; @@ -203,8 +205,7 @@ static void WINAPI icmp_echo_cancel( DEVICE_OBJECT *device, IRP *irp ) TRACE( "device %p, irp %p.\n", device, irp ); IoReleaseCancelSpinLock( irp->CancelIrql ); - - EnterCriticalSection( &nsiproxy_cs ); + EnterCriticalSection( &icmp_echo_completion_cs ); /* If the handle is not set, either the irp is still in the request queue, in which case the request thread will @@ -213,8 +214,7 @@ static void WINAPI icmp_echo_cancel( DEVICE_OBJECT *device, IRP *irp ) will be completed elsewhere. */ params.handle = irp_get_icmp_handle( irp ); if (params.handle) nsiproxy_call( icmp_cancel_listen, ¶ms ); - - LeaveCriticalSection( &nsiproxy_cs ); + LeaveCriticalSection( &icmp_echo_completion_cs ); } static int icmp_echo_reply_struct_len( ULONG family, ULONG bits ) @@ -247,8 +247,7 @@ static DWORD WINAPI listen_thread_proc( void *arg ) status = nsiproxy_call( icmp_listen, ¶ms ); TRACE( "icmp_listen rets %08lx\n", status ); - EnterCriticalSection( &nsiproxy_cs ); - + EnterCriticalSection( &icmp_echo_completion_cs ); close_params.handle = irp_set_icmp_handle( irp, 0 ); nsiproxy_call( icmp_close, &close_params ); @@ -257,10 +256,8 @@ static DWORD WINAPI listen_thread_proc( void *arg ) irp->IoStatus.Information = params.reply_len; else irp->IoStatus.Information = 0; + LeaveCriticalSection( &icmp_echo_completion_cs ); IoCompleteRequest( irp, IO_NO_INCREMENT ); - - LeaveCriticalSection( &nsiproxy_cs ); - return 0; } From 72aa869a51840a7afc73f20299f73ac7cf5d6849 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 1 Sep 2025 17:02:34 -0600 Subject: [PATCH 395/454] iphlpapi: Use IOCP callback for echo async completion. --- dlls/iphlpapi/iphlpapi_main.c | 108 ++++++++++++++++++++++----------- dlls/iphlpapi/tests/iphlpapi.c | 67 +++++++++++++++++++- 2 files changed, 139 insertions(+), 36 deletions(-) diff --git a/dlls/iphlpapi/iphlpapi_main.c b/dlls/iphlpapi/iphlpapi_main.c index 135e1dc4cddb..5f833a0f4892 100644 --- a/dlls/iphlpapi/iphlpapi_main.c +++ b/dlls/iphlpapi/iphlpapi_main.c @@ -4774,6 +4774,43 @@ struct icmp_handle_data HANDLE nsi_device; }; +struct icmp_apc_ctxt +{ + HANDLE event; + HANDLE thread; + void *apc_ctxt; + PIO_APC_ROUTINE apc_routine; + IO_STATUS_BLOCK iosb; +}; + +static void CALLBACK icmp_apc_routine( ULONG_PTR context ) +{ + struct icmp_apc_ctxt *ctx = (struct icmp_apc_ctxt *)context; + + ctx->apc_routine( ctx->apc_ctxt, &ctx->iosb, 0 ); + heap_free( ctx ); +} + +static void CALLBACK icmp_iocp_callback( DWORD error, DWORD count, OVERLAPPED *ovr ) +{ + struct icmp_apc_ctxt *ctx = (struct icmp_apc_ctxt *)ovr; + HANDLE thread; + BOOL ret; + + if (!ctx) return; + if (ctx->event) SetEvent( ctx->event ); + else if (ctx->apc_routine) + { + /* Don't access ctx after successful APC queue, it will be freed there. */ + thread = ctx->thread; + ctx->thread = NULL; + ret = QueueUserAPC( icmp_apc_routine, thread, (ULONG_PTR)ctx ); + CloseHandle( thread ); + if (ret) return; + } + heap_free( ctx ); +} + /*********************************************************************** * IcmpCloseHandle (IPHLPAPI.@) */ @@ -4811,7 +4848,13 @@ HANDLE WINAPI IcmpCreateFile( void ) heap_free( data ); return INVALID_HANDLE_VALUE; } - + if (!BindIoCompletionCallback( data->nsi_device, icmp_iocp_callback, 0 )) + { + ERR( "BindIoCompletionCallback failed.\n" ); + CloseHandle( data->nsi_device ); + heap_free( data ); + return INVALID_HANDLE_VALUE; + } return (HANDLE)data; } @@ -4850,33 +4893,17 @@ DWORD WINAPI IcmpSendEcho2( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc_rou opts, reply, reply_size, timeout ); } -struct icmp_apc_ctxt -{ - void *apc_ctxt; - PIO_APC_ROUTINE apc_routine; - IO_STATUS_BLOCK iosb; -}; - -void WINAPI icmp_apc_routine( void *context, IO_STATUS_BLOCK *iosb, ULONG reserved ) -{ - struct icmp_apc_ctxt *ctxt = context; - - ctxt->apc_routine( ctxt->apc_ctxt, iosb, reserved ); - heap_free( ctxt ); -} - static NTSTATUS icmp_send_echo( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc_routine, void *apc_ctxt, SOCKADDR_INET *src_addr, SOCKADDR_INET *dst_addr, void *request, WORD request_size, IP_OPTION_INFORMATION *opts, void *reply, DWORD reply_size, DWORD timeout ) { - static IO_STATUS_BLOCK iosb_placeholder; struct icmp_handle_data *data = (struct icmp_handle_data *)handle; struct icmp_apc_ctxt *ctxt = NULL; - IO_STATUS_BLOCK *iosb; + IO_STATUS_BLOCK *iosb, iosb_local; DWORD opt_size, in_size; struct nsiproxy_icmp_echo *in; - HANDLE request_event; + HANDLE request_event = NULL; NTSTATUS status; if (handle == INVALID_HANDLE_VALUE || !reply) return STATUS_INVALID_PARAMETER; @@ -4905,14 +4932,7 @@ static NTSTATUS icmp_send_echo( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc in->timeout = timeout; memcpy( in->data + opt_size, request, request_size ); - request_event = event ? event : (apc_routine ? NULL : CreateEventW( NULL, 0, 0, NULL )); - - if (event) - { - /* Async completion without calling APC routine, IOSB is not delivered anywhere. */ - iosb = &iosb_placeholder; - } - else + if (event || apc_routine) { if (!(ctxt = heap_alloc( sizeof(*ctxt) ))) { @@ -4922,19 +4942,37 @@ static NTSTATUS icmp_send_echo( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc iosb = &ctxt->iosb; ctxt->apc_routine = apc_routine; ctxt->apc_ctxt = apc_ctxt; + ctxt->event = event; + ctxt->thread = NULL; + if (!event) + { + if (!DuplicateHandle( GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), + &ctxt->thread, 0, FALSE, DUPLICATE_SAME_ACCESS )) + { + heap_free( ctxt ); + heap_free( in ); + return STATUS_NO_MEMORY; + } + } } - status = NtDeviceIoControlFile( data->nsi_device, request_event, apc_routine && !event ? icmp_apc_routine : NULL, - ctxt, iosb, IOCTL_NSIPROXY_WINE_ICMP_ECHO, + else + { + iosb = &iosb_local; + request_event = CreateEventW( NULL, 0, 0, NULL ); + } + + status = NtDeviceIoControlFile( data->nsi_device, request_event, NULL, ctxt, + iosb, IOCTL_NSIPROXY_WINE_ICMP_ECHO, in, in_size, reply, reply_size ); + if (!ctxt && status == STATUS_PENDING && !WaitForSingleObject( request_event, INFINITE )) + status = iosb->Status; - if (status == STATUS_PENDING) + if (request_event) CloseHandle( request_event ); + if (ctxt && status != STATUS_PENDING) { - if (!event && !apc_routine && !WaitForSingleObject( request_event, INFINITE )) - status = iosb->Status; + if (ctxt->thread) CloseHandle( ctxt->thread ); + heap_free( ctxt ); } - - if (!event && request_event) CloseHandle( request_event ); - if ((!apc_routine && !event) || status != STATUS_PENDING) heap_free( ctxt ); heap_free( in ); return status; } diff --git a/dlls/iphlpapi/tests/iphlpapi.c b/dlls/iphlpapi/tests/iphlpapi.c index 7aa73744813b..e8386f52822d 100644 --- a/dlls/iphlpapi/tests/iphlpapi.c +++ b/dlls/iphlpapi/tests/iphlpapi.c @@ -951,11 +951,37 @@ static IO_STATUS_BLOCK icmp_send_echo_io; static void WINAPI icmp_send_echo_test_apc(void *context, IO_STATUS_BLOCK *io_status, ULONG reserved) { ok_(__FILE__, icmp_send_echo_test_line)(icmp_send_echo_test_apc_expect, "Unexpected APC execution\n"); + ok_(__FILE__, icmp_send_echo_test_line)(!reserved, "Got reserved %#lx\n", reserved); ok_(__FILE__, icmp_send_echo_test_line)(context == (void*)0xdeadc0de, "Wrong context: %p\n", context); icmp_send_echo_test_apc_expect = FALSE; icmp_send_echo_io = *io_status; } +struct test_echo_thread_params +{ + HANDLE icmp; + HANDLE event; + PIO_APC_ROUTINE apc; + void *apc_context; + IPAddr address; + char *senddata; + DWORD send_data_size; + char *replydata; + DWORD replysz; + DWORD timeout; +}; + +static DWORD CALLBACK test_echo_thread(void *params) +{ + struct test_echo_thread_params *p = params; + BOOL ret; + + ret = IcmpSendEcho2(p->icmp, p->event, p->apc, p->apc_context, p->address, p->senddata, p->send_data_size, + NULL, p->replydata, p->replysz, p->timeout); + ok(!ret && GetLastError() == ERROR_IO_PENDING, "got ret %d, error %ld.\n", ret, GetLastError()); + return 0; +} + static void testIcmpSendEcho(void) { /* The APC's signature is different pre-Vista */ @@ -963,10 +989,11 @@ static void testIcmpSendEcho(void) char senddata[32], replydata[sizeof(senddata) + sizeof(ICMP_ECHO_REPLY)]; char replydata2[sizeof(replydata) + sizeof(IO_STATUS_BLOCK)]; DWORD ret, error, replysz = sizeof(replydata); + struct test_echo_thread_params p; IP_OPTION_INFORMATION opt; IPAddr address; ICMP_ECHO_REPLY *reply; - HANDLE event; + HANDLE event, thread; INT i; memset(senddata, 0, sizeof(senddata)); @@ -1283,6 +1310,37 @@ static void testIcmpSendEcho(void) ok(!memcmp(senddata, reply->Data, min(sizeof(senddata), reply->DataSize)), "Data mismatch\n"); /* asynchronous tests with event */ + + replysz = sizeof(replydata2); + ResetEvent(event); + p.icmp = icmp; + p.event = event; + p.apc = icmp_send_echo_test_apc; + p.apc_context = (void *)0xdeadbeef; + ret = inet_pton(AF_INET, "192.18.1.1", &address); + ok(ret, "got error %u.\n", WSAGetLastError()); + p.address = address; + p.senddata = senddata; + p.send_data_size = sizeof(senddata); + p.replydata = replydata2; + p.replysz = replysz; + p.timeout = 30; + memset(replydata2, 0xcc, sizeof(replydata2)); + reply = (ICMP_ECHO_REPLY*)replydata2; + thread = CreateThread(NULL, 0, test_echo_thread, &p, 0, NULL); + + ret = WaitForSingleObject(thread, INFINITE); + ok(ret == WAIT_OBJECT_0, "got %lu.\n", ret); + IcmpCloseHandle(icmp); /* completion is delivered even if icmp handle is closed */ + icmp = IcmpCreateFile(); + ok (icmp != INVALID_HANDLE_VALUE, "IcmpCreateFile failed unexpectedly with error %ld\n", GetLastError()); + + CloseHandle(thread); + ret = WaitForSingleObject(event, INFINITE); + ok(ret == WAIT_OBJECT_0, "got %lu.\n", ret); + ok(reply->Status == IP_REQ_TIMED_OUT, "Expect status: 0x%08x, got: 0x%08lx\n", IP_REQ_TIMED_OUT, reply->Status); + ok(!reply->DataSize, "got size %d.\n", reply->DataSize); + SetLastError(0xdeadbeef); replysz = sizeof(replydata2); address = htonl(INADDR_LOOPBACK); @@ -1297,6 +1355,9 @@ static void testIcmpSendEcho(void) { ok(!ret, "IcmpSendEcho2 returned success unexpectedly\n"); ok(error == ERROR_IO_PENDING, "Expect last error: 0x%08x, got: 0x%08lx\n", ERROR_IO_PENDING, error); + IcmpCloseHandle(icmp); /* completion is delivered even if icmp handle is closed */ + icmp = IcmpCreateFile(); + ok (icmp != INVALID_HANDLE_VALUE, "IcmpCreateFile failed unexpectedly with error %ld\n", GetLastError()); ret = WaitForSingleObjectEx(event, 2000, TRUE); ok(ret == WAIT_OBJECT_0, "WaitForSingleObjectEx failed unexpectedly with %lu\n", ret); reply = (ICMP_ECHO_REPLY*)replydata2; @@ -1365,6 +1426,10 @@ static void testIcmpSendEcho(void) error = GetLastError(); ok(!ret, "IcmpSendEcho2 returned success unexpectedly\n"); ok(error == ERROR_IO_PENDING, "Expect last error: 0x%08x, got: 0x%08lx\n", ERROR_IO_PENDING, error); + IcmpCloseHandle(icmp); + icmp = IcmpCreateFile(); + ok (icmp != INVALID_HANDLE_VALUE, "IcmpCreateFile failed unexpectedly with error %ld\n", GetLastError()); + SleepEx(200, TRUE); SleepEx(0, TRUE); ok(icmp_send_echo_test_apc_expect == FALSE, "APC was not executed!\n"); From 639d8b198f87dc6db11293c34c49862e59ac201a Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 1 Sep 2025 18:40:43 -0600 Subject: [PATCH 396/454] amend! iphlpapi: Use IOCP callback for echo async completion. iphlpapi: Use IOCP callback for echo async completion. CW-Bug-Id: #25877 From 136d477a8f168ee0f6fff97c9497f40ff4a48e99 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 1 Sep 2025 18:40:43 -0600 Subject: [PATCH 397/454] amend! nsiproxy.sys: Use a separate critical section to sync icmp echo IRP cancel and listen. nsiproxy.sys: Use a separate critical section to sync icmp echo IRP cancel and listen. CW-Bug-Id: #25877 From 43d4f45bbe07438659a2fc052744ac506a8076e4 Mon Sep 17 00:00:00 2001 From: Billy Laws Date: Thu, 24 Jul 2025 18:57:39 +0100 Subject: [PATCH 398/454] winevulkan: Add HANDLE and LPCWSTR as pointer-size types. Fixes wow64 struct conversion for memory import/export. --- dlls/winevulkan/make_vulkan | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan index ca690079130b..96fc524560be 100755 --- a/dlls/winevulkan/make_vulkan +++ b/dlls/winevulkan/make_vulkan @@ -1371,7 +1371,7 @@ class VkVariable(object): return self.pointer and self.pointer.count('*') > 1 def is_pointer_size(self): - if self.type in ["size_t", "HWND", "HINSTANCE"] or self.type.startswith("PFN"): + if self.type in ["size_t", "HWND", "HINSTANCE", "HANDLE", "LPCWSTR"] or self.type.startswith("PFN"): return True if self.is_handle() and self.handle.is_dispatchable(): return True From a76dfef60ab604ecb5c68932a7f3da15e3295a76 Mon Sep 17 00:00:00 2001 From: Billy Laws Date: Thu, 24 Jul 2025 18:58:59 +0100 Subject: [PATCH 399/454] fixup! winevulkan: Implement support for KMT handles and named objects. --- dlls/winevulkan/vulkan.c | 47 ++++++++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 19 deletions(-) diff --git a/dlls/winevulkan/vulkan.c b/dlls/winevulkan/vulkan.c index 51b59bfabb77..866b4ac65f46 100644 --- a/dlls/winevulkan/vulkan.c +++ b/dlls/winevulkan/vulkan.c @@ -2179,6 +2179,27 @@ struct shared_resource_create WCHAR name[1]; }; +/* helper for internal ioctl calls */ +typedef struct +{ + union + { + NTSTATUS Status; + ULONG Pointer; + }; + ULONG Information; +} IO_STATUS_BLOCK32; + +static NTSTATUS wine_ioctl(HANDLE file, ULONG code, void *in_buffer, ULONG in_size, void *out_buffer, ULONG out_size) +{ + IO_STATUS_BLOCK32 io32; + IO_STATUS_BLOCK io; + + /* the 32-bit iosb is filled for overlapped file handles */ + io.Pointer = &io32; + return NtDeviceIoControlFile(file, NULL, NULL, NULL, &io, code, in_buffer, in_size, out_buffer, out_size); +} + static HANDLE create_gpu_resource(int fd, LPCWSTR name, UINT64 resource_size) { static const WCHAR shared_gpu_resourceW[] = {'\\','?','?','\\','S','h','a','r','e','d','G','p','u','R','e','s','o','u','r','c','e',0}; @@ -2219,8 +2240,7 @@ static HANDLE create_gpu_resource(int fd, LPCWSTR name, UINT64 resource_size) if (name) lstrcpyW(&inbuff->name[0], name); - if ((status = NtDeviceIoControlFile(shared_resource, NULL, NULL, NULL, &iosb, IOCTL_SHARED_GPU_RESOURCE_CREATE, - inbuff, in_size, NULL, 0))) + if ((status = wine_ioctl(shared_resource, IOCTL_SHARED_GPU_RESOURCE_CREATE, inbuff, in_size, NULL, 0))) free(inbuff); NtClose(unix_resource); @@ -2280,8 +2300,7 @@ static HANDLE open_shared_resource(HANDLE kmt_handle, LPCWSTR name) if (name) lstrcpyW(&inbuff->name[0], name); - status = NtDeviceIoControlFile(shared_resource, NULL, NULL, NULL, &iosb, IOCTL_SHARED_GPU_RESOURCE_OPEN, - inbuff, in_size, NULL, 0); + status = wine_ioctl(shared_resource, IOCTL_SHARED_GPU_RESOURCE_OPEN, inbuff, in_size, NULL, 0); free(inbuff); @@ -2299,11 +2318,9 @@ static HANDLE open_shared_resource(HANDLE kmt_handle, LPCWSTR name) static BOOL shared_resource_get_info(HANDLE handle, struct shared_resource_info *info) { - IO_STATUS_BLOCK iosb; unsigned int status; - status = NtDeviceIoControlFile(handle, NULL, NULL, NULL, &iosb, IOCTL_SHARED_GPU_RESOURCE_GET_INFO, - NULL, 0, info, sizeof(*info)); + status = wine_ioctl(handle, IOCTL_SHARED_GPU_RESOURCE_GET_INFO, NULL, 0, info, sizeof(*info)); if (status) ERR("Failed to get shared resource info, status %#x.\n", status); @@ -2314,13 +2331,11 @@ static BOOL shared_resource_get_info(HANDLE handle, struct shared_resource_info static int get_shared_resource_fd(HANDLE shared_resource) { - IO_STATUS_BLOCK iosb; obj_handle_t unix_resource; NTSTATUS status; int ret; - if (NtDeviceIoControlFile(shared_resource, NULL, NULL, NULL, &iosb, IOCTL_SHARED_GPU_RESOURCE_GET_UNIX_RESOURCE, - NULL, 0, &unix_resource, sizeof(unix_resource))) + if (wine_ioctl(shared_resource, IOCTL_SHARED_GPU_RESOURCE_GET_UNIX_RESOURCE, NULL, 0, &unix_resource, sizeof(unix_resource))) return -1; status = wine_server_handle_to_fd(wine_server_ptr_handle(unix_resource), FILE_READ_DATA, &ret, NULL); @@ -2332,11 +2347,9 @@ static int get_shared_resource_fd(HANDLE shared_resource) static HANDLE get_shared_resource_kmt_handle(HANDLE shared_resource) { - IO_STATUS_BLOCK iosb; obj_handle_t kmt_handle; - if (NtDeviceIoControlFile(shared_resource, NULL, NULL, NULL, &iosb, IOCTL_SHARED_GPU_RESOURCE_GETKMT, - NULL, 0, &kmt_handle, sizeof(kmt_handle))) + if (wine_ioctl(shared_resource, IOCTL_SHARED_GPU_RESOURCE_GETKMT, NULL, 0, &kmt_handle, sizeof(kmt_handle))) return INVALID_HANDLE_VALUE; return wine_server_ptr_handle(kmt_handle); @@ -3383,7 +3396,6 @@ VkResult wine_vkGetMemoryWin32HandlePropertiesKHR(VkDevice device_handle, VkExte static bool set_shared_resource_object(HANDLE shared_resource, unsigned int index, HANDLE handle) { - IO_STATUS_BLOCK iosb; struct shared_resource_set_object { unsigned int index; @@ -3393,19 +3405,16 @@ static bool set_shared_resource_object(HANDLE shared_resource, unsigned int inde params.index = index; params.handle = wine_server_obj_handle(handle); - return NtDeviceIoControlFile(shared_resource, NULL, NULL, NULL, &iosb, IOCTL_SHARED_GPU_RESOURCE_SET_OBJECT, - ¶ms, sizeof(params), NULL, 0) == STATUS_SUCCESS; + return wine_ioctl(shared_resource, IOCTL_SHARED_GPU_RESOURCE_SET_OBJECT, ¶ms, sizeof(params), NULL, 0) == STATUS_SUCCESS; } #define IOCTL_SHARED_GPU_RESOURCE_GET_OBJECT CTL_CODE(FILE_DEVICE_VIDEO, 6, METHOD_BUFFERED, FILE_READ_ACCESS) static HANDLE get_shared_resource_object(HANDLE shared_resource, unsigned int index) { - IO_STATUS_BLOCK iosb; obj_handle_t handle; - if (NtDeviceIoControlFile(shared_resource, NULL, NULL, NULL, &iosb, IOCTL_SHARED_GPU_RESOURCE_GET_OBJECT, - &index, sizeof(index), &handle, sizeof(handle))) + if (wine_ioctl(shared_resource, IOCTL_SHARED_GPU_RESOURCE_GET_OBJECT, &index, sizeof(index), &handle, sizeof(handle))) return NULL; return wine_server_ptr_handle(handle); From e502960431e9e029419707040688e757a87ece08 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 2 Sep 2025 18:04:27 -0600 Subject: [PATCH 400/454] winevulkan: Handle NULL buffer pointer in vkFreeCommandBuffers(). CW-Bug-Id: #25887 --- dlls/winevulkan/loader.c | 2 ++ dlls/winevulkan/vulkan.c | 6 ++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/dlls/winevulkan/loader.c b/dlls/winevulkan/loader.c index 644e0982e7a0..79bbe8a27582 100644 --- a/dlls/winevulkan/loader.c +++ b/dlls/winevulkan/loader.c @@ -697,6 +697,8 @@ void WINAPI vkFreeCommandBuffers(VkDevice device, VkCommandPool cmd_pool, uint32 assert(!status); for (i = 0; i < count; i++) { + if (!buffers[i]) + continue; list_remove(&buffers[i]->pool_link); free(buffers[i]); } diff --git a/dlls/winevulkan/vulkan.c b/dlls/winevulkan/vulkan.c index 866b4ac65f46..dcf0d17a9ce7 100644 --- a/dlls/winevulkan/vulkan.c +++ b/dlls/winevulkan/vulkan.c @@ -606,12 +606,14 @@ static void wine_vk_free_command_buffers(struct vulkan_device *device, struct wine_cmd_pool *pool, uint32_t count, const VkCommandBuffer *buffers) { struct vulkan_instance *instance = device->physical_device->instance; + struct wine_cmd_buffer *buffer; unsigned int i; for (i = 0; i < count; i++) { - struct wine_cmd_buffer *buffer = wine_cmd_buffer_from_handle(buffers[i]); - + if (!buffers[i]) + continue; + buffer = wine_cmd_buffer_from_handle(buffers[i]); if (!buffer) continue; From c685935b3ff4436f9f7103c493198ffd88e69cf5 Mon Sep 17 00:00:00 2001 From: Santino Mazza Date: Wed, 23 Jul 2025 10:41:15 -0300 Subject: [PATCH 401/454] fixup! winex11: Add support for absolute position in RawMotion events. Sometimes when there is X and Y motion, the x11 driver would accumulate motion in the Y axis more than it should, causing random movements on the Y direction. Cw-Bug-Id: #25505 --- dlls/winex11.drv/mouse.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/dlls/winex11.drv/mouse.c b/dlls/winex11.drv/mouse.c index 902776c49726..b84bc16c01da 100644 --- a/dlls/winex11.drv/mouse.c +++ b/dlls/winex11.drv/mouse.c @@ -1807,13 +1807,18 @@ static BOOL map_raw_event_coords( XIRawEvent *event, INPUT *input, BOOL send_raw TRACE( "event %f,%f raw value %f,%f, raw input %d,%d\n", x_value, y_value, x_raw, y_raw, (int)input->mi.dx, (int)input->mi.dy ); } - else if (!(input->mi.dx = round( x->value )) && !(input->mi.dy = round( y->value ))) - { - TRACE( "event %f,%f value %f,%f, accumulating motion\n", x_value, y_value, x->value, y->value ); - input->mi.dwFlags &= ~MOUSEEVENTF_MOVE; - } else { + input->mi.dx = round( x->value ); + input->mi.dy = round( y->value ); + + if (!input->mi.dx && !input->mi.dy) + { + TRACE( "event %f,%f value %f,%f, accumulating motion\n", x_value, y_value, x->value, y->value ); + input->mi.dwFlags &= ~MOUSEEVENTF_MOVE; + return TRUE; + } + TRACE( "event %f,%f value %f,%f, input %d,%d\n", x_value, y_value, x->value, y->value, (int)input->mi.dx, (int)input->mi.dy ); x->value -= input->mi.dx; From f11eff98a546969d8963598e1e7430a6c66c2f36 Mon Sep 17 00:00:00 2001 From: Santino Mazza Date: Fri, 29 Aug 2025 12:48:34 -0300 Subject: [PATCH 402/454] dinput: Set per monitor aware DPI awareness in the worker thread. (cherry picked from commit e7be1068ef462450005a9d03b0720903d0473190) Cw-Bug-Id: #25505 --- dlls/dinput/dinput_main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/dlls/dinput/dinput_main.c b/dlls/dinput/dinput_main.c index d7c1d4729d5e..62365f0b3231 100644 --- a/dlls/dinput/dinput_main.c +++ b/dlls/dinput/dinput_main.c @@ -375,6 +375,7 @@ static DWORD WINAPI dinput_thread_proc( void *params ) MSG msg; SetThreadDescription( GetCurrentThread(), L"wine_dinput_worker" ); + SetThreadDpiAwarenessContext( DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE ); di_em_win = CreateWindowW( L"DIEmWin", L"DIEmWin", 0, 0, 0, 0, 0, HWND_MESSAGE, 0, DINPUT_instance, NULL ); input_thread_state = &state; From 0a09c41578b2cfb0a94e5d8de56bdec2bce222e1 Mon Sep 17 00:00:00 2001 From: Santino Mazza Date: Wed, 30 Jul 2025 00:16:59 -0300 Subject: [PATCH 403/454] win32u: Map raw coordinates to virtual screen in low-level hooks. (cherry picked from commit 6643fbd21d1cd2cb93e3c21e482dbebb306cc8f5) Cw-Bug-Id: #25505 --- dlls/win32u/message.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/dlls/win32u/message.c b/dlls/win32u/message.c index 9331e928aa22..480e41a8c28d 100644 --- a/dlls/win32u/message.c +++ b/dlls/win32u/message.c @@ -2969,8 +2969,13 @@ int peek_message( MSG *msg, const struct peek_message_filter *filter ) } else if (info.msg.message == WH_MOUSE_LL && size >= sizeof(msg_data->hardware)) { + RECT rect = {info.msg.pt.x, info.msg.pt.y, info.msg.pt.x, info.msg.pt.y}; MSLLHOOKSTRUCT hook; + rect = map_rect_raw_to_virt( rect, 0 ); + info.msg.pt.x = rect.left; + info.msg.pt.y = rect.top; + hook.pt = info.msg.pt; hook.mouseData = info.msg.lParam; hook.flags = msg_data->hardware.flags; From f555ddab26b5f903128068189249f004dc3db974 Mon Sep 17 00:00:00 2001 From: Esme Povirk Date: Tue, 19 Aug 2025 19:46:53 +0000 Subject: [PATCH 404/454] mscoree: Update Wine Mono to 10.2.0. (cherry picked from commit 288a40d05c8cddf62d0b12524a90d2d4f5ce114d) --- dlls/appwiz.cpl/addons.c | 4 ++-- dlls/mscoree/mscoree_private.h | 2 +- tools/gitlab/test.yml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/dlls/appwiz.cpl/addons.c b/dlls/appwiz.cpl/addons.c index e55f0dda47c9..d12e9182d0c0 100644 --- a/dlls/appwiz.cpl/addons.c +++ b/dlls/appwiz.cpl/addons.c @@ -56,10 +56,10 @@ WINE_DEFAULT_DEBUG_CHANNEL(appwizcpl); #define GECKO_SHA "???" #endif -#define MONO_VERSION "10.1.0" +#define MONO_VERSION "10.2.0" #if defined(__i386__) || defined(__x86_64__) #define MONO_ARCH "x86" -#define MONO_SHA "c88c2431890bc32cacec8d7ea70e53a5ae4b95c8c55ca6e75ef8db0e4ccf1070" +#define MONO_SHA "4e1ed3f02e92d053133d03ddfbefcf6db4a4dc231a9aed3367b17117a88847d8" #else #define MONO_ARCH "" #define MONO_SHA "???" diff --git a/dlls/mscoree/mscoree_private.h b/dlls/mscoree/mscoree_private.h index 7044cb968d9c..aa542c317b3d 100644 --- a/dlls/mscoree/mscoree_private.h +++ b/dlls/mscoree/mscoree_private.h @@ -45,7 +45,7 @@ extern HRESULT assembly_get_runtime_version(ASSEMBLY *assembly, LPSTR *version); extern HRESULT assembly_get_vtable_fixups(ASSEMBLY *assembly, VTableFixup **fixups, DWORD *count); extern HRESULT assembly_get_native_entrypoint(ASSEMBLY *assembly, NativeEntryPointFunc *func); -#define WINE_MONO_VERSION "10.1.0" +#define WINE_MONO_VERSION "10.2.0" /* Mono embedding */ typedef struct _MonoDomain MonoDomain; diff --git a/tools/gitlab/test.yml b/tools/gitlab/test.yml index c3c15a3b2b56..787cfee53416 100644 --- a/tools/gitlab/test.yml +++ b/tools/gitlab/test.yml @@ -7,7 +7,7 @@ variables: GIT_STRATEGY: none GECKO_VER: 2.47.4 - MONO_VER: 10.1.0 + MONO_VER: 10.2.0 cache: - key: wine-gecko-$GECKO_VER paths: From 549ae9361120a190efdf5fddcc5fffdc6e1c5463 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 3 Sep 2025 14:59:30 -0600 Subject: [PATCH 405/454] win32u: Bump AMD internal driver version. CW-Bug-Id: #25893 --- dlls/win32u/sysparams.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/win32u/sysparams.c b/dlls/win32u/sysparams.c index a8ad5d338c54..17102f4d8a67 100644 --- a/dlls/win32u/sysparams.c +++ b/dlls/win32u/sysparams.c @@ -1095,7 +1095,7 @@ static const char* driver_vendor_to_version( UINT16 vendor ) switch (vendor) { case 0x8086: /* Intel */ return "32.0.101.6314"; - case 0x1002: /* AMD */ return "31.0.21921.1000"; + case 0x1002: /* AMD */ return "32.0.21025.1024"; case 0x10de: /* Nvidia */ return "32.0.19.9999"; default: return "31.0.10.1000"; } From 07b082e29003417b556e5e9d92a00a631c7247e8 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 3 Sep 2025 15:07:03 -0600 Subject: [PATCH 406/454] wbemprox: Bump AMD internal driver version. (to be dropped on next rebase, that is queried in wbemprox now). CW-Bug-Id: #25893 --- dlls/wbemprox/builtin.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/wbemprox/builtin.c b/dlls/wbemprox/builtin.c index 024d2cc4cb0e..2381d73766f1 100644 --- a/dlls/wbemprox/builtin.c +++ b/dlls/wbemprox/builtin.c @@ -4384,7 +4384,7 @@ static enum fill_status fill_videocontroller( struct table *table, const struct rec->description = wcsdup( name ); rec->device_id = L"VideoController1"; rec->driverdate = L"20250831000000.000000-000"; - rec->driverversion = L"31.0.21902.5"; + rec->driverversion = L"32.0.21025.1024"; rec->installeddriver = get_videocontroller_installeddriver( desc.VendorId ); rec->name = wcsdup( name ); rec->pnpdevice_id = get_videocontroller_pnpdeviceid( &desc ); From 01459dbe4d17b6674e68000bb398a9665dfe1397 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 4 Sep 2025 18:24:12 -0600 Subject: [PATCH 407/454] wbemprox: Fix CIM_UINT16 handling in to_safearray(). CW-Bug-Id: #25896 --- dlls/wbemprox/query.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/dlls/wbemprox/query.c b/dlls/wbemprox/query.c index ff7be29ed521..9e080f9a03d4 100644 --- a/dlls/wbemprox/query.c +++ b/dlls/wbemprox/query.c @@ -1084,10 +1084,20 @@ SAFEARRAY *to_safearray( const struct array *array, CIMTYPE basetype ) } SysFreeString( str ); } - else if (SafeArrayPutElement( ret, &i, ptr ) != S_OK) + else { - SafeArrayDestroy( ret ); - return NULL; + UINT32 v; + + if (vartype == VT_I4 && basetype == CIM_UINT16) + { + v = *(UINT16 *)ptr; + ptr = &v; + } + if (SafeArrayPutElement( ret, &i, ptr ) != S_OK) + { + SafeArrayDestroy( ret ); + return NULL; + } } } return ret; From f636e7122ad6e3aacc4e8350d5bce53ff9259846 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 4 Sep 2025 14:16:15 -0600 Subject: [PATCH 408/454] wbemprox: Add MSFT_PhysicalDisk table. CW-Bug-Id: #25896 --- dlls/wbemprox/builtin.c | 169 +++++++++++++++++++++++++++++++++++- dlls/wbemprox/tests/query.c | 77 +++++++++++++++- 2 files changed, 242 insertions(+), 4 deletions(-) diff --git a/dlls/wbemprox/builtin.c b/dlls/wbemprox/builtin.c index 2381d73766f1..7ee2bdf01b5f 100644 --- a/dlls/wbemprox/builtin.c +++ b/dlls/wbemprox/builtin.c @@ -204,6 +204,43 @@ static const struct column col_logicaldisktopartition[] = { L"Antecedent", CIM_REFERENCE|COL_FLAG_DYNAMIC|COL_FLAG_KEY }, { L"Dependent", CIM_REFERENCE|COL_FLAG_DYNAMIC|COL_FLAG_KEY }, }; +static const struct column col_msft_phys_disk[] = +{ + { L"AdapterSerialNumber", CIM_STRING }, + { L"AllocatedSize", CIM_UINT64 }, + { L"BusType", CIM_UINT16 }, + { L"CannotPoolReason", CIM_UINT16|CIM_FLAG_ARRAY }, + { L"CanPool", CIM_BOOLEAN }, + { L"Description", CIM_STRING }, + { L"DeviceId", CIM_STRING|COL_FLAG_DYNAMIC|COL_FLAG_KEY }, + { L"EnclosureNumber", CIM_UINT16 }, + { L"FirmwareVersion", CIM_STRING }, + { L"FriendlyName", CIM_STRING }, + { L"FruId", CIM_STRING }, + { L"HealthStatus", CIM_UINT16 }, + { L"IsIndicationEnabled", CIM_BOOLEAN }, + { L"IsPartial", CIM_BOOLEAN }, + { L"LogicalSectorSize", CIM_UINT64 }, + { L"Manufacturer", CIM_STRING }, + { L"MediaType", CIM_UINT16 }, + { L"Model", CIM_STRING }, + { L"OperationalDetails", CIM_STRING|CIM_FLAG_ARRAY }, + { L"OperationalStatus", CIM_UINT16|CIM_FLAG_ARRAY|COL_FLAG_DYNAMIC }, + { L"OtherCannotPoolReasonDescription", CIM_STRING }, + { L"PartNumber", CIM_STRING }, + { L"PhysicalLocation", CIM_STRING }, + { L"PhysicalSectorSize", CIM_UINT64 }, + { L"SerialNumber", CIM_STRING|COL_FLAG_DYNAMIC }, + { L"Size", CIM_UINT64 }, + { L"SlotNumber", CIM_UINT16 }, + { L"SoftwareVersion", CIM_STRING }, + { L"SpindleSpeed", CIM_UINT32 }, + { L"SupportedUsages", CIM_UINT16|CIM_FLAG_ARRAY|COL_FLAG_DYNAMIC }, + { L"UniqueId", CIM_STRING|COL_FLAG_DYNAMIC }, + { L"UniqueIdFormat", CIM_UINT16 }, + { L"Usage", CIM_UINT16 }, + { L"VirtualDiskFootprint", CIM_UINT64 }, +}; static const struct column col_networkadapter[] = { { L"AdapterType", CIM_STRING }, @@ -709,6 +746,43 @@ struct record_logicaldisktopartition const WCHAR *antecedent; const WCHAR *dependent; }; +struct record_msft_phys_disk +{ + const WCHAR *adapter_serial_number; + UINT64 allocated_size; + UINT16 bus_type; + struct array *cannot_pool_reason; + int can_pool; + const WCHAR *description; + const WCHAR *device_id; + UINT16 enclosure_number; + const WCHAR *firmware_version; + const WCHAR *friendly_name; + const WCHAR *fru_id; + UINT16 health_status; + int is_indication_enabled; + int is_partial; + UINT64 logical_sector_size; + const WCHAR *manufacturer; + UINT16 media_type; + const WCHAR *model; + struct array *operational_details; + struct array *operational_status; + const WCHAR *other_cannot_pool_reason_description; + const WCHAR *part_number; + const WCHAR *physical_location; + UINT64 physical_sector_size; + const WCHAR *serial_number; + UINT64 size; + UINT16 slot_number; + const WCHAR *software_version; + UINT32 spindle_speed; + struct array *supported_usages; + const WCHAR *unique_id; + UINT16 unique_id_format; + UINT16 usage; + UINT64 virtual_disk_footprint; +}; struct record_networkadapter { const WCHAR *adaptertype; @@ -4548,6 +4622,99 @@ static struct table wmi_builtin_classes[] = { { L"MSSMBios_RawSMBiosTables", C(col_rawsmbiostables), D(data_rawsmbiostables) }, }; + +static enum fill_status fill_msft_phys_disk( struct table *table, const struct expr *cond ) +{ + static UINT16 operational_status[] = { 2 }; + static struct array operational_status_array = + { + .elem_size = sizeof(*operational_status), + .count = ARRAY_SIZE(operational_status), + .ptr = &operational_status, + }; + static UINT16 supported_usages[] = { 1, 2, 3, 4, 5 }; + static struct array supported_usages_array = + { + .elem_size = sizeof(*supported_usages), + .count = ARRAY_SIZE(supported_usages), + .ptr = supported_usages, + }; + WCHAR device_id[10], root[] = L"A:\\"; + struct record_msft_phys_disk *rec; + UINT i, row = 0, offset = 0, index = 0, type; + UINT64 size; + DWORD drives = GetLogicalDrives(); + enum fill_status status = FILL_STATUS_UNFILTERED; + + if (!resize_table( table, 2, sizeof(*rec) )) return FILL_STATUS_FAILED; + + for (i = 0; i < 26; i++) + { + if (drives & (1 << i)) + { + root[0] = 'A' + i; + type = GetDriveTypeW( root ); + if (type != DRIVE_FIXED && type != DRIVE_REMOVABLE) continue; + + if (!resize_table( table, row + 1, sizeof(*rec) )) return FILL_STATUS_FAILED; + + get_freespace( root, &size ); + rec = (struct record_msft_phys_disk *)(table->data + offset); + rec->adapter_serial_number = NULL; + rec->allocated_size = size; + rec->bus_type = type == DRIVE_FIXED ? 17 /* NVME */: 1 /* USB */; + rec->cannot_pool_reason = NULL; + rec->can_pool = -1; + rec->description = NULL; + swprintf( device_id, ARRAY_SIZE( device_id ), L"%d", index ); + rec->device_id = wcsdup( device_id ); + rec->enclosure_number = 0; + rec->firmware_version = L"1234"; + rec->friendly_name = L"Wine disk"; + rec->fru_id = NULL; + rec->health_status = 0; /* Healthy */ + rec->is_indication_enabled = 0; + rec->is_partial = 0; + rec->logical_sector_size = 512; + rec->manufacturer = NULL; + rec->media_type = 4; /* SSD */ + rec->model = wcsdup( L"Wine disk" ); + rec->operational_details = NULL; + rec->operational_status = dup_array( &operational_status_array ); + rec->other_cannot_pool_reason_description = NULL; + rec->part_number = NULL; + rec->physical_location = L"Integrated : Bus 0 : Device 0 : Function 0 : Adapter 0 : Port 0"; + rec->physical_sector_size = 4096; + rec->serial_number = get_diskdrive_serialnumber( root[0] ); + rec->size = size; + rec->slot_number = 0; + rec->software_version = NULL; + rec->spindle_speed = 0; + rec->supported_usages = dup_array( &supported_usages_array ); + rec->unique_id = wcsdup( rec->serial_number ); + rec->unique_id_format = 0; /* Vendor specific */ + rec->usage = 1; /* Auto select */ + rec->virtual_disk_footprint = 0; + ++index; + if (!match_row( table, row, cond, &status )) + { + free_row_values( table, row ); + continue; + } + offset += sizeof(*rec); + row++; + } + } + TRACE("created %u rows\n", row); + table->num_rows = row; + return status; +} + +static struct table win_storage_builtin_classes[] = +{ + { L"MSFT_PhysicalDisk", C(col_msft_phys_disk), 0, 0, NULL, fill_msft_phys_disk }, +}; + #undef C #undef D @@ -4560,7 +4727,7 @@ static const struct builtin_namespaces[WBEMPROX_NAMESPACE_LAST] = { {L"cimv2", cimv2_builtin_classes, ARRAY_SIZE(cimv2_builtin_classes)}, - {L"Microsoft\\Windows\\Storage", NULL, 0}, + {L"Microsoft\\Windows\\Storage", win_storage_builtin_classes, ARRAY_SIZE(win_storage_builtin_classes)}, {L"StandardCimv2", NULL, 0}, {L"wmi", wmi_builtin_classes, ARRAY_SIZE(wmi_builtin_classes)}, }; diff --git a/dlls/wbemprox/tests/query.c b/dlls/wbemprox/tests/query.c index 7b6f3c35da58..f5c387a9a3f0 100644 --- a/dlls/wbemprox/tests/query.c +++ b/dlls/wbemprox/tests/query.c @@ -304,7 +304,8 @@ static void test_IEnumWbemClassObject_Next( IWbemServices *services ) SysFreeString( wql ); } -static void _check_property( ULONG line, IWbemClassObject *obj, const WCHAR *prop, VARTYPE vartype, CIMTYPE cimtype ) +static void _check_property( ULONG line, IWbemClassObject *obj, const WCHAR *prop, VARTYPE vartype, CIMTYPE cimtype, + BOOL nullable) { CIMTYPE type = 0xdeadbeef; VARIANT val; @@ -313,7 +314,8 @@ static void _check_property( ULONG line, IWbemClassObject *obj, const WCHAR *pro VariantInit( &val ); hr = IWbemClassObject_Get( obj, prop, 0, &val, &type, NULL ); ok( hr == S_OK, "%lu: failed to get description %#lx\n", line, hr ); - ok( V_VT( &val ) == vartype, "%lu: unexpected variant type 0x%x\n", line, V_VT(&val) ); + ok( V_VT( &val ) == vartype || (nullable && V_VT( &val ) == VT_NULL), "%lu: unexpected variant type 0x%x\n", + line, V_VT(&val) ); ok( type == cimtype, "%lu: unexpected type %#lx\n", line, type ); switch (V_VT(&val)) { @@ -337,7 +339,8 @@ static void _check_property( ULONG line, IWbemClassObject *obj, const WCHAR *pro } VariantClear( &val ); } -#define check_property(a,b,c,d) _check_property(__LINE__,a,b,c,d) +#define check_property(a,b,c,d) _check_property(__LINE__,a,b,c,d,FALSE) +#define check_property_nullable(a,b,c,d) _check_property(__LINE__,a,b,c,d,TRUE) static void test_Win32_Service( IWbemServices *services ) { @@ -2455,6 +2458,73 @@ static void test_MSSMBios_RawSMBiosTables( IWbemLocator *locator ) SysFreeString( bios ); } +static void test_MSFT_PhysicalDisk( IWbemLocator *locator ) +{ + BSTR path = SysAllocString( L"ROOT\\Microsoft\\Windows\\Storage" ); + BSTR query = SysAllocString( L"SELECT * FROM MSFT_PhysicalDisk" ); + BSTR wql = SysAllocString( L"wql" ); + IEnumWbemClassObject *result; + IWbemServices *services; + IWbemClassObject *obj; + ULONG count; + HRESULT hr; + + hr = IWbemLocator_ConnectServer( locator, path, NULL, NULL, NULL, 0, NULL, NULL, &services ); + ok( hr == S_OK, "failed to get IWbemServices interface %#lx\n", hr ); + + hr = IWbemServices_ExecQuery( services, wql, query, 0, NULL, &result ); + ok( hr == S_OK, "got %#lx\n", hr ); + + for (;;) + { + hr = IEnumWbemClassObject_Next( result, 10000, 1, &obj, &count ); + if (hr != S_OK) break; + + /* Properties not checked with 'if (0)' are absent on older Windows. */ + if (0) check_property_nullable( obj, L"AdapterSerialNumber", VT_BSTR, CIM_STRING ); + check_property( obj, L"AllocatedSize", VT_BSTR, CIM_UINT64 ); + check_property( obj, L"BusType", VT_I4, CIM_UINT16 ); + check_property_nullable( obj, L"CannotPoolReason", VT_ARRAY | VT_I4, CIM_FLAG_ARRAY | CIM_UINT16 ); + check_property( obj, L"CanPool", VT_BOOL, CIM_BOOLEAN ); + check_property_nullable( obj, L"Description", VT_BSTR, CIM_STRING ); + check_property( obj, L"DeviceID", VT_BSTR, CIM_STRING ); + check_property_nullable( obj, L"EnclosureNumber", VT_I4, CIM_UINT16 ); + check_property( obj, L"FirmwareVersion", VT_BSTR, CIM_STRING ); + check_property( obj, L"FriendlyName", VT_BSTR, CIM_STRING ); + if (0) check_property_nullable( obj, L"FruId", VT_BSTR, CIM_STRING ); + check_property( obj, L"HealthStatus", VT_I4, CIM_UINT16 ); + check_property_nullable( obj, L"IsIndicationEnabled", VT_BOOL, CIM_BOOLEAN ); + check_property( obj, L"IsPartial", VT_BOOL, CIM_BOOLEAN ); + check_property( obj, L"LogicalSectorSize", VT_BSTR, CIM_UINT64 ); + check_property_nullable( obj, L"Manufacturer", VT_BSTR, CIM_STRING ); + check_property( obj, L"MediaType", VT_I4, CIM_UINT16 ); + check_property( obj, L"Model", VT_BSTR, CIM_STRING ); + if (0) check_property_nullable( obj, L"OperationalDetails", VT_ARRAY | VT_BSTR, CIM_FLAG_ARRAY | CIM_STRING ); + check_property( obj, L"OperationalStatus", VT_ARRAY | VT_I4, CIM_FLAG_ARRAY | CIM_UINT16 ); + check_property_nullable( obj, L"OtherCannotPoolReasonDescription", VT_BSTR, CIM_STRING ); + check_property_nullable( obj, L"PartNumber", VT_BSTR, CIM_STRING ); + check_property_nullable( obj, L"PhysicalLocation", VT_BSTR, CIM_STRING ); + check_property( obj, L"PhysicalSectorSize", VT_BSTR, CIM_UINT64 ); + check_property_nullable( obj, L"SerialNumber", VT_BSTR, CIM_STRING ); + check_property( obj, L"Size", VT_BSTR, CIM_UINT64 ); + check_property_nullable( obj, L"SlotNumber", VT_I4, CIM_UINT16 ); + check_property_nullable( obj, L"SoftwareVersion", VT_BSTR, CIM_STRING ); + check_property( obj, L"SpindleSpeed", VT_I4, CIM_UINT32 ); + check_property( obj, L"SupportedUsages", VT_ARRAY | VT_I4, CIM_FLAG_ARRAY | CIM_UINT16 ); + check_property( obj, L"UniqueId", VT_BSTR, CIM_STRING ); + if (0) check_property( obj, L"UniqueIdFormat", VT_I4, CIM_UINT16 ); + check_property( obj, L"Usage", VT_I4, CIM_UINT16 ); + if (0) check_property( obj, L"VirtualDiskFootprint", VT_BSTR, CIM_UINT64 ); + IWbemClassObject_Release( obj ); + } + + IEnumWbemClassObject_Release( result ); + IWbemServices_Release( services ); + SysFreeString( wql ); + SysFreeString( path ); + SysFreeString( query ); +} + START_TEST(query) { BSTR path = SysAllocString( L"ROOT\\CIMV2" ); @@ -2539,6 +2609,7 @@ START_TEST(query) test_SystemRestore( services ); test_empty_namespace( locator ); test_MSSMBios_RawSMBiosTables( locator ); + test_MSFT_PhysicalDisk( locator ); SysFreeString( path ); IWbemServices_Release( services ); From d94fb1f08c5e2561990a57000f199a831c5d0e32 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 4 Sep 2025 19:14:52 -0600 Subject: [PATCH 409/454] wbemprox: Return an error from class_object_Next() for non-zero flags. CW-Bug-Id: #25896 --- dlls/wbemprox/class.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/dlls/wbemprox/class.c b/dlls/wbemprox/class.c index 49e0201cc53f..ee215b1aced7 100644 --- a/dlls/wbemprox/class.c +++ b/dlls/wbemprox/class.c @@ -543,6 +543,12 @@ static HRESULT WINAPI class_object_Next( TRACE( "%p, %#lx, %p, %p, %p, %p\n", iface, lFlags, strName, pVal, pType, plFlavor ); + if (lFlags) + { + WARN( "lFlags %#lx.\n", lFlags ); + return WBEM_E_INVALID_PARAMETER; + } + for (i = obj->index_property; i < table->num_cols; i++) { if (is_method( table, i )) continue; From 6b69b02599b9d73fcfb8348868743808233f41ce Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 4 Sep 2025 18:37:57 -0600 Subject: [PATCH 410/454] wbemprox: Enumerate system properties in class_object_Next(). CW-Bug-Id: #25896 --- dlls/wbemprox/class.c | 18 ++++++--- dlls/wbemprox/query.c | 13 ++++--- dlls/wbemprox/tests/query.c | 63 ++++++++++++++++++++++++++++++++ dlls/wbemprox/wbemprox_private.h | 3 ++ 4 files changed, 87 insertions(+), 10 deletions(-) diff --git a/dlls/wbemprox/class.c b/dlls/wbemprox/class.c index ee215b1aced7..19f8d79d51bd 100644 --- a/dlls/wbemprox/class.c +++ b/dlls/wbemprox/class.c @@ -539,7 +539,7 @@ static HRESULT WINAPI class_object_Next( struct table *table = get_view_table( view, obj->index ); BSTR prop; HRESULT hr; - UINT i; + UINT i, view_idx_start; TRACE( "%p, %#lx, %p, %p, %p, %p\n", iface, lFlags, strName, pVal, pType, plFlavor ); @@ -549,11 +549,19 @@ static HRESULT WINAPI class_object_Next( return WBEM_E_INVALID_PARAMETER; } - for (i = obj->index_property; i < table->num_cols; i++) + view_idx_start = obj->record ? 0 : system_prop_count; + for (i = obj->index_property; i < table->num_cols + view_idx_start; i++) { - if (is_method( table, i )) continue; - if (!is_result_prop( view, table->columns[i].name )) continue; - if (!(prop = SysAllocString( table->columns[i].name ))) return E_OUTOFMEMORY; + if (i < view_idx_start) + { + if (!(prop = SysAllocString( system_props[i] ))) return E_OUTOFMEMORY; + } + else + { + if (is_method( table, i - view_idx_start )) continue; + if (!is_result_prop( view, table->columns[i - view_idx_start].name )) continue; + if (!(prop = SysAllocString( table->columns[i - view_idx_start].name ))) return E_OUTOFMEMORY; + } if (obj->record) { UINT index; diff --git a/dlls/wbemprox/query.c b/dlls/wbemprox/query.c index 9e080f9a03d4..868a5fd9a714 100644 --- a/dlls/wbemprox/query.c +++ b/dlls/wbemprox/query.c @@ -815,6 +815,12 @@ static BOOL is_system_prop( const WCHAR *name ) return (name[0] == '_' && name[1] == '_'); } +const WCHAR * const system_props[] = + { L"__GENUS", L"__CLASS", L"__RELPATH", L"__PROPERTY_COUNT", L"__DERIVATION", L"__SERVER", L"__NAMESPACE", + L"__PATH" }; + +unsigned int system_prop_count = ARRAY_SIZE(system_props); + static BSTR build_proplist( const struct table *table, UINT row, UINT count, UINT *len ) { UINT i, j, offset; @@ -1413,9 +1419,6 @@ HRESULT put_propval( const struct view *view, UINT index, const WCHAR *name, VAR HRESULT get_properties( const struct view *view, UINT index, LONG flags, SAFEARRAY **props ) { - static const WCHAR * const system_props[] = - { L"__GENUS", L"__CLASS", L"__RELPATH", L"__PROPERTY_COUNT", L"__DERIVATION", L"__SERVER", L"__NAMESPACE", - L"__PATH" }; SAFEARRAY *sa; BSTR str; UINT i, table_index, result_index, count = 0; @@ -1426,7 +1429,7 @@ HRESULT get_properties( const struct view *view, UINT index, LONG flags, SAFEARR if ((hr = map_view_index( view, index, &table_index, &result_index )) != S_OK) return hr; table = view->table[table_index]; - if (!(flags & WBEM_FLAG_NONSYSTEM_ONLY)) count += ARRAY_SIZE(system_props); + if (!(flags & WBEM_FLAG_NONSYSTEM_ONLY)) count += system_prop_count; if (!(flags & WBEM_FLAG_SYSTEM_ONLY)) { for (i = 0; i < table->num_cols; i++) @@ -1439,7 +1442,7 @@ HRESULT get_properties( const struct view *view, UINT index, LONG flags, SAFEARR if (!(flags & WBEM_FLAG_NONSYSTEM_ONLY)) { - for (j = 0; j < ARRAY_SIZE(system_props); j++) + for (j = 0; j < system_prop_count; j++) { str = SysAllocString( system_props[j] ); if (!str || SafeArrayPutElement( sa, &j, str ) != S_OK) diff --git a/dlls/wbemprox/tests/query.c b/dlls/wbemprox/tests/query.c index f5c387a9a3f0..4a212dd8a5a8 100644 --- a/dlls/wbemprox/tests/query.c +++ b/dlls/wbemprox/tests/query.c @@ -216,6 +216,68 @@ static void test_like_query( IWbemServices *services ) } +static void test_IWbemClassObject_Next( IWbemServices *services ) +{ + struct + { + const WCHAR *name; + BOOL found; + } + system_props[] = + { + {L"__GENUS"}, {L"__CLASS"}, {L"__RELPATH"}, {L"__PROPERTY_COUNT"}, {L"__DERIVATION"}, + {L"__SERVER"}, {L"__NAMESPACE"}, {L"__PATH"}, + }; + + BSTR wql = SysAllocString( L"wql" ), query = SysAllocString( L"SELECT * FROM Win32_LogicalDisk" ); + BSTR name; + IEnumWbemClassObject *result; + IWbemClassObject *obj; + HRESULT hr; + unsigned int i, j; + DWORD count; + + hr = IWbemServices_ExecQuery( services, wql, query, 0, NULL, &result ); + if (hr != S_OK) + { + win_skip( "Win32_Volume not available\n" ); + return; + } + + hr = IEnumWbemClassObject_Next( result, 10000, 1, &obj, &count ); + ok( hr == S_OK, "got %#lx.\n", hr ); + + IWbemClassObject_BeginEnumeration(obj, 0); + hr = IWbemClassObject_Next( obj, WBEM_FLAG_SYSTEM_ONLY, &name, NULL, NULL, NULL ); + ok( hr == WBEM_E_INVALID_PARAMETER, "got %#lx.\n", hr ); + hr = IWbemClassObject_Next( obj, WBEM_FLAG_NONSYSTEM_ONLY, &name, NULL, NULL, NULL ); + ok( hr == WBEM_E_INVALID_PARAMETER, "got %#lx.\n", hr ); + + for (i = 0; !(hr = IWbemClassObject_Next( obj, 0, &name, NULL, NULL, NULL )); ++i) + { + ok( hr == S_OK, "got %#lx\n", hr ); + for (j = 0; j < ARRAY_SIZE(system_props); ++j) + { + if (!wcscmp(name, system_props[j].name)) + { + system_props[j].found = TRUE; + break; + } + } + SysFreeString( name ); + } + ok( hr == WBEM_S_NO_MORE_DATA, "got %#lx.\n", hr ); + IWbemClassObject_Release( obj ); + + for (i = 0; i < ARRAY_SIZE(system_props); ++i) + ok( system_props[i].found, "%s not found.\n", debugstr_w(system_props[i].name) ); + + IEnumWbemClassObject_Release( result ); + SysFreeString( query ); + SysFreeString( wql ); +} + + static void test_associators( IWbemServices *services ) { static const WCHAR *test[] = @@ -2574,6 +2636,7 @@ START_TEST(query) test_query_semisync( services ); test_select( services ); test_like_query( services ); + test_IWbemClassObject_Next( services ); /* classes */ test_SoftwareLicensingProduct( services ); diff --git a/dlls/wbemprox/wbemprox_private.h b/dlls/wbemprox/wbemprox_private.h index 632d448b8cd3..b8d2cb56ad58 100644 --- a/dlls/wbemprox/wbemprox_private.h +++ b/dlls/wbemprox/wbemprox_private.h @@ -290,3 +290,6 @@ static inline BOOL is_digit(WCHAR c) { return '0' <= c && c <= '9'; } + +extern const WCHAR * const system_props[]; +extern unsigned int system_prop_count; From e2c3c1cf97d450c35f8fc6bcd8fd2c1ba4180620 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 8 Sep 2025 15:44:45 -0600 Subject: [PATCH 411/454] user32/tests: Add test for CB size after setting font. CW-Bug-Id: #25771 --- dlls/user32/tests/combo.c | 48 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/dlls/user32/tests/combo.c b/dlls/user32/tests/combo.c index 2fc0d1ad9c0f..59c810a0203c 100644 --- a/dlls/user32/tests/combo.c +++ b/dlls/user32/tests/combo.c @@ -915,6 +915,53 @@ static void test_combo_ctlcolor(void) DestroyWindow(combo); } +static void test_combo_setfont(void) +{ + COMBOBOXINFO info; + RECT r1, r2; + HWND combo; + BOOL ret; + HFONT hf; + + hf = CreateFontA( 24, 0, 0, 0, FW_DONTCARE, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, + CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH|FF_DONTCARE, "Arial" ); + ok( !!hf, "got NULL.\n" ); + + combo = CreateWindowA( "ComboBox", "Combo", WS_VISIBLE | WS_CHILD | CBS_DROPDOWNLIST, + 5, 5, 200, 200, hMainWnd, (HMENU)COMBO_ID, NULL, 0 ); + info.cbSize = sizeof(COMBOBOXINFO); + ret = GetComboBoxInfo( combo, &info ); + ok( ret, "got error %lu.\n", GetLastError() ); + r1 = info.rcItem; + + SendMessageA( combo, WM_SETFONT, (WPARAM)hf, TRUE ); + + ret = GetComboBoxInfo( combo, &info ); + ok( ret, "got error %lu.\n", GetLastError() ); + r2 = info.rcItem; + ok( memcmp( &r1, &r2, sizeof(r1) ), "got equal rects %s.\n", wine_dbgstr_rect(&r2) ); + + DestroyWindow(combo); + + + combo = CreateWindowA( "ComboBox", "Combo", WS_VISIBLE | WS_CHILD | CBS_DROPDOWNLIST | CBS_OWNERDRAWFIXED, + 5, 5, 200, 200, hMainWnd, (HMENU)COMBO_ID, NULL, 0 ); + info.cbSize = sizeof(COMBOBOXINFO); + ret = GetComboBoxInfo( combo, &info ); + ok( ret, "got error %lu.\n", GetLastError() ); + r1 = info.rcItem; + + SendMessageA( combo, WM_SETFONT, (WPARAM)hf, TRUE ); + + ret = GetComboBoxInfo( combo, &info ); + ok( ret, "got error %lu.\n", GetLastError() ); + r2 = info.rcItem; + todo_wine ok( !memcmp( &r1, &r2, sizeof(r1) ), "got %s, expected %s.\n", wine_dbgstr_rect(&r2), wine_dbgstr_rect(&r1) ); + DestroyWindow(combo); + + DeleteObject(hf); +} + START_TEST(combo) { brush_red = CreateSolidBrush(RGB(255, 0, 0)); @@ -938,6 +985,7 @@ START_TEST(combo) test_listbox_styles(CBS_DROPDOWNLIST); test_listbox_size(CBS_DROPDOWN); test_combo_ctlcolor(); + test_combo_setfont(); DestroyWindow(hMainWnd); DeleteObject(brush_red); From 3ebe8d0615dbdf97e9b5fbe20c67c171b5734dde Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 8 Sep 2025 15:45:15 -0600 Subject: [PATCH 412/454] user32/combo: Don't update item height on WM_SETFONT for owner drawn CB. CW-Bug-Id: #25771 --- dlls/user32/combo.c | 3 ++- dlls/user32/tests/combo.c | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/dlls/user32/combo.c b/dlls/user32/combo.c index a5a81c84a27d..b27a5cc05450 100644 --- a/dlls/user32/combo.c +++ b/dlls/user32/combo.c @@ -1467,7 +1467,8 @@ static void COMBO_Size( HEADCOMBO *lphc ) static void COMBO_Font( LPHEADCOMBO lphc, HFONT hFont, BOOL bRedraw ) { lphc->hFont = hFont; - lphc->item_height = combo_get_text_height(lphc); + if (!CB_OWNERDRAWN(lphc)) + lphc->item_height = combo_get_text_height(lphc); /* * Propagate to owned windows. diff --git a/dlls/user32/tests/combo.c b/dlls/user32/tests/combo.c index 59c810a0203c..77dec2382bde 100644 --- a/dlls/user32/tests/combo.c +++ b/dlls/user32/tests/combo.c @@ -956,7 +956,7 @@ static void test_combo_setfont(void) ret = GetComboBoxInfo( combo, &info ); ok( ret, "got error %lu.\n", GetLastError() ); r2 = info.rcItem; - todo_wine ok( !memcmp( &r1, &r2, sizeof(r1) ), "got %s, expected %s.\n", wine_dbgstr_rect(&r2), wine_dbgstr_rect(&r1) ); + ok( !memcmp( &r1, &r2, sizeof(r1) ), "got %s, expected %s.\n", wine_dbgstr_rect(&r2), wine_dbgstr_rect(&r1) ); DestroyWindow(combo); DeleteObject(hf); From d0e2f093fee575b76d10d7e13e0d1c3af8fc0bfe Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 8 Sep 2025 16:15:46 -0600 Subject: [PATCH 413/454] comctl32/tests: Also call test_combo_setfont() with CBS_OWNERDRAWFIXED. CW-Bug-Id: #25771 --- dlls/comctl32/tests/combo.c | 53 +++++++++++++++++++++++++++++++------ 1 file changed, 45 insertions(+), 8 deletions(-) diff --git a/dlls/comctl32/tests/combo.c b/dlls/comctl32/tests/combo.c index 0ba720a03cea..61516fd4aa9e 100644 --- a/dlls/comctl32/tests/combo.c +++ b/dlls/comctl32/tests/combo.c @@ -743,17 +743,20 @@ static void test_combo_setitemheight(DWORD style) static void test_combo_setfont(DWORD style) { + unsigned int expected_height, initial_height; HFONT hFont1, hFont2; HWND hCombo; RECT r; int i; + winetest_push_context("style %#lx", style); hCombo = create_combobox(style); hFont1 = CreateFontA(10, 0, 0, 0, FW_DONTCARE, FALSE, FALSE, FALSE, SYMBOL_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH|FF_DONTCARE, "Marlett"); hFont2 = CreateFontA(8, 0, 0, 0, FW_DONTCARE, FALSE, FALSE, FALSE, SYMBOL_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH|FF_DONTCARE, "Marlett"); GetClientRect(hCombo, &r); - expect_rect(r, 0, 0, 100, get_font_height(GetStockObject(SYSTEM_FONT)) + 8); + initial_height = get_font_height(GetStockObject(SYSTEM_FONT)) + 8; + expect_rect(r, 0, 0, 100, initial_height); SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&r); MapWindowPoints(HWND_DESKTOP, hMainWnd, (LPPOINT)&r, 2); todo_wine expect_rect(r, 5, 5, 105, 105); @@ -766,24 +769,50 @@ static void test_combo_setfont(DWORD style) { SendMessageA(hCombo, WM_SETFONT, (WPARAM)hFont1, FALSE); GetClientRect(hCombo, &r); - expect_rect(r, 0, 0, 100, 18); + expected_height = style & CBS_OWNERDRAWFIXED ? initial_height : 18; + todo_wine_if(style & CBS_OWNERDRAWFIXED) expect_rect(r, 0, 0, 100, expected_height); SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&r); MapWindowPoints(HWND_DESKTOP, hMainWnd, (LPPOINT)&r, 2); - todo_wine expect_rect(r, 5, 5, 105, 105 - (get_font_height(GetStockObject(SYSTEM_FONT)) - get_font_height(hFont1))); + + if (style & CBS_OWNERDRAWFIXED) + { + todo_wine expect_rect(r, 5, 5, 105, 105); + } + else + { + todo_wine expect_rect(r, 5, 5, 105, 105 - (get_font_height(GetStockObject(SYSTEM_FONT)) - get_font_height(hFont1))); + } SendMessageA(hCombo, WM_SETFONT, (WPARAM)hFont2, FALSE); GetClientRect(hCombo, &r); - expect_rect(r, 0, 0, 100, 16); + expected_height = style & CBS_OWNERDRAWFIXED ? initial_height : 16; + todo_wine_if(style & CBS_OWNERDRAWFIXED) expect_rect(r, 0, 0, 100, expected_height); SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&r); MapWindowPoints(HWND_DESKTOP, hMainWnd, (LPPOINT)&r, 2); - todo_wine expect_rect(r, 5, 5, 105, 105 - (get_font_height(GetStockObject(SYSTEM_FONT)) - get_font_height(hFont2))); + + if (style & CBS_OWNERDRAWFIXED) + { + todo_wine expect_rect(r, 5, 5, 105, 105); + } + else + { + todo_wine expect_rect(r, 5, 5, 105, 105 - (get_font_height(GetStockObject(SYSTEM_FONT)) - get_font_height(hFont2))); + } SendMessageA(hCombo, WM_SETFONT, (WPARAM)hFont1, FALSE); GetClientRect(hCombo, &r); - expect_rect(r, 0, 0, 100, 18); + expected_height = style & CBS_OWNERDRAWFIXED ? initial_height : 18; + todo_wine_if(style & CBS_OWNERDRAWFIXED) expect_rect(r, 0, 0, 100, expected_height); SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&r); MapWindowPoints(HWND_DESKTOP, hMainWnd, (LPPOINT)&r, 2); - todo_wine expect_rect(r, 5, 5, 105, 105 - (get_font_height(GetStockObject(SYSTEM_FONT)) - get_font_height(hFont1))); + if (style & CBS_OWNERDRAWFIXED) + { + todo_wine expect_rect(r, 5, 5, 105, 105); + } + else + { + todo_wine expect_rect(r, 5, 5, 105, 105 - (get_font_height(GetStockObject(SYSTEM_FONT)) - get_font_height(hFont1))); + } } else { @@ -798,7 +827,13 @@ static void test_combo_setfont(DWORD style) SendMessageA(hCombo, WM_SETFONT, (WPARAM)hFont, FALSE); GetClientRect(hCombo, &r); - ok((r.bottom - r.top) == (height + 8), "Unexpected client rect height.\n"); + if (style & CBS_OWNERDRAWFIXED) + expected_height = initial_height; + else + expected_height = (height + 8); + todo_wine_if(style & CBS_OWNERDRAWFIXED && initial_height != height + 8) + ok((r.bottom - r.top) == expected_height, "Unexpected client rect height %ld, expected %d.\n", r.bottom - r.top, + expected_height); SendMessageA(hCombo, WM_SETFONT, 0, FALSE); DeleteObject(hFont); } @@ -806,6 +841,7 @@ static void test_combo_setfont(DWORD style) DestroyWindow(hCombo); DeleteObject(hFont1); DeleteObject(hFont2); + winetest_pop_context(); } static LRESULT (CALLBACK *old_parent_proc)(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); @@ -1691,6 +1727,7 @@ START_TEST(combo) test_combo_WS_VSCROLL(); test_combo_setfont(CBS_DROPDOWN); test_combo_setfont(CBS_DROPDOWNLIST); + test_combo_setfont(CBS_DROPDOWNLIST | CBS_OWNERDRAWFIXED); test_combo_setitemheight(CBS_DROPDOWN); test_combo_setitemheight(CBS_DROPDOWNLIST); test_combo_CBN_SELCHANGE(); From 56bd027bf58c5f804797a0d4e30a5733d183fba9 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 8 Sep 2025 16:19:06 -0600 Subject: [PATCH 414/454] comctl32/combo: Don't update item height on WM_SETFONT for owner drawn CB. CW-Bug-Id: #25771 --- dlls/comctl32/combo.c | 3 ++- dlls/comctl32/tests/combo.c | 7 +++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/dlls/comctl32/combo.c b/dlls/comctl32/combo.c index 09579952f225..dfa2e1e60fdc 100644 --- a/dlls/comctl32/combo.c +++ b/dlls/comctl32/combo.c @@ -1439,7 +1439,8 @@ static void COMBO_Size( HEADCOMBO *lphc ) static void COMBO_Font( LPHEADCOMBO lphc, HFONT hFont, BOOL bRedraw ) { lphc->hFont = hFont; - lphc->item_height = combo_get_text_height(lphc); + if (!CB_OWNERDRAWN(lphc)) + lphc->item_height = combo_get_text_height(lphc); /* * Propagate to owned windows. diff --git a/dlls/comctl32/tests/combo.c b/dlls/comctl32/tests/combo.c index 61516fd4aa9e..5af530ad5be4 100644 --- a/dlls/comctl32/tests/combo.c +++ b/dlls/comctl32/tests/combo.c @@ -770,7 +770,7 @@ static void test_combo_setfont(DWORD style) SendMessageA(hCombo, WM_SETFONT, (WPARAM)hFont1, FALSE); GetClientRect(hCombo, &r); expected_height = style & CBS_OWNERDRAWFIXED ? initial_height : 18; - todo_wine_if(style & CBS_OWNERDRAWFIXED) expect_rect(r, 0, 0, 100, expected_height); + expect_rect(r, 0, 0, 100, expected_height); SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&r); MapWindowPoints(HWND_DESKTOP, hMainWnd, (LPPOINT)&r, 2); @@ -786,7 +786,7 @@ static void test_combo_setfont(DWORD style) SendMessageA(hCombo, WM_SETFONT, (WPARAM)hFont2, FALSE); GetClientRect(hCombo, &r); expected_height = style & CBS_OWNERDRAWFIXED ? initial_height : 16; - todo_wine_if(style & CBS_OWNERDRAWFIXED) expect_rect(r, 0, 0, 100, expected_height); + expect_rect(r, 0, 0, 100, expected_height); SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&r); MapWindowPoints(HWND_DESKTOP, hMainWnd, (LPPOINT)&r, 2); @@ -802,7 +802,7 @@ static void test_combo_setfont(DWORD style) SendMessageA(hCombo, WM_SETFONT, (WPARAM)hFont1, FALSE); GetClientRect(hCombo, &r); expected_height = style & CBS_OWNERDRAWFIXED ? initial_height : 18; - todo_wine_if(style & CBS_OWNERDRAWFIXED) expect_rect(r, 0, 0, 100, expected_height); + expect_rect(r, 0, 0, 100, expected_height); SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&r); MapWindowPoints(HWND_DESKTOP, hMainWnd, (LPPOINT)&r, 2); if (style & CBS_OWNERDRAWFIXED) @@ -831,7 +831,6 @@ static void test_combo_setfont(DWORD style) expected_height = initial_height; else expected_height = (height + 8); - todo_wine_if(style & CBS_OWNERDRAWFIXED && initial_height != height + 8) ok((r.bottom - r.top) == expected_height, "Unexpected client rect height %ld, expected %d.\n", r.bottom - r.top, expected_height); SendMessageA(hCombo, WM_SETFONT, 0, FALSE); From cea3123357a80736417018997c91cddf905afc9a Mon Sep 17 00:00:00 2001 From: Ziqing Hui Date: Tue, 2 Sep 2025 16:59:25 +0800 Subject: [PATCH 415/454] mf/tests: Test GetOutputStatus for video processor. (cherry picked from commit e5ab13967de05784069be235cbd4dd4d01b17ff1) CW-Bug-Id: #25688 --- dlls/mf/tests/transform.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/dlls/mf/tests/transform.c b/dlls/mf/tests/transform.c index ba72a1e625d7..b8eddb2df8c0 100644 --- a/dlls/mf/tests/transform.c +++ b/dlls/mf/tests/transform.c @@ -8599,9 +8599,19 @@ static void test_video_processor(BOOL use_2d_buffer) hr = IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, &MFVideoFormat_RGB32); ok(hr == S_OK, "Failed to set attribute, hr %#lx.\n", hr); + hr = IMFTransform_GetOutputStatus(transform, &flags); + ok(hr == MF_E_TRANSFORM_TYPE_NOT_SET, "GetOutputStatus returned %#lx.\n", hr); + hr = IMFTransform_SetOutputType(transform, 0, media_type, 0); ok(hr == S_OK, "Failed to set output type, hr %#lx.\n", hr); + flags = 0xdeadbeef; + hr = IMFTransform_GetOutputStatus(transform, &flags); + todo_wine + ok(hr == S_OK, "GetOutputStatus returned %#lx.\n", hr); + todo_wine + ok(flags == 0, "Unexpected output status %#lx.\n", flags); + hr = MFCalculateImageSize(&MFVideoFormat_IYUV, 16, 16, (UINT32 *)&input_info.cbSize); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); hr = MFCalculateImageSize(&MFVideoFormat_RGB32, 16, 16, (UINT32 *)&output_info.cbSize); @@ -8623,6 +8633,13 @@ static void test_video_processor(BOOL use_2d_buffer) todo_wine ok(hr == S_OK, "Failed to push a sample, hr %#lx.\n", hr); + flags = 0xdeadbeef; + hr = IMFTransform_GetOutputStatus(transform, &flags); + todo_wine + ok(hr == S_OK, "GetOutputStatus returned %#lx.\n", hr); + todo_wine + ok(flags == MFT_OUTPUT_STATUS_SAMPLE_READY, "Unexpected output status %#lx.\n", flags); + hr = IMFTransform_ProcessInput(transform, 0, input_sample, 0); todo_wine ok(hr == MF_E_NOTACCEPTING, "Unexpected hr %#lx.\n", hr); @@ -8654,6 +8671,13 @@ static void test_video_processor(BOOL use_2d_buffer) { hr = check_mft_process_output(transform, output_sample, &output_status); ok(hr == MF_E_TRANSFORM_NEED_MORE_INPUT, "Unexpected hr %#lx.\n", hr); + + flags = 0xdeadbeef; + hr = IMFTransform_GetOutputStatus(transform, &flags); + todo_wine + ok(hr == S_OK, "GetOutputStatus returned %#lx.\n", hr); + todo_wine + ok(flags == 0, "Unexpected output status %#lx.\n", flags); } ref = IMFTransform_Release(transform); From 8f62de0c27ab66b4df8290d3128a3d771bd924c8 Mon Sep 17 00:00:00 2001 From: Ziqing Hui Date: Tue, 2 Sep 2025 17:06:26 +0800 Subject: [PATCH 416/454] winegstreamer: Add semi-stub implementation for video_processor_GetOutputStatus. (cherry picked from commit 1af014a375c9b8bbb7f23d6a331eb0803d429a37) CW-Bug-Id: #25688 --- dlls/mf/tests/transform.c | 4 ---- dlls/winegstreamer/video_processor.c | 3 ++- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/dlls/mf/tests/transform.c b/dlls/mf/tests/transform.c index b8eddb2df8c0..5dc8df6ac5a4 100644 --- a/dlls/mf/tests/transform.c +++ b/dlls/mf/tests/transform.c @@ -8607,7 +8607,6 @@ static void test_video_processor(BOOL use_2d_buffer) flags = 0xdeadbeef; hr = IMFTransform_GetOutputStatus(transform, &flags); - todo_wine ok(hr == S_OK, "GetOutputStatus returned %#lx.\n", hr); todo_wine ok(flags == 0, "Unexpected output status %#lx.\n", flags); @@ -8635,9 +8634,7 @@ static void test_video_processor(BOOL use_2d_buffer) flags = 0xdeadbeef; hr = IMFTransform_GetOutputStatus(transform, &flags); - todo_wine ok(hr == S_OK, "GetOutputStatus returned %#lx.\n", hr); - todo_wine ok(flags == MFT_OUTPUT_STATUS_SAMPLE_READY, "Unexpected output status %#lx.\n", flags); hr = IMFTransform_ProcessInput(transform, 0, input_sample, 0); @@ -8674,7 +8671,6 @@ static void test_video_processor(BOOL use_2d_buffer) flags = 0xdeadbeef; hr = IMFTransform_GetOutputStatus(transform, &flags); - todo_wine ok(hr == S_OK, "GetOutputStatus returned %#lx.\n", hr); todo_wine ok(flags == 0, "Unexpected output status %#lx.\n", flags); diff --git a/dlls/winegstreamer/video_processor.c b/dlls/winegstreamer/video_processor.c index 35974615ba3b..ed09f3984d5c 100644 --- a/dlls/winegstreamer/video_processor.c +++ b/dlls/winegstreamer/video_processor.c @@ -884,7 +884,8 @@ static HRESULT WINAPI video_processor_GetOutputStatus(IMFTransform *iface, DWORD if (!impl->output_type) return MF_E_TRANSFORM_TYPE_NOT_SET; - return E_NOTIMPL; + *flags = MFT_OUTPUT_STATUS_SAMPLE_READY; + return S_OK; } static HRESULT WINAPI video_processor_SetOutputBounds(IMFTransform *iface, LONGLONG lower, LONGLONG upper) From d0f685399b4f7f02169b3d92705d6b25bc16cf38 Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Tue, 9 Sep 2025 10:16:53 +0100 Subject: [PATCH 417/454] winegstreamer: Free stream buffers before wg_parser_disconnect. Renaming destroy_stream to free_stream_buffers because that's what it does. wg_parser_disconnect frees all streams on the wg_parser, but free_stream_buffers later calls wg_parser_stream_release_buffer on those streams. It has saved pointers to those in wm_stream->wg_stream, which became dangling pointers after wg_parser_disconnect. CW-Bug-Id: #24140 --- dlls/winegstreamer/wm_reader.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/dlls/winegstreamer/wm_reader.c b/dlls/winegstreamer/wm_reader.c index 9145f59f474a..df7c20e8749d 100644 --- a/dlls/winegstreamer/wm_reader.c +++ b/dlls/winegstreamer/wm_reader.c @@ -1490,7 +1490,7 @@ static const IWMReaderTimecodeVtbl timecode_vtbl = timecode_GetTimecodeRangeBounds, }; -static void destroy_stream(struct wm_reader *reader) +static void free_stream_buffers(struct wm_reader *reader) { unsigned int i; @@ -1652,6 +1652,8 @@ static HRESULT reinit_stream(struct wm_reader *reader, bool read_compressed) enable_opengl = FALSE; } + free_stream_buffers(reader); + wg_parser_disconnect(reader->wg_parser); EnterCriticalSection(&reader->shutdown_cs); @@ -1661,7 +1663,6 @@ static HRESULT reinit_stream(struct wm_reader *reader, bool read_compressed) CloseHandle(reader->read_thread); reader->read_thread = NULL; - destroy_stream(reader); wg_parser_destroy(reader->wg_parser); reader->wg_parser = 0; @@ -1713,7 +1714,7 @@ static HRESULT reinit_stream(struct wm_reader *reader, bool read_compressed) reader->read_thread = NULL; out_destroy_parser: - destroy_stream(reader); + free_stream_buffers(reader); wg_parser_destroy(reader->wg_parser); reader->wg_parser = 0; @@ -1993,6 +1994,8 @@ static HRESULT WINAPI reader_Close(IWMSyncReader2 *iface) return NS_E_INVALID_REQUEST; } + free_stream_buffers(reader); + wg_parser_disconnect(reader->wg_parser); EnterCriticalSection(&reader->shutdown_cs); @@ -2002,7 +2005,6 @@ static HRESULT WINAPI reader_Close(IWMSyncReader2 *iface) CloseHandle(reader->read_thread); reader->read_thread = NULL; - destroy_stream(reader); wg_parser_destroy(reader->wg_parser); reader->wg_parser = 0; From 40a703195d9a7ee5051e7abae0a8003ae11b0e98 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 9 Sep 2025 16:41:26 -0600 Subject: [PATCH 418/454] iphlpapi/tests: Add more tests for GetBestRoute(). CW-Bug-Id: #25912 --- dlls/iphlpapi/tests/iphlpapi.c | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/dlls/iphlpapi/tests/iphlpapi.c b/dlls/iphlpapi/tests/iphlpapi.c index e8386f52822d..7bd079cd4581 100644 --- a/dlls/iphlpapi/tests/iphlpapi.c +++ b/dlls/iphlpapi/tests/iphlpapi.c @@ -1877,26 +1877,34 @@ static void testGetBestInterfaceEx(void) static void testGetBestRoute(void) { - DWORD apiReturn; + MIB_IFROW if_row; + DWORD err; MIB_IPFORWARDROW bestRoute; - apiReturn = GetBestRoute( INADDR_ANY, 0, &bestRoute ); - trace( "GetBestRoute([0.0.0.0], 0, [...]) = %lu\n", apiReturn ); - if (apiReturn == ERROR_NOT_SUPPORTED) + err = GetBestRoute( INADDR_ANY, 0, &bestRoute ); + trace( "GetBestRoute([0.0.0.0], 0, [...]) = %lu\n", err ); + if (err == ERROR_NOT_SUPPORTED) { skip( "GetBestRoute is not supported\n" ); return; } - apiReturn = GetBestRoute( INADDR_ANY, 0, NULL ); - ok( apiReturn == ERROR_INVALID_PARAMETER, + err = GetBestRoute( INADDR_ANY, 0, NULL ); + ok( err == ERROR_INVALID_PARAMETER, "GetBestRoute([0.0.0.0], 0, NULL) returned %lu, expected %d\n", - apiReturn, ERROR_INVALID_PARAMETER ); + err, ERROR_INVALID_PARAMETER ); - apiReturn = GetBestRoute( INADDR_LOOPBACK, 0, &bestRoute ); - ok( apiReturn == NO_ERROR, + memset( &bestRoute, 0xcc, sizeof(bestRoute)); + err = GetBestRoute( htonl( INADDR_LOOPBACK ), 0, &bestRoute ); + ok( err == NO_ERROR, "GetBestRoute([127.0.0.1], 0, NULL) returned %lu, expected %d\n", - apiReturn, NO_ERROR ); + err, NO_ERROR ); + todo_wine ok( bestRoute.dwForwardMask == 0xffffffff, "got %#lx.\n", bestRoute.dwForwardMask ); + + if_row.dwIndex = bestRoute.dwForwardIfIndex; + err = GetIfEntry( &if_row ); + ok( !err, "got %lu.\n", err ); + todo_wine ok( if_row.dwType == IF_TYPE_SOFTWARE_LOOPBACK, "got %#lx.\n", if_row.dwType ); } /* From ed0ef8087754c21fa0b44895e9843da9f9ef6468 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 9 Sep 2025 17:01:42 -0600 Subject: [PATCH 419/454] nsi/tests: Add test for ipv4 loopback routes presence. CW-Bug-Id: #25912 --- dlls/nsi/tests/Makefile.in | 2 +- dlls/nsi/tests/nsi.c | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/dlls/nsi/tests/Makefile.in b/dlls/nsi/tests/Makefile.in index 45a7d77668f2..50e331b3c3b4 100644 --- a/dlls/nsi/tests/Makefile.in +++ b/dlls/nsi/tests/Makefile.in @@ -1,5 +1,5 @@ TESTDLL = nsi.dll -IMPORTS = nsi uuid iphlpapi +IMPORTS = nsi uuid iphlpapi ws2_32 SOURCES = \ nsi.c diff --git a/dlls/nsi/tests/nsi.c b/dlls/nsi/tests/nsi.c index 56cf8508e7b0..b11061f64e68 100644 --- a/dlls/nsi/tests/nsi.c +++ b/dlls/nsi/tests/nsi.c @@ -709,6 +709,7 @@ static void test_ip_forward( int family ) const NPI_MODULEID *mod = (family == AF_INET) ? &NPI_MS_IPV4_MODULEID : &NPI_MS_IPV6_MODULEID; DWORD key_size = (family == AF_INET) ? sizeof(*key4) : sizeof(*key6); DWORD err, count, i, rw_size, dyn_size; + BOOL ipv4_loopback_mask_found = FALSE, ipv4_loopback_found = FALSE; winetest_push_context( family == AF_INET ? "AF_INET" : "AF_INET6" ); @@ -760,6 +761,18 @@ static void test_ip_forward( int family ) ok( row->NextHop.Ipv4.sin_addr.s_addr == key4->next_hop.s_addr, "mismatch\n" ); ok( row->NextHop.Ipv4.sin_port == 0, "mismatch\n" ); ok( row->Age == dyn4->age, "mismatch\n" ); + if (key4->prefix.s_addr == htonl( 0x7f000000 )) + { + ipv4_loopback_mask_found = TRUE; + ok( key4->prefix_len == 8, "got %u.\n", key4->prefix_len ); + ok( !key4->next_hop.s_addr, "got %#lx.\n", key4->next_hop.s_addr ); + } + if (key4->prefix.s_addr == htonl( INADDR_LOOPBACK )) + { + ipv4_loopback_found = TRUE; + ok( key4->prefix_len == 32, "got %u.\n", key4->prefix_len ); + ok( !key4->next_hop.s_addr, "got %#lx.\n", key4->next_hop.s_addr ); + } } else { @@ -796,6 +809,11 @@ static void test_ip_forward( int family ) winetest_pop_context(); } + if (family == AF_INET) + { + todo_wine ok( ipv4_loopback_mask_found, "127.0.0.0/8 not found.\n" ); + todo_wine ok( ipv4_loopback_found, "127.0.0.1/32 not found.\n" ); + } FreeMibTable( table ); NsiFreeTable( key_tbl, rw_tbl, dyn_tbl, stat_tbl ); From e3ba0d24e88f1152725077f4cdd368fa363c194d Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 9 Sep 2025 16:04:17 -0600 Subject: [PATCH 420/454] nsiproxy.sys: Explicitly add loopback entries to ipv4 forward table on Linux. CW-Bug-Id: #25912 --- dlls/iphlpapi/tests/iphlpapi.c | 4 ++-- dlls/nsi/tests/nsi.c | 4 ++-- dlls/nsiproxy.sys/ip.c | 43 ++++++++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+), 4 deletions(-) diff --git a/dlls/iphlpapi/tests/iphlpapi.c b/dlls/iphlpapi/tests/iphlpapi.c index 7bd079cd4581..ebd69a37ad89 100644 --- a/dlls/iphlpapi/tests/iphlpapi.c +++ b/dlls/iphlpapi/tests/iphlpapi.c @@ -1899,12 +1899,12 @@ static void testGetBestRoute(void) ok( err == NO_ERROR, "GetBestRoute([127.0.0.1], 0, NULL) returned %lu, expected %d\n", err, NO_ERROR ); - todo_wine ok( bestRoute.dwForwardMask == 0xffffffff, "got %#lx.\n", bestRoute.dwForwardMask ); + ok( bestRoute.dwForwardMask == 0xffffffff, "got %#lx.\n", bestRoute.dwForwardMask ); if_row.dwIndex = bestRoute.dwForwardIfIndex; err = GetIfEntry( &if_row ); ok( !err, "got %lu.\n", err ); - todo_wine ok( if_row.dwType == IF_TYPE_SOFTWARE_LOOPBACK, "got %#lx.\n", if_row.dwType ); + ok( if_row.dwType == IF_TYPE_SOFTWARE_LOOPBACK, "got %#lx.\n", if_row.dwType ); } /* diff --git a/dlls/nsi/tests/nsi.c b/dlls/nsi/tests/nsi.c index b11061f64e68..6de63221ac43 100644 --- a/dlls/nsi/tests/nsi.c +++ b/dlls/nsi/tests/nsi.c @@ -811,8 +811,8 @@ static void test_ip_forward( int family ) } if (family == AF_INET) { - todo_wine ok( ipv4_loopback_mask_found, "127.0.0.0/8 not found.\n" ); - todo_wine ok( ipv4_loopback_found, "127.0.0.1/32 not found.\n" ); + ok( ipv4_loopback_mask_found, "127.0.0.0/8 not found.\n" ); + ok( ipv4_loopback_found, "127.0.0.1/32 not found.\n" ); } FreeMibTable( table ); diff --git a/dlls/nsiproxy.sys/ip.c b/dlls/nsiproxy.sys/ip.c index be43d32557d8..5bb834fee5a8 100644 --- a/dlls/nsiproxy.sys/ip.c +++ b/dlls/nsiproxy.sys/ip.c @@ -59,6 +59,10 @@ #include #endif +#ifdef HAVE_NET_IF_H +# include +#endif + #ifdef __APPLE__ /* For reasons unknown, Mac OS doesn't export to user- * space. We'll have to define the needed struct ourselves. @@ -1494,11 +1498,50 @@ static NTSTATUS ipv4_forward_enumerate_all( void *key_data, UINT key_size, void #ifdef __linux__ { + struct ifaddrs *addrs, *ifentry; char buf[512], *ptr; struct in_addr mask; UINT rtf_flags; FILE *fp; + /* Loopback routes are not present in /proc/net/routes, add those explicitly. */ + if (getifaddrs( &addrs )) return STATUS_NO_MORE_ENTRIES; + for (ifentry = addrs; ifentry; ifentry = ifentry->ifa_next) + { + if (!(ifentry->ifa_flags & IFF_LOOPBACK)) continue; + if (!convert_unix_name_to_luid( ifentry->ifa_name, &entry.luid )) continue; + if (!convert_luid_to_index( &entry.luid, &entry.if_index )) continue; + + if (num < *count) + { + entry.prefix.s_addr = htonl( 0x7f000000 ); + entry.next_hop.s_addr = 0; + entry.metric = 256; + entry.prefix_len = 8; + entry.protocol = MIB_IPPROTO_LOCAL; + entry.loopback = 1; + ipv4_forward_fill_entry( &entry, key_data, rw_data, dynamic_data, static_data ); + key_data = (BYTE *)key_data + key_size; + rw_data = (BYTE *)rw_data + rw_size; + dynamic_data = (BYTE *)dynamic_data + dynamic_size; + static_data = (BYTE *)static_data + static_size; + } + num++; + if (num < *count) + { + entry.prefix.s_addr = htonl( INADDR_LOOPBACK ); + entry.prefix_len = 32; + ipv4_forward_fill_entry( &entry, key_data, rw_data, dynamic_data, static_data ); + key_data = (BYTE *)key_data + key_size; + rw_data = (BYTE *)rw_data + rw_size; + dynamic_data = (BYTE *)dynamic_data + dynamic_size; + static_data = (BYTE *)static_data + static_size; + } + num++; + break; + } + freeifaddrs( addrs ); + if (!(fp = fopen( "/proc/net/route", "r" ))) return STATUS_NOT_SUPPORTED; /* skip header line */ From 549247a476ba7111e1d973f1c65e66c3aa1fa883 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 9 Sep 2025 17:41:27 -0600 Subject: [PATCH 421/454] nsi: Match struct nsi_tcp_conn_dynamic size to up to date Win11. Fixes a crash in test_tcp_tables() on up to date Win11. CW-Bug-Id: #25912 --- dlls/nsi/tests/nsi.c | 4 ++-- include/wine/nsi.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/dlls/nsi/tests/nsi.c b/dlls/nsi/tests/nsi.c index 6de63221ac43..0f1cb6db0b08 100644 --- a/dlls/nsi/tests/nsi.c +++ b/dlls/nsi/tests/nsi.c @@ -926,7 +926,7 @@ static void test_tcp_stats( int family ) static void test_tcp_tables( int family, int table_type ) { DWORD dyn_sizes[] = { FIELD_OFFSET(struct nsi_tcp_conn_dynamic, unk[2]), FIELD_OFFSET(struct nsi_tcp_conn_dynamic, unk[3]), - sizeof(struct nsi_tcp_conn_dynamic) }; + FIELD_OFFSET(struct nsi_tcp_conn_dynamic, unk[4]), sizeof(struct nsi_tcp_conn_dynamic) }; DWORD i, err, count, table_num, dyn_size, size; struct nsi_tcp_conn_key *keys; struct nsi_tcp_conn_dynamic *dyn_tbl, *dyn; @@ -949,7 +949,7 @@ static void test_tcp_tables( int family, int table_type ) for (i = 0; i < ARRAY_SIZE(dyn_sizes); i++) { err = NsiAllocateAndGetTable( 1, &NPI_MS_TCP_MODULEID, table_num, (void **)&keys, sizeof(*keys), NULL, 0, - (void **)&dyn_tbl, dyn_sizes[i], (void **)&stat, sizeof(*stat), &count, 0 ); + (void **)&dyn_tbl, dyn_sizes[i] + 4, (void **)&stat, sizeof(*stat), &count, 0 ); if (!err) break; } ok( !err, "got %ld\n", err ); diff --git a/include/wine/nsi.h b/include/wine/nsi.h index e281321b026e..5028bbf9e192 100644 --- a/include/wine/nsi.h +++ b/include/wine/nsi.h @@ -376,7 +376,7 @@ struct nsi_tcp_conn_key struct nsi_tcp_conn_dynamic { UINT state; - UINT unk[4]; + UINT unk[5]; }; struct nsi_tcp_conn_static From 9f29e3db997139bdf08f66fdf89e9849b148012c Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 9 Sep 2025 19:33:32 -0600 Subject: [PATCH 422/454] iphlpapi: Fix ipforward_row_cmp(). CW-Bug-Id: #25912 --- dlls/iphlpapi/iphlpapi_main.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/dlls/iphlpapi/iphlpapi_main.c b/dlls/iphlpapi/iphlpapi_main.c index 5f833a0f4892..b9dbde5699c7 100644 --- a/dlls/iphlpapi/iphlpapi_main.c +++ b/dlls/iphlpapi/iphlpapi_main.c @@ -2080,10 +2080,15 @@ DWORD WINAPI AllocateAndGetIpAddrTableFromStack( MIB_IPADDRTABLE **table, BOOL s static int ipforward_row_cmp( const void *a, const void *b ) { const MIB_IPFORWARDROW *rowA = a, *rowB = b; - return DWORD_cmp(RtlUlongByteSwap( rowA->dwForwardDest ), RtlUlongByteSwap( rowB->dwForwardDest )) || - DWORD_cmp(rowA->dwForwardProto, rowB->dwForwardProto) || - DWORD_cmp(rowA->dwForwardPolicy, rowB->dwForwardPolicy) || - DWORD_cmp(RtlUlongByteSwap( rowA->dwForwardNextHop ), RtlUlongByteSwap( rowB->dwForwardNextHop )); + int ret; + + if ((ret = DWORD_cmp(RtlUlongByteSwap( rowA->dwForwardDest ), RtlUlongByteSwap( rowB->dwForwardDest )))) + return ret; + if ((ret = DWORD_cmp(rowA->dwForwardProto, rowB->dwForwardProto))) + return ret; + if ((ret = DWORD_cmp(rowA->dwForwardPolicy, rowB->dwForwardPolicy))) + return ret; + return DWORD_cmp(RtlUlongByteSwap( rowA->dwForwardNextHop ), RtlUlongByteSwap( rowB->dwForwardNextHop )); } /****************************************************************** From 655009f5959e01c1a477820eb8224eb6239234c7 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 9 Sep 2025 19:50:54 -0600 Subject: [PATCH 423/454] iphlpapi: Fix udp_row_cmp(). CW-Bug-Id: #25912 --- dlls/iphlpapi/iphlpapi_main.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/dlls/iphlpapi/iphlpapi_main.c b/dlls/iphlpapi/iphlpapi_main.c index b9dbde5699c7..01d1c4c318a4 100644 --- a/dlls/iphlpapi/iphlpapi_main.c +++ b/dlls/iphlpapi/iphlpapi_main.c @@ -3552,9 +3552,12 @@ static void udp_row_fill( void *table, DWORD num, ULONG family, ULONG table_clas static int udp_row_cmp( const void *a, const void *b ) { const MIB_UDPROW *rowA = a, *rowB = b; + int ret; + + if ((ret = DWORD_cmp(RtlUlongByteSwap( rowA->dwLocalAddr), RtlUlongByteSwap( rowB->dwLocalAddr )))) + return ret; - return DWORD_cmp(RtlUlongByteSwap( rowA->dwLocalAddr), RtlUlongByteSwap( rowB->dwLocalAddr )) || - RtlUshortByteSwap( rowA->dwLocalPort ) - RtlUshortByteSwap( rowB->dwLocalPort ); + return RtlUshortByteSwap( rowA->dwLocalPort ) - RtlUshortByteSwap( rowB->dwLocalPort ); } static int udp6_row_cmp( const void *a, const void *b ) From 4d46be12856b3a3ab31626d29af342d7ac0993da Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 10 Sep 2025 09:21:33 -0600 Subject: [PATCH 424/454] ntdll: HACK: Enable WINE_SIMULATE_WRITECOPY for Gemstones. CW-Bug-Id: #25917 --- dlls/ntdll/unix/loader.c | 1 + 1 file changed, 1 insertion(+) diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c index 8b661c8ea06a..fe6e59057fca 100644 --- a/dlls/ntdll/unix/loader.c +++ b/dlls/ntdll/unix/loader.c @@ -2227,6 +2227,7 @@ static void hacks_init(void) || !strcmp(sgi, "2152990") /* Dinogen Online */ || !strcmp(sgi, "2176450") /* Mr. Hopp's Playhouse 3 */ || !strcmp(sgi, "2329630") /* Lovey-Dovey Lockdown */ + || !strcmp(sgi, "2209020") /* Gemstones */ || !strcmp(sgi, "2361360"); /* Hentai Maid Memories */ if (sgi) wine_allocs_2g_limit = !strcmp(sgi, "359870"); From b8a0e3dfbcaee7a7434b3b6d9e49716f3bba4099 Mon Sep 17 00:00:00 2001 From: Zhiyi Zhang Date: Thu, 28 Aug 2025 16:29:11 +0800 Subject: [PATCH 425/454] Revert "win32u: Limit fullscreen visible rects to the monitor rects." This reverts commit f2b08298382a2b463d377dd7c1e33bfa6f060d3d. CW-Bug-Id: #25858 --- dlls/win32u/sysparams.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/dlls/win32u/sysparams.c b/dlls/win32u/sysparams.c index 17102f4d8a67..5e2014aee180 100644 --- a/dlls/win32u/sysparams.c +++ b/dlls/win32u/sysparams.c @@ -2767,8 +2767,8 @@ RECT map_rect_virt_to_raw_for_monitor( HMONITOR handle, RECT rect, UINT dpi_from struct window_rects map_window_rects_virt_to_raw( struct window_rects rects, UINT dpi_from ) { RECT rect, monitor_rect, virt_visible_rect = rects.visible; - BOOL is_fullscreen, first_fullscreen = TRUE; struct monitor *monitor; + BOOL is_fullscreen; if (!lock_display_devices( FALSE )) return rects; if ((monitor = get_monitor_from_rect( rects.window, MONITOR_DEFAULTTONEAREST, dpi_from, MDT_DEFAULT ))) @@ -2786,18 +2786,6 @@ struct window_rects map_window_rects_virt_to_raw( struct window_rects rects, UIN is_fullscreen = intersect_rect( &rect, &monitor_rect, &virt_visible_rect ) && EqualRect( &rect, &monitor_rect ); if (is_fullscreen) { - /* If the visible rect is fullscreen on any one of the monitors, limit the visible rect - * to the monitor rects. This is needed because adding __NET_WM_STATE_FULLSCREEN will - * make WMs move the window to cover exactly the monitor rect. If the application sets a - * visible rect slightly larger than the monitor rect and insists on changing to the rect - * that it previously set when the rect is changed by the WM, then the window rect will - * be repeatedly changed by :wthe WM and the application, causing a flickering effect */ - if (first_fullscreen) - { - SetRectEmpty( &rects.visible ); - first_fullscreen = FALSE; - } - rect = monitor_get_rect( monitor, 0, MDT_RAW_DPI ); union_rect( &rects.visible, &rects.visible, &rect ); } From 0addff07647996f2ef08e3821a5d89779b72c327 Mon Sep 17 00:00:00 2001 From: Zhiyi Zhang Date: Thu, 28 Aug 2025 16:21:26 +0800 Subject: [PATCH 426/454] winex11.drv: Ignore fullscreen window config changes. CW-Bug-Id: #25747 CW-Bug-Id: #25858 --- dlls/winex11.drv/window.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index 14da3e97b8b9..76ad973ed2ae 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -1904,6 +1904,7 @@ static UINT window_update_client_config( struct x11drv_win_data *data ) static const UINT fullscreen_mask = (1 << NET_WM_STATE_MAXIMIZED) | (1 << NET_WM_STATE_FULLSCREEN); UINT old_style = NtUserGetWindowLongW( data->hwnd, GWL_STYLE ), new_style, flags; RECT rect, old_rect = data->rects.window, new_rect; + long old_monitors[4], monitors[4]; BOOL disable_maximize; if (!data->managed) return 0; /* unmanaged windows are managed by the Win32 side */ @@ -1914,6 +1915,18 @@ static UINT window_update_client_config( struct x11drv_win_data *data ) if (data->net_wm_state_serial) return 0; /* another _NET_WM_STATE update is pending, wait for it to complete */ if (data->mwm_hints_serial) return 0; /* another MWM_HINT update is pending, wait for it to complete */ if (data->configure_serial) return 0; /* another config update is pending, wait for it to complete */ + /* Ignore fullscreen config changes when it's still on the same monitor. This is needed because + * adding __NET_WM_STATE_FULLSCREEN will make WMs move the window to cover exactly the monitor + * rect. If the application sets a visible rect slightly larger than the monitor rect and insists + * on changing to the rect that it previously set when the rect is changed by the WM, then the + * window rect will be repeatedly changed by the WM and the application, causing a flickering effect */ + if (data->is_fullscreen) + { + if (xinerama_get_fullscreen_monitors(&data->rects.visible, old_monitors) && + xinerama_get_fullscreen_monitors(&data->current_state.rect, monitors) && + !memcmp(old_monitors, monitors, sizeof(monitors))) + return 0; + } new_style = old_style & ~(WS_VISIBLE | WS_MINIMIZE | WS_MAXIMIZE); if (data->current_state.wm_state != WithdrawnState) new_style |= WS_VISIBLE; From 160e2fe5fb5aabe30b9943d53a194af57f402764 Mon Sep 17 00:00:00 2001 From: Ratchanan Srirattanamet Date: Tue, 12 Aug 2025 02:38:07 +0700 Subject: [PATCH 427/454] msi: Fix .NET assembly-related functionalities due to missed string copy. Commit 8cd69810596e ("msi: Get system directory just once.") remove call to GetSystemDirectoryW() in load_fusion_dlls() but forgot to add corresponding wcscpy() call. This causes loading mscoree.dll to fail, breaking .NET-assembly-related functionalities. Add it back. (cherry picked from commit 7242e15eaddc8f0619fe7e158e0f6dc8c73ad6d2) --- dlls/msi/assembly.c | 1 + 1 file changed, 1 insertion(+) diff --git a/dlls/msi/assembly.c b/dlls/msi/assembly.c index 6613bf3bf960..2452bb0b8d1d 100644 --- a/dlls/msi/assembly.c +++ b/dlls/msi/assembly.c @@ -35,6 +35,7 @@ static void load_fusion_dlls( MSIPACKAGE *package ) HRESULT (WINAPI *pLoadLibraryShim)( const WCHAR *, const WCHAR *, void *, HMODULE * ); WCHAR path[MAX_PATH]; + wcscpy(path, sysdir); lstrcpyW( path + sysdir_len, L"\\mscoree.dll" ); if (!package->hmscoree && !(package->hmscoree = LoadLibraryW( path ))) return; if (!(pLoadLibraryShim = (void *)GetProcAddress( package->hmscoree, "LoadLibraryShim" ))) From d02c91a9fcb99d7c29235767aceceafa29fc2395 Mon Sep 17 00:00:00 2001 From: Conor McCarthy Date: Thu, 11 Sep 2025 00:04:28 +1000 Subject: [PATCH 428/454] winegstreamer: HACK: Fix media source NV12 output alignment. This can go away once URL media sources go through winedmo, like local files and byte streams already do. A decoder transform is used in that case, and NV12 is fixed there. CW-Bug-Id: #25905 --- dlls/winegstreamer/main.c | 5 ++- dlls/winegstreamer/wg_parser.c | 71 +++++++++++++++++++++++++++++++++- 2 files changed, 74 insertions(+), 2 deletions(-) diff --git a/dlls/winegstreamer/main.c b/dlls/winegstreamer/main.c index c1b7a7a7bdae..0c3e31d05cb3 100644 --- a/dlls/winegstreamer/main.c +++ b/dlls/winegstreamer/main.c @@ -862,10 +862,13 @@ unsigned int wg_format_get_stride(const struct wg_format *format) return ALIGN(width * 2, 4); case WG_VIDEO_FORMAT_I420: - case WG_VIDEO_FORMAT_NV12: case WG_VIDEO_FORMAT_YV12: return ALIGN(width, 4); /* Y plane */ + /* NV12 stride in Windows has alignment 2. GStreamer output is reformatted to 2 where necessary. */ + case WG_VIDEO_FORMAT_NV12: + return ALIGN(width, 2); /* Y plane */ + case WG_VIDEO_FORMAT_UNKNOWN: FIXME("Cannot calculate stride for unknown video format.\n"); } diff --git a/dlls/winegstreamer/wg_parser.c b/dlls/winegstreamer/wg_parser.c index 9755548790d5..eeee52c39eb3 100644 --- a/dlls/winegstreamer/wg_parser.c +++ b/dlls/winegstreamer/wg_parser.c @@ -126,7 +126,7 @@ struct wg_parser_stream GstBuffer *buffer; GstMapInfo map_info; - bool flushing, eos, enabled, has_tags, has_buffer, no_more_pads; + bool flushing, eos, enabled, has_tags, has_buffer, no_more_pads, fix_nv12; uint64_t duration; gchar *tags[WG_PARSER_TAG_COUNT]; @@ -737,11 +737,29 @@ static gboolean sink_event_cb(GstPad *pad, GstObject *parent, GstEvent *event) case GST_EVENT_CAPS: { + GstStructure *structure; + GstVideoInfo video_info; + bool fix_nv12 = false; GstCaps *caps; gst_event_parse_caps(event, &caps); + structure = gst_caps_get_structure(caps, 0); + if (gst_structure_has_name(structure, "video/x-raw") && gst_video_info_from_caps(&video_info, caps)) + { + fix_nv12 = video_info.stride[0] > GST_ROUND_UP_2(video_info.width) + && GST_VIDEO_INFO_FORMAT(&video_info) == GST_VIDEO_FORMAT_NV12; + if (fix_nv12 && GST_VIDEO_INFO_IS_INTERLACED(&video_info)) + { + GST_WARNING("NV12 alignment fix is not implemented for interlaced NV12.\n"); + fix_nv12 = false; + } + if (fix_nv12) + GST_INFO("Enabling the NV12 alignment fix."); + } + pthread_mutex_lock(&parser->mutex); stream->current_caps = gst_caps_ref(caps); + stream->fix_nv12 = fix_nv12; pthread_mutex_unlock(&parser->mutex); pthread_cond_signal(&parser->init_cond); break; @@ -761,6 +779,54 @@ static gboolean sink_event_cb(GstPad *pad, GstObject *parent, GstEvent *event) return TRUE; } +static void buffer_fix_nv12(GstBuffer *buffer, GstCaps *caps) +{ + GstVideoInfo src_info, dst_info; + gint i, aligned_height; + GstMapInfo map_info; + guint8 *dst, *src; + + if (!gst_video_info_from_caps(&src_info, caps)) + { + GST_ERROR("Failed to get video info from %"GST_PTR_FORMAT, caps); + return; + } + if (!gst_buffer_map(buffer, &map_info, GST_MAP_READWRITE)) + { + GST_ERROR("Failed to map buffer."); + return; + } + + dst_info = src_info; + + aligned_height = GST_ROUND_UP_2(dst_info.height); + dst_info.stride[0] = GST_ROUND_UP_2(dst_info.width); + dst_info.stride[1] = dst_info.stride[0]; + dst_info.offset[0] = 0; + dst_info.offset[1] = dst_info.stride[0] * aligned_height; + dst_info.size = dst_info.offset[1] + dst_info.stride[0] * aligned_height / 2; + + dst = src = map_info.data; + for (i = 0; i < aligned_height; ++i) + { + memmove(dst, src, dst_info.stride[0]); + dst += dst_info.stride[0]; + src += src_info.stride[0]; + } + + dst = map_info.data + dst_info.offset[1]; + src = map_info.data + src_info.offset[1]; + for (i = 0; i < aligned_height / 2; ++i) + { + memmove(dst, src, dst_info.stride[1]); + dst += dst_info.stride[1]; + src += src_info.stride[1]; + } + + gst_buffer_unmap(buffer, &map_info); + gst_buffer_set_size(buffer, dst_info.size); +} + static GstFlowReturn sink_chain_cb(GstPad *pad, GstObject *parent, GstBuffer *buffer) { struct wg_parser_stream *stream = gst_pad_get_element_private(pad); @@ -798,6 +864,9 @@ static GstFlowReturn sink_chain_cb(GstPad *pad, GstObject *parent, GstBuffer *bu return GST_FLOW_FLUSHING; } + if (stream->fix_nv12) + buffer_fix_nv12(buffer, stream->current_caps); + if (!gst_buffer_map(buffer, &stream->map_info, GST_MAP_READ)) { pthread_mutex_unlock(&parser->mutex); From d04732930fdddf22ee22a1b4f22d4b7ac66bd6f3 Mon Sep 17 00:00:00 2001 From: Conor McCarthy Date: Wed, 9 Jul 2025 12:49:44 +1000 Subject: [PATCH 429/454] mfmediaengine: HACK: Support video output in format R10G10B10A2. CW-Bug-Id: #25515 CW-Bug-Id: #25571 --- dlls/mfmediaengine/main.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/dlls/mfmediaengine/main.c b/dlls/mfmediaengine/main.c index 49720204c161..46f1f8e15cd7 100644 --- a/dlls/mfmediaengine/main.c +++ b/dlls/mfmediaengine/main.c @@ -182,6 +182,7 @@ struct media_engine BYTE *buffer; UINT buffer_size; DXGI_FORMAT output_format; + BOOL format_mismatch; struct { @@ -1164,6 +1165,23 @@ static HRESULT media_engine_create_video_renderer(struct media_engine *engine, I return E_FAIL; } + switch (output_format) + { + case DXGI_FORMAT_R10G10B10A2_TYPELESS: + case DXGI_FORMAT_R10G10B10A2_UNORM: + case DXGI_FORMAT_R10G10B10A2_UINT: + /* IMFMediaSession doesn't support output to these formats unless the decoder supports + * MFVideoFormat_P010 output, which would allow inclusion of a suitable converter. + * The Windows H.264 decoder doesn't suppport MFVideoFormat_P010 output, and Media + * Engine apparently performs a format conversion. + * Create an 8-bit output and ensure the sampled texture is copied via a pixel shader. */ + output_format = DXGI_FORMAT_B8G8R8A8_UNORM; + engine->video_frame.format_mismatch = TRUE; + break; + default: + break; + } + memcpy(&subtype, &MFVideoFormat_Base, sizeof(subtype)); if (!(subtype.Data1 = MFMapDXGIFormatToDX9Format(output_format))) { @@ -2718,7 +2736,9 @@ static HRESULT WINAPI media_engine_TransferVideoFrame(IMFMediaEngineEx *iface, I if (SUCCEEDED(IUnknown_QueryInterface(surface, &IID_ID3D11Texture2D, (void **)&texture))) { - if (!engine->device_manager || FAILED(hr = media_engine_transfer_d3d11(engine, texture, src_rect, dst_rect, color))) + if (!engine->device_manager + || engine->video_frame.format_mismatch + || FAILED(hr = media_engine_transfer_d3d11(engine, texture, src_rect, dst_rect, color))) hr = media_engine_transfer_to_d3d11_texture(engine, texture, src_rect, dst_rect, color); ID3D11Texture2D_Release(texture); } From 268e22e4bdf309d9800dec9435e81185967926e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20K=C3=B6lbl?= Date: Tue, 16 Sep 2025 17:43:36 +0200 Subject: [PATCH 430/454] wine.inf.in: Pin Act of War: Direct Action to Windows XP. The game puts itself in XP compatibility mode on Windows. CW-Bug-Id: #25874 --- loader/wine.inf.in | 1 + 1 file changed, 1 insertion(+) diff --git a/loader/wine.inf.in b/loader/wine.inf.in index bcb1f5dc362c..70472c19bbfa 100644 --- a/loader/wine.inf.in +++ b/loader/wine.inf.in @@ -3017,6 +3017,7 @@ HKCU,Software\Wine\AppDefaults\ffxvi_demo.exe\DllOverrides,"atiadlxx",,"builtin" HKCU,Software\Wine\AppDefaults\rayne1.exe\DllOverrides,"d3d8",,"native" HKCU,Software\Wine\AppDefaults\rayne2.exe\DllOverrides,"d3d8",,"native" HKCU,Software\Wine\AppDefaults\RDR2.exe\DllOverrides,"vulkan-1",,"native" +HKCU,Software\Wine\AppDefaults\ActOfWar.exe,"Version",,"winxp" HKLM,Software\Khronos\OpenXR\1,"ActiveRuntime",,"C:\openxr\wineopenxr64.json" HKLM,Software\Wow6432Node\lucasarts entertainment company llc\Star Wars: Episode I Racer\v1.0,"Display Height",0x10001,480 HKLM,Software\Wow6432Node\lucasarts entertainment company llc\Star Wars: Episode I Racer\v1.0,"Display Width",0x10001,640 From 2768b7521fc6550b2a71c81222d62216a99532c2 Mon Sep 17 00:00:00 2001 From: Alistair Leslie-Hughes Date: Wed, 9 Jul 2025 19:32:25 +1000 Subject: [PATCH 431/454] msado15: Implement _Connection::get_version. Different windows version return differnt values. MDAC 2.8 returns 2.8 Windows 10 returns 10.0 (cherry picked from commit e7eeb27f3c6564b37f7700e6dd9706ad6f8e8826) --- dlls/msado15/connection.c | 6 ++++-- dlls/msado15/tests/msado15.c | 6 ++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/dlls/msado15/connection.c b/dlls/msado15/connection.c index d6ca0c2df397..ea9302e0b817 100644 --- a/dlls/msado15/connection.c +++ b/dlls/msado15/connection.c @@ -269,8 +269,10 @@ static HRESULT WINAPI connection_put_ConnectionTimeout( _Connection *iface, LONG static HRESULT WINAPI connection_get_Version( _Connection *iface, BSTR *str ) { - FIXME( "%p, %p\n", iface, str ); - return E_NOTIMPL; + struct connection *connection = impl_from_Connection( iface ); + TRACE( "%p, %p\n", connection, str ); + *str = SysAllocString( L"2.8" ); + return S_OK; } static HRESULT WINAPI connection_Close( _Connection *iface ) diff --git a/dlls/msado15/tests/msado15.c b/dlls/msado15/tests/msado15.c index 76e1bb35b77d..d1e22fa14342 100644 --- a/dlls/msado15/tests/msado15.c +++ b/dlls/msado15/tests/msado15.c @@ -1179,6 +1179,12 @@ if (0) /* Crashes on windows */ ok(hr == E_INVALIDARG, "Unexpected hr 0x%08lx\n", hr); } + str = NULL; + hr = _Connection_get_Version(connection, &str); + ok(hr == S_OK, "Failed to get state, hr 0x%08lx\n", hr); + ok(str != NULL, "got %p\n", str); + SysFreeString(str); + state = -1; hr = _Connection_get_State(connection, &state); ok(hr == S_OK, "Failed to get state, hr 0x%08lx\n", hr); From 403d1c1907fe3363e8e749719a98ff1a878d1a65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20K=C3=B6lbl?= Date: Sun, 14 Sep 2025 18:00:56 +0200 Subject: [PATCH 432/454] make_unicode: Add some halfwidth mapping exceptions. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Bernhard Kölbl (cherry picked from commit 61e69b8de301799e5665cfea65dbd9aa9f1e3b0b) --- dlls/kernel32/tests/locale.c | 18 ++++++++++++++++++ nls/locale.nls | Bin 771670 -> 771496 bytes tools/make_unicode | 1 + 3 files changed, 19 insertions(+) diff --git a/dlls/kernel32/tests/locale.c b/dlls/kernel32/tests/locale.c index 60fa7d99a8a2..c0cb9aeeded1 100644 --- a/dlls/kernel32/tests/locale.c +++ b/dlls/kernel32/tests/locale.c @@ -2841,6 +2841,24 @@ static void test_lcmapstring_unicode(lcmapstring_wrapper func_ptr, const char *f ok(!ret, "%s func_ptr should fail with srclen = 0\n", func_name); ok(GetLastError() == ERROR_INVALID_PARAMETER, "%s unexpected error code %ld\n", func_name, GetLastError()); + + /* test for characters which don't get mapped to their + halfwidth counterparts on LCMAP_HALFWIDTH */ + for (i = 0x2190; i <= 0x21ff; ++i) + buf[i - 0x2190] = buf2[i - 0x2190] = i; + + buf[0x70] = buf2[0x70] = 0x25cb; + ret = func_ptr(LCMAP_HALFWIDTH, buf, 0x71, buf2, 0x71); + ok(ret == 0x71, "%s ret %#x, expected value 0x71\n", func_name, ret); + ok(!memcmp(buf, buf2, sizeof(WCHAR) * 0x71), "in- and output must be equal\n"); + + /* test the other way around */ + for (i = 0xffe9; i <= 0xffee; ++i) + buf[i - 0xffe9] = buf2[i - 0xffe9] = i; + + ret = func_ptr(LCMAP_FULLWIDTH, buf, 0x6, buf2, 0x6); + ok(ret == 0x6, "%s ret %#x, expected value 0x6\n", func_name, ret); + ok(!memcmp(buf, buf2, sizeof(WCHAR) * 0x6), "in- and output must be equal\n"); } static INT LCMapStringW_wrapper(DWORD flags, LPCWSTR src, INT srclen, LPWSTR dst, INT dstlen) diff --git a/nls/locale.nls b/nls/locale.nls index e46df56c6ab31ff23090199f10f4481e263f4947..c717bb328f17a6b6242b9d12609e1a650caeeaf5 100644 GIT binary patch delta 2763 zcmccCqrakCe}V)XgB>@+ildEkt#XX5a!jpq%&l@Pt#Yiba%`<~?5%Pft#X`O<+z@u zG6gbCOXPB3-vy9YeO1v4wF8;}XU#jE5L6 zL1iB@K4SdH$iReVml9B#pGg1+MSxh5$pB4(1C#&siy2&o+cPq`E-^Cy7GapaFq_MM zdrdai0mgb3HhMrgpe=StHpQ`(0(l*5EcHl&^l%8O9V^)AJfI1rvHIW|NLmyPq!}Rs%EigwP&54$it?v{Z=Vg7GwPhHp<`~ zHV$@DP4Hl^VPC?2gC0<2K@s zBwnmaV3)uz0V}~O!CivC1g(Usgmwx260#Dm65b{JOV~=JN@SM^7Sp(qf`9tQ0?xqc T1w3r}?P--<+tVt!KllIuUFiq8 delta 2886 zcmZ3{t$(dYe}V+tIy-KLuw#vKt#XX5a!jpq%&l@Pt#Yiba%`<~?5%Pft#X`O<+z@u zGRS=(BM*}d6GV$PlMa(LlNXeS?7&E-C?Jdj;%ufWH2E&3 zwCNjDxfG`#$>0*;h`a}Z(?70Zm0)^y3q(sX`GRRVHsxCkK(PHn2G=!4W)4w?>4Mo@ z_S;`%aUEc+7i6Oc)CSt(jbu|cTN98sl})f7Nst~6LA7Hm8=d@*YAq6PVEr#R>VKrT z7>J}e8vg?u0i*eUAW}WT&e8Ngu<4&4&HCxfOSr087}@MuK}`sS?QW%9S-_SP97w4y zSA@XLs&`cx7_PEW24Av?vJ*6+9?g<`&xIbu1ca~$UQ&0)@2&N-j+GN&+? zJ6Ar}e6HJE!rb=U+1!h{A95@5MDk4J*~{~uN0v95cPj5iUP2ZM@&)qs@}1@5<@e?X zsjWxX(9FLcECCUJ&95zxEP$?tG=96laRGKgcfoeS= 0xffe8 && $src <= 0xffef; # don't remap arrows $halfwidth_table[$dst] = $src; $fullwidth_table[$src] = $dst; } From 073c3ba5c75869858aedca99145a152efeee9d8c Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 17 Sep 2025 14:49:23 -0600 Subject: [PATCH 433/454] opengl32: HACK: Do not check for extensions in wglGetProcAddress() for Warnament. CW-Bug-Id: #25960 --- dlls/opengl32/unix_wgl.c | 1 + 1 file changed, 1 insertion(+) diff --git a/dlls/opengl32/unix_wgl.c b/dlls/opengl32/unix_wgl.c index 725b6a869442..f3ff2ec1ea4e 100644 --- a/dlls/opengl32/unix_wgl.c +++ b/dlls/opengl32/unix_wgl.c @@ -585,6 +585,7 @@ static BOOL ignore_extenstions_for_get_proc_address(void) cached = (sgi = getenv( "SteamGameId" )) && ( !strcmp( sgi, "2293310" ) || !strcmp( sgi, "2914160" ) + || !strcmp( sgi, "1201700" ) ); } From a50a5a94b39605410966ee3708dd4ee10d735bb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 4 Aug 2025 16:38:04 +0200 Subject: [PATCH 434/454] winebus: Improve gamepad report compatibility with XUSB / GIP. Inverting left/right trigger usages and Y values, so that WGI doesn't need to mess with axes order. (cherry picked from commit 6d08d32ef96cd71b452af6c3f3654037086b83e9) CW-Bug-Id: #25366 CW-Bug-Id: #25325 --- dlls/windows.gaming.input/gamepad.c | 4 ++-- dlls/winebus.sys/bus_sdl.c | 11 +++++++++-- dlls/winebus.sys/bus_udev.c | 1 + dlls/winebus.sys/hid.c | 6 ++++-- dlls/winexinput.sys/main.c | 4 ++-- dlls/xinput1_3/main.c | 4 ++-- 6 files changed, 20 insertions(+), 10 deletions(-) diff --git a/dlls/windows.gaming.input/gamepad.c b/dlls/windows.gaming.input/gamepad.c index def2c9dcf5ea..ba0944cd6dae 100644 --- a/dlls/windows.gaming.input/gamepad.c +++ b/dlls/windows.gaming.input/gamepad.c @@ -322,10 +322,10 @@ static HRESULT WINAPI gamepad_GetCurrentReading( IGamepad *iface, struct Gamepad } value->LeftThumbstickX = 2. * state.axes[0] - 1.; - value->LeftThumbstickY = 1. - 2. * state.axes[1]; + value->LeftThumbstickY = 2. * state.axes[1] - 1.; value->LeftTrigger = state.axes[2]; value->RightThumbstickX = 2. * state.axes[3] - 1.; - value->RightThumbstickY = 1. - 2. * state.axes[4]; + value->RightThumbstickY = 2. * state.axes[4] - 1.; value->RightTrigger = state.axes[5]; value->Timestamp = state.timestamp; diff --git a/dlls/winebus.sys/bus_sdl.c b/dlls/winebus.sys/bus_sdl.c index b40e1244ed5f..d9420a7bf083 100644 --- a/dlls/winebus.sys/bus_sdl.c +++ b/dlls/winebus.sys/bus_sdl.c @@ -416,9 +416,13 @@ static NTSTATUS build_controller_report_descriptor(struct unix_device *iface) if (!descriptor_add_haptic(impl, FALSE)) return STATUS_NO_MEMORY; if (!hid_device_end_report_descriptor(iface)) return STATUS_NO_MEMORY; - /* Initialize axis in the report */ for (int i = SDL_CONTROLLER_AXIS_LEFTX; i < SDL_CONTROLLER_AXIS_MAX; i++) - hid_device_set_abs_axis(iface, i, pSDL_GameControllerGetAxis(impl->sdl_controller, i)); + { + int value = pSDL_GameControllerGetAxis(impl->sdl_controller, i); + if (i == SDL_CONTROLLER_AXIS_LEFTY || i == SDL_CONTROLLER_AXIS_RIGHTY) + value = -value - 1; /* match XUSB / GIP protocol */ + hid_device_set_abs_axis(iface, i, value); + } state = pSDL_GameControllerGetButton(impl->sdl_controller, SDL_CONTROLLER_BUTTON_DPAD_UP); hid_device_move_hatswitch(iface, 0, 0, state ? -1 : +1); @@ -907,6 +911,9 @@ static BOOL set_report_from_controller_event(struct sdl_device *impl, SDL_Event { SDL_ControllerAxisEvent *ie = &event->caxis; + if (ie->axis == SDL_CONTROLLER_AXIS_LEFTY || ie->axis == SDL_CONTROLLER_AXIS_RIGHTY) + ie->value = -ie->value - 1; /* match XUSB / GIP protocol */ + hid_device_set_abs_axis(iface, ie->axis, ie->value); bus_event_queue_input_report(&event_queue, iface, state->report_buf, state->report_len); break; diff --git a/dlls/winebus.sys/bus_udev.c b/dlls/winebus.sys/bus_udev.c index dd3a596c5717..2274cdfd20f8 100644 --- a/dlls/winebus.sys/bus_udev.c +++ b/dlls/winebus.sys/bus_udev.c @@ -847,6 +847,7 @@ static void set_abs_axis_value(struct unix_device *iface, int code, int value) { double scale = (code == 5 || code == 6 ? 32767.0 : 65535.0) / range; value = (value - min) * scale - (code == 5 || code == 6 ? 0 : 32768); + if (code == 2 || code == 4) value = -value - 1; /* match XUSB / GIP protocol */ } hid_device_set_abs_axis(iface, code - 1, value); diff --git a/dlls/winebus.sys/hid.c b/dlls/winebus.sys/hid.c index 693697fa8c00..bb1ddbdabdb9 100644 --- a/dlls/winebus.sys/hid.c +++ b/dlls/winebus.sys/hid.c @@ -348,12 +348,14 @@ BOOL hid_device_add_gamepad(struct unix_device *iface) static const USAGE_AND_PAGE device_usage = {.UsagePage = HID_USAGE_PAGE_GENERIC, .Usage = HID_USAGE_GENERIC_GAMEPAD}; static const USAGE left[] = {HID_USAGE_GENERIC_X, HID_USAGE_GENERIC_Y}; static const USAGE right[] = {HID_USAGE_GENERIC_RX, HID_USAGE_GENERIC_RY}; - static const USAGE trigger[] = {HID_USAGE_GENERIC_Z, HID_USAGE_GENERIC_RZ}; + static const USAGE lt = HID_USAGE_GENERIC_Z; + static const USAGE rt = HID_USAGE_GENERIC_RZ; if (!hid_device_begin_input_report(iface, &device_usage)) return FALSE; if (!hid_device_add_axes(iface, 2, HID_USAGE_PAGE_GENERIC, left, FALSE, -32768, 32767)) return FALSE; if (!hid_device_add_axes(iface, 2, HID_USAGE_PAGE_GENERIC, right, FALSE, -32768, 32767)) return FALSE; - if (!hid_device_add_axes(iface, 2, HID_USAGE_PAGE_GENERIC, trigger, FALSE, 0, 32767)) return FALSE; + if (!hid_device_add_axes(iface, 1, HID_USAGE_PAGE_GENERIC, <, FALSE, 0, 32767)) return FALSE; + if (!hid_device_add_axes(iface, 1, HID_USAGE_PAGE_GENERIC, &rt, FALSE, 0, 32767)) return FALSE; if (!hid_device_add_hatswitch(iface, 1)) return FALSE; if (!hid_device_add_buttons(iface, HID_USAGE_PAGE_BUTTON, 1, 15)) return FALSE; if (!hid_device_end_input_report(iface)) return FALSE; diff --git a/dlls/winexinput.sys/main.c b/dlls/winexinput.sys/main.c index 45c6835bfe7a..48743d6adf67 100644 --- a/dlls/winexinput.sys/main.c +++ b/dlls/winexinput.sys/main.c @@ -236,9 +236,9 @@ static void translate_report_to_xinput_state(struct func_device *fdo) fdo->xinput_state.buttons |= (1 << (usages[i] - 1)); } fdo->xinput_state.lx_axis = scale_value(lx, &fdo->lx_caps, 0, 65535); - fdo->xinput_state.ly_axis = scale_value(ly, &fdo->ly_caps, 0, 65535); + fdo->xinput_state.ly_axis = scale_value(-ly - 1, &fdo->ly_caps, 0, 65535); fdo->xinput_state.rx_axis = scale_value(rx, &fdo->rx_caps, 0, 65535); - fdo->xinput_state.ry_axis = scale_value(ry, &fdo->ry_caps, 0, 65535); + fdo->xinput_state.ry_axis = scale_value(-ry - 1, &fdo->ry_caps, 0, 65535); rt = scale_value(rt, &fdo->rt_caps, 0, 255); lt = scale_value(lt, &fdo->lt_caps, 0, 255); fdo->xinput_state.trigger = 0x8000 + (lt - rt) * 128; diff --git a/dlls/xinput1_3/main.c b/dlls/xinput1_3/main.c index dfce8312bdcf..c45f7570bd1a 100644 --- a/dlls/xinput1_3/main.c +++ b/dlls/xinput1_3/main.c @@ -622,7 +622,7 @@ static void read_controller_state(struct xinput_controller *controller) status = HidP_GetUsageValue(HidP_Input, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_Y, &value, controller->hid.preparsed, report_buf, report_len); if (status != HIDP_STATUS_SUCCESS) WARN("HidP_GetUsageValue HID_USAGE_PAGE_GENERIC / HID_USAGE_GENERIC_Y returned %#lx\n", status); - else state.Gamepad.sThumbLY = -scale_value(value, &controller->hid.ly_caps, -32768, 32767) - 1; + else state.Gamepad.sThumbLY = scale_value(value, &controller->hid.ly_caps, -32768, 32767); status = HidP_GetUsageValue(HidP_Input, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_RX, &value, controller->hid.preparsed, report_buf, report_len); if (status != HIDP_STATUS_SUCCESS) WARN("HidP_GetUsageValue HID_USAGE_PAGE_GENERIC / HID_USAGE_GENERIC_RX returned %#lx\n", status); @@ -630,7 +630,7 @@ static void read_controller_state(struct xinput_controller *controller) status = HidP_GetUsageValue(HidP_Input, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_RY, &value, controller->hid.preparsed, report_buf, report_len); if (status != HIDP_STATUS_SUCCESS) WARN("HidP_GetUsageValue HID_USAGE_PAGE_GENERIC / HID_USAGE_GENERIC_RY returned %#lx\n", status); - else state.Gamepad.sThumbRY = -scale_value(value, &controller->hid.ry_caps, -32768, 32767) - 1; + else state.Gamepad.sThumbRY = scale_value(value, &controller->hid.ry_caps, -32768, 32767); status = HidP_GetUsageValue(HidP_Input, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_RZ, &value, controller->hid.preparsed, report_buf, report_len); if (status != HIDP_STATUS_SUCCESS) WARN("HidP_GetUsageValue HID_USAGE_PAGE_GENERIC / HID_USAGE_GENERIC_RZ returned %#lx\n", status); From d2e383218659f242f13bcda5398570080bc858a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 4 Aug 2025 16:37:35 +0200 Subject: [PATCH 435/454] windows.gaming.input: Use a generic dinput device data format. To avoid the builtin axis mapping that c_dfDIJoystick2 does, we need IRawGameController to expose the axes / povs / buttons in their raw order. (cherry picked from commit 15c02e64e284d83f11b10894592e0337df62cfdc) CW-Bug-Id: #25366 CW-Bug-Id: #25325 --- dlls/windows.gaming.input/gamepad.c | 8 +- dlls/windows.gaming.input/provider.c | 156 ++++++++++++++++++++++++++- 2 files changed, 159 insertions(+), 5 deletions(-) diff --git a/dlls/windows.gaming.input/gamepad.c b/dlls/windows.gaming.input/gamepad.c index ba0944cd6dae..ecac632ec3bc 100644 --- a/dlls/windows.gaming.input/gamepad.c +++ b/dlls/windows.gaming.input/gamepad.c @@ -321,11 +321,11 @@ static HRESULT WINAPI gamepad_GetCurrentReading( IGamepad *iface, struct Gamepad break; } - value->LeftThumbstickX = 2. * state.axes[0] - 1.; - value->LeftThumbstickY = 2. * state.axes[1] - 1.; - value->LeftTrigger = state.axes[2]; + value->LeftThumbstickX = 2. * state.axes[1] - 1.; + value->LeftThumbstickY = 2. * state.axes[0] - 1.; + value->LeftTrigger = state.axes[4]; value->RightThumbstickX = 2. * state.axes[3] - 1.; - value->RightThumbstickY = 2. * state.axes[4] - 1.; + value->RightThumbstickY = 2. * state.axes[2] - 1.; value->RightTrigger = state.axes[5]; value->Timestamp = state.timestamp; diff --git a/dlls/windows.gaming.input/provider.c b/dlls/windows.gaming.input/provider.c index e5db1e81a0be..b0c54044b668 100644 --- a/dlls/windows.gaming.input/provider.c +++ b/dlls/windows.gaming.input/provider.c @@ -629,6 +629,160 @@ static void open_haptics_device( struct provider *provider ) CloseHandle( device ); } +static const DIOBJECTDATAFORMAT data_format_objs[] = +{ + {NULL,DIJOFS_X,DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_Y,DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_Z,DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_RX,DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_RY,DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_RZ,DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_SLIDER(0),DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_SLIDER(1),DIDFT_OPTIONAL|DIDFT_AXIS|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_POV(0),DIDFT_OPTIONAL|DIDFT_POV|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_POV(1),DIDFT_OPTIONAL|DIDFT_POV|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_POV(2),DIDFT_OPTIONAL|DIDFT_POV|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_POV(3),DIDFT_OPTIONAL|DIDFT_POV|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(0),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(1),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(2),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(3),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(4),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(5),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(6),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(7),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(8),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(9),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(10),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(11),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(12),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(13),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(14),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(15),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(16),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(17),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(18),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(19),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(20),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(21),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(22),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(23),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(24),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(25),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(26),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(27),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(28),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(29),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(30),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(31),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(32),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(33),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(34),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(35),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(36),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(37),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(38),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(39),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(40),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(41),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(42),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(43),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(44),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(45),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(46),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(47),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(48),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(49),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(50),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(51),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(52),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(53),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(54),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(55),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(56),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(57),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(58),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(59),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(60),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(61),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(62),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(63),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(64),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(65),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(66),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(67),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(68),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(69),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(70),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(71),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(72),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(73),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(74),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(75),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(76),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(77),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(78),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(79),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(80),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(81),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(82),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(83),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(84),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(85),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(86),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(87),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(88),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(89),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(90),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(91),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(92),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(93),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(94),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(95),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(96),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(97),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(98),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(99),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(100),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(101),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(102),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(103),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(104),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(105),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(106),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(107),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(108),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(109),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(110),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(111),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(112),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(113),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(114),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(115),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(116),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(117),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(118),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(119),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(120),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(121),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(122),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(123),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(124),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(125),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(126),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, + {NULL,DIJOFS_BUTTON(127),DIDFT_OPTIONAL|DIDFT_BUTTON|DIDFT_ANYINSTANCE}, +}; + +static const DIDATAFORMAT data_format = +{ + sizeof(DIDATAFORMAT), + sizeof(DIOBJECTDATAFORMAT), + DIDF_ABSAXIS, + sizeof(DIJOYSTATE2), + ARRAY_SIZE(data_format_objs), + (LPDIOBJECTDATAFORMAT)data_format_objs +}; + void provider_create( const WCHAR *device_path ) { IDirectInputDevice8W *dinput_device; @@ -653,7 +807,7 @@ void provider_create( const WCHAR *device_path ) if (FAILED(hr)) return; if (FAILED(hr = IDirectInputDevice8_SetCooperativeLevel( dinput_device, 0, DISCL_BACKGROUND | DISCL_NONEXCLUSIVE ))) goto done; - if (FAILED(hr = IDirectInputDevice8_SetDataFormat( dinput_device, &c_dfDIJoystick2 ))) goto done; + if (FAILED(hr = IDirectInputDevice8_SetDataFormat( dinput_device, &data_format ))) goto done; if (FAILED(hr = IDirectInputDevice8_Acquire( dinput_device ))) goto done; if (!(impl = calloc( 1, sizeof(*impl) ))) goto done; From cffa1de5fc8fe7834ce65501d15897186bb10de6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Mon, 4 Aug 2025 16:38:04 +0200 Subject: [PATCH 436/454] winebus: Use a vendor specific usage for gamepad guide buttons. To avoid exposing it with the other buttons, the XUSB / GIP frontends don't expose it to applications (for instance in Windows.Gaming.Input). (cherry picked from commit 9206673e9d4a60511ef22b6890695b053a60f7cb) CW-Bug-Id: #25366 CW-Bug-Id: #25325 --- dlls/dinput/joystick_hid.c | 2 +- dlls/winebus.sys/bus_sdl.c | 30 ++++++++++++++++-------------- dlls/winebus.sys/bus_udev.c | 18 ++++++++++-------- dlls/winebus.sys/hid.c | 3 ++- dlls/xinput1_3/main.c | 6 +++++- 5 files changed, 34 insertions(+), 25 deletions(-) diff --git a/dlls/dinput/joystick_hid.c b/dlls/dinput/joystick_hid.c index 33f03a9e2c16..6efb9acd07b3 100644 --- a/dlls/dinput/joystick_hid.c +++ b/dlls/dinput/joystick_hid.c @@ -1404,7 +1404,7 @@ static HRESULT hid_joystick_read( IDirectInputDevice8W *iface ) { usages = impl->usages_buf + count; if (usages->UsagePage != HID_USAGE_PAGE_BUTTON) - FIXME( "unimplemented usage page %x.\n", usages->UsagePage ); + WARN( "unimplemented usage page %x.\n", usages->UsagePage ); else if (usages->Usage >= 128) FIXME( "ignoring extraneous button %d.\n", usages->Usage ); else diff --git a/dlls/winebus.sys/bus_sdl.c b/dlls/winebus.sys/bus_sdl.c index d9420a7bf083..9278f1c41ef6 100644 --- a/dlls/winebus.sys/bus_sdl.c +++ b/dlls/winebus.sys/bus_sdl.c @@ -880,29 +880,31 @@ static BOOL set_report_from_controller_event(struct sdl_device *impl, SDL_Event SDL_ControllerButtonEvent *ie = &event->cbutton; int button; - switch ((button = ie->button)) + switch (ie->button) { - case SDL_CONTROLLER_BUTTON_DPAD_UP: - hid_device_move_hatswitch(iface, 0, 0, ie->state ? -1 : +1); - break; - case SDL_CONTROLLER_BUTTON_DPAD_DOWN: - hid_device_move_hatswitch(iface, 0, 0, ie->state ? +1 : -1); - break; - case SDL_CONTROLLER_BUTTON_DPAD_LEFT: - hid_device_move_hatswitch(iface, 0, ie->state ? -1 : +1, 0); - break; - case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: - hid_device_move_hatswitch(iface, 0, ie->state ? +1 : -1, 0); - break; + case SDL_CONTROLLER_BUTTON_A: button = 0; break; + case SDL_CONTROLLER_BUTTON_B: button = 1; break; + case SDL_CONTROLLER_BUTTON_X: button = 2; break; + case SDL_CONTROLLER_BUTTON_Y: button = 3; break; case SDL_CONTROLLER_BUTTON_LEFTSHOULDER: button = 4; break; case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER: button = 5; break; case SDL_CONTROLLER_BUTTON_BACK: button = 6; break; case SDL_CONTROLLER_BUTTON_START: button = 7; break; case SDL_CONTROLLER_BUTTON_LEFTSTICK: button = 8; break; case SDL_CONTROLLER_BUTTON_RIGHTSTICK: button = 9; break; - case SDL_CONTROLLER_BUTTON_GUIDE: button = 10; break; + case SDL_CONTROLLER_BUTTON_DPAD_UP: button = 10; break; + case SDL_CONTROLLER_BUTTON_DPAD_DOWN: button = 11; break; + case SDL_CONTROLLER_BUTTON_DPAD_LEFT: button = 12; break; + case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: button = 13; break; + case SDL_CONTROLLER_BUTTON_GUIDE: button = 16; break; + default: button = -1; break; } + if (button == -1) break; + if (button == 10) hid_device_move_hatswitch(iface, 0, 0, ie->state ? -1 : +1); + if (button == 11) hid_device_move_hatswitch(iface, 0, 0, ie->state ? +1 : -1); + if (button == 12) hid_device_move_hatswitch(iface, 0, ie->state ? -1 : +1, 0); + if (button == 13) hid_device_move_hatswitch(iface, 0, ie->state ? +1 : -1, 0); hid_device_set_button(iface, button, ie->state); bus_event_queue_input_report(&event_queue, iface, state->report_buf, state->report_len); break; diff --git a/dlls/winebus.sys/bus_udev.c b/dlls/winebus.sys/bus_udev.c index 2274cdfd20f8..a324f49d2d74 100644 --- a/dlls/winebus.sys/bus_udev.c +++ b/dlls/winebus.sys/bus_udev.c @@ -857,8 +857,8 @@ static void set_abs_axis_value(struct unix_device *iface, int code, int value) if (!(code = impl->hat_map[code - ABS_HAT0X])) return; if (impl->is_gamepad) { - hid_device_set_button(iface, 11, value < 0); - hid_device_set_button(iface, 12, value > 0); + hid_device_set_button(iface, 10, value < 0); + hid_device_set_button(iface, 11, value > 0); } hid_device_set_hatswitch_y(iface, code - 1, value); } @@ -867,8 +867,8 @@ static void set_abs_axis_value(struct unix_device *iface, int code, int value) if (!(code = impl->hat_map[code - ABS_HAT0X])) return; if (impl->is_gamepad) { - hid_device_set_button(iface, 13, value < 0); - hid_device_set_button(iface, 14, value > 0); + hid_device_set_button(iface, 12, value < 0); + hid_device_set_button(iface, 13, value > 0); } hid_device_set_hatswitch_x(iface, code - 1, value); } @@ -989,10 +989,10 @@ static BOOL set_report_from_event(struct unix_device *iface, struct input_event if (!(button = impl->button_map[ie->code])) return FALSE; if (impl->is_gamepad && !impl->hat_count) { - if (button == 12) hid_device_set_hatswitch_y(iface, 0, -1); - if (button == 13) hid_device_set_hatswitch_y(iface, 0, +1); - if (button == 14) hid_device_set_hatswitch_x(iface, 0, -1); - if (button == 15) hid_device_set_hatswitch_x(iface, 0, +1); + if (button == 11) hid_device_set_hatswitch_y(iface, 0, -1); + if (button == 12) hid_device_set_hatswitch_y(iface, 0, +1); + if (button == 13) hid_device_set_hatswitch_x(iface, 0, -1); + if (button == 14) hid_device_set_hatswitch_x(iface, 0, +1); } hid_device_set_button(iface, button - 1, ie->value); return FALSE; @@ -1640,6 +1640,7 @@ static NTSTATUS lnxev_device_create(struct udev_device *dev, int fd, const char if (!test_bit(info.key, button)) continue; if (impl->button_count > (impl->hat_count ? 10 : 14)) break; impl->button_map[button] = ++impl->button_count; + if (impl->hat_count && impl->button_count == 11) impl->button_map[button] = 17; } for (int i = BTN_MISC; i < KEY_MAX; i++) @@ -1648,6 +1649,7 @@ static NTSTATUS lnxev_device_create(struct udev_device *dev, int fd, const char if (impl->button_count > (impl->hat_count ? 10 : 14)) break; if (!test_bit(info.key, i)) continue; impl->button_map[i] = ++impl->button_count; + if (impl->hat_count && impl->button_count == 11) impl->button_map[i] = 17; } } diff --git a/dlls/winebus.sys/hid.c b/dlls/winebus.sys/hid.c index bb1ddbdabdb9..ee1c9a9de079 100644 --- a/dlls/winebus.sys/hid.c +++ b/dlls/winebus.sys/hid.c @@ -357,7 +357,8 @@ BOOL hid_device_add_gamepad(struct unix_device *iface) if (!hid_device_add_axes(iface, 1, HID_USAGE_PAGE_GENERIC, <, FALSE, 0, 32767)) return FALSE; if (!hid_device_add_axes(iface, 1, HID_USAGE_PAGE_GENERIC, &rt, FALSE, 0, 32767)) return FALSE; if (!hid_device_add_hatswitch(iface, 1)) return FALSE; - if (!hid_device_add_buttons(iface, HID_USAGE_PAGE_BUTTON, 1, 15)) return FALSE; + if (!hid_device_add_buttons(iface, HID_USAGE_PAGE_BUTTON, 1, 14)) return FALSE; + if (!hid_device_add_buttons(iface, HID_USAGE_PAGE_VENDOR_DEFINED_BEGIN, 1, 8)) return FALSE; if (!hid_device_end_input_report(iface)) return FALSE; return TRUE; diff --git a/dlls/xinput1_3/main.c b/dlls/xinput1_3/main.c index c45f7570bd1a..906d25d196b6 100644 --- a/dlls/xinput1_3/main.c +++ b/dlls/xinput1_3/main.c @@ -594,10 +594,14 @@ static void read_controller_state(struct xinput_controller *controller) case 8: state.Gamepad.wButtons |= XINPUT_GAMEPAD_START; break; case 9: state.Gamepad.wButtons |= XINPUT_GAMEPAD_LEFT_THUMB; break; case 10: state.Gamepad.wButtons |= XINPUT_GAMEPAD_RIGHT_THUMB; break; - case 11: state.Gamepad.wButtons |= XINPUT_GAMEPAD_GUIDE; break; } } + button_length = ARRAY_SIZE(buttons); + status = HidP_GetUsages(HidP_Input, HID_USAGE_PAGE_VENDOR_DEFINED_BEGIN, 0, buttons, &button_length, controller->hid.preparsed, report_buf, report_len); + if (status != HIDP_STATUS_SUCCESS) WARN("HidP_GetUsages HID_USAGE_PAGE_VENDOR_DEFINED_BEGIN returned %#lx\n", status); + if (button_length) state.Gamepad.wButtons |= XINPUT_GAMEPAD_GUIDE; + status = HidP_GetUsageValue(HidP_Input, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_HATSWITCH, &value, controller->hid.preparsed, report_buf, report_len); if (status != HIDP_STATUS_SUCCESS) WARN("HidP_GetUsageValue HID_USAGE_PAGE_GENERIC / HID_USAGE_GENERIC_HATSWITCH returned %#lx\n", status); else switch (value) From 16b0179e963c1a1cebbf9b4796456941fc391ad7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 9 Sep 2025 12:33:06 +0200 Subject: [PATCH 437/454] winebus: Match match gamepad dpad buttons with XUSB / GIP. (cherry picked from commit c736282109057c59b248df1f5c514b7dba02610c) --- dlls/winebus.sys/bus_sdl.c | 12 ++++++------ dlls/winebus.sys/bus_udev.c | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/dlls/winebus.sys/bus_sdl.c b/dlls/winebus.sys/bus_sdl.c index 9278f1c41ef6..259ee419c21d 100644 --- a/dlls/winebus.sys/bus_sdl.c +++ b/dlls/winebus.sys/bus_sdl.c @@ -893,18 +893,18 @@ static BOOL set_report_from_controller_event(struct sdl_device *impl, SDL_Event case SDL_CONTROLLER_BUTTON_LEFTSTICK: button = 8; break; case SDL_CONTROLLER_BUTTON_RIGHTSTICK: button = 9; break; case SDL_CONTROLLER_BUTTON_DPAD_UP: button = 10; break; - case SDL_CONTROLLER_BUTTON_DPAD_DOWN: button = 11; break; - case SDL_CONTROLLER_BUTTON_DPAD_LEFT: button = 12; break; - case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: button = 13; break; + case SDL_CONTROLLER_BUTTON_DPAD_DOWN: button = 12; break; + case SDL_CONTROLLER_BUTTON_DPAD_LEFT: button = 13; break; + case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: button = 11; break; case SDL_CONTROLLER_BUTTON_GUIDE: button = 16; break; default: button = -1; break; } if (button == -1) break; if (button == 10) hid_device_move_hatswitch(iface, 0, 0, ie->state ? -1 : +1); - if (button == 11) hid_device_move_hatswitch(iface, 0, 0, ie->state ? +1 : -1); - if (button == 12) hid_device_move_hatswitch(iface, 0, ie->state ? -1 : +1, 0); - if (button == 13) hid_device_move_hatswitch(iface, 0, ie->state ? +1 : -1, 0); + if (button == 12) hid_device_move_hatswitch(iface, 0, 0, ie->state ? +1 : -1); + if (button == 13) hid_device_move_hatswitch(iface, 0, ie->state ? -1 : +1, 0); + if (button == 11) hid_device_move_hatswitch(iface, 0, ie->state ? +1 : -1, 0); hid_device_set_button(iface, button, ie->state); bus_event_queue_input_report(&event_queue, iface, state->report_buf, state->report_len); break; diff --git a/dlls/winebus.sys/bus_udev.c b/dlls/winebus.sys/bus_udev.c index a324f49d2d74..9079fb2cf35b 100644 --- a/dlls/winebus.sys/bus_udev.c +++ b/dlls/winebus.sys/bus_udev.c @@ -858,7 +858,7 @@ static void set_abs_axis_value(struct unix_device *iface, int code, int value) if (impl->is_gamepad) { hid_device_set_button(iface, 10, value < 0); - hid_device_set_button(iface, 11, value > 0); + hid_device_set_button(iface, 12, value > 0); } hid_device_set_hatswitch_y(iface, code - 1, value); } @@ -867,8 +867,8 @@ static void set_abs_axis_value(struct unix_device *iface, int code, int value) if (!(code = impl->hat_map[code - ABS_HAT0X])) return; if (impl->is_gamepad) { - hid_device_set_button(iface, 12, value < 0); - hid_device_set_button(iface, 13, value > 0); + hid_device_set_button(iface, 13, value < 0); + hid_device_set_button(iface, 11, value > 0); } hid_device_set_hatswitch_x(iface, code - 1, value); } @@ -990,9 +990,9 @@ static BOOL set_report_from_event(struct unix_device *iface, struct input_event if (impl->is_gamepad && !impl->hat_count) { if (button == 11) hid_device_set_hatswitch_y(iface, 0, -1); - if (button == 12) hid_device_set_hatswitch_y(iface, 0, +1); - if (button == 13) hid_device_set_hatswitch_x(iface, 0, -1); - if (button == 14) hid_device_set_hatswitch_x(iface, 0, +1); + if (button == 13) hid_device_set_hatswitch_y(iface, 0, +1); + if (button == 14) hid_device_set_hatswitch_x(iface, 0, -1); + if (button == 12) hid_device_set_hatswitch_x(iface, 0, +1); } hid_device_set_button(iface, button - 1, ie->value); return FALSE; From 669a4fdd1d6597fc55fd22c1866d89a4eb4d76fd Mon Sep 17 00:00:00 2001 From: Zhiyi Zhang Date: Wed, 20 Aug 2025 20:37:54 +0800 Subject: [PATCH 438/454] win32u: Disable undecorated windows hack for CHRONO TRIGGER. CW-Bug-Id: #25747 --- dlls/win32u/defwnd.c | 1 + dlls/win32u/sysparams.c | 1 + 2 files changed, 2 insertions(+) diff --git a/dlls/win32u/defwnd.c b/dlls/win32u/defwnd.c index 67f371ac9fe4..5bc50bb083e3 100644 --- a/dlls/win32u/defwnd.c +++ b/dlls/win32u/defwnd.c @@ -1874,6 +1874,7 @@ static void handle_nc_calc_size( HWND hwnd, WPARAM wparam, RECT *win_rect ) && !(sgi && !strcmp( sgi, "2563800" )) /* Bug 23342: The Last Game */ && !(sgi && !strcmp( sgi, "1240440" )) /* Bug 23802: Halo Infinite */ && !(sgi && !strcmp( sgi, "2883280" )) /* Bug 24151: Dog Brew */ + && !(sgi && !strcmp( sgi, "613830" )) /* Bug 25747: CHRONO TRIGGER */ ) return; } diff --git a/dlls/win32u/sysparams.c b/dlls/win32u/sysparams.c index 5e2014aee180..c73cb91f9459 100644 --- a/dlls/win32u/sysparams.c +++ b/dlls/win32u/sysparams.c @@ -7372,6 +7372,7 @@ ULONG_PTR WINAPI NtUserCallTwoParam( ULONG_PTR arg1, ULONG_PTR arg2, ULONG code !((params->style & WS_POPUP) && (params->ex_style & WS_EX_TOOLWINDOW)) /* Bug 20038: game splash screens */ && !(sgi && !strcmp( sgi, "2563800" )) /* Bug 23342: The Last Game */ && !(sgi && !strcmp( sgi, "1240440" )) /* Bug 23802: Halo Infinite */ + && !(sgi && !strcmp( sgi, "613830" )) /* Bug 25747: CHRONO TRIGGER */ ) return TRUE; } From 49f88aee13a710b1993ed00242d6598747f1e77d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 19 Sep 2025 15:45:47 +0200 Subject: [PATCH 439/454] Revert "winex11: Try harder to get offscreen blit destination rectangle." This reverts commit 451aace69a7ae7cc876eac0bf489284415b928eb. --- dlls/winex11.drv/opengl.c | 12 +++--------- dlls/winex11.drv/vulkan.c | 15 ++++----------- 2 files changed, 7 insertions(+), 20 deletions(-) diff --git a/dlls/winex11.drv/opengl.c b/dlls/winex11.drv/opengl.c index cf06d27e80b6..478e3c253e5b 100644 --- a/dlls/winex11.drv/opengl.c +++ b/dlls/winex11.drv/opengl.c @@ -3301,21 +3301,15 @@ static void present_gl_drawable( HWND hwnd, HDC hdc, struct gl_drawable *gl, BOO NtUserMapWindowPoints( hwnd, toplevel, (POINT *)&rect_dst, 2, dpi ); if (IsRectEmpty( &rect_dst ) || IsRectEmpty( &gl->rect )) return; rect_dst = map_rect_virt_to_raw_for_monitor( NtUserMonitorFromWindow( toplevel, MONITOR_DEFAULTTONEAREST ), rect_dst, dpi ); - - window = get_dc_drawable( hdc, &rect ); - region = get_dc_monitor_region( hwnd, hdc ); - if ((data = get_win_data( toplevel ))) { OffsetRect( &rect_dst, data->rects.client.left - data->rects.visible.left, data->rects.client.top - data->rects.visible.top ); release_win_data( data ); } - else - { - OffsetRect( &rect_dst, rect.left, rect.top ); - WARN( "Using rect %s for other process window\n", wine_dbgstr_rect( &rect_dst ) ); - } + + window = get_dc_drawable( hdc, &rect ); + region = get_dc_monitor_region( hwnd, hdc ); if (get_dc_drawable( gl->hdc_dst, &rect ) != window || !EqualRect( &rect, &rect_dst )) set_dc_drawable( gl->hdc_dst, window, &rect_dst, IncludeInferiors ); diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c index b382651ae0bd..908066c109e1 100644 --- a/dlls/winex11.drv/vulkan.c +++ b/dlls/winex11.drv/vulkan.c @@ -341,23 +341,16 @@ static void X11DRV_vulkan_surface_presented( HWND hwnd, void *private, VkResult NtUserMapWindowPoints( hwnd, toplevel, (POINT *)&rect_dst, 2, dpi ); if (IsRectEmpty( &rect_dst ) || IsRectEmpty( &surface->rect )) return; rect_dst = map_rect_virt_to_raw_for_monitor( NtUserMonitorFromWindow( toplevel, MONITOR_DEFAULTTONEAREST ), rect_dst, dpi ); - - if (!(hdc = NtUserGetDCEx( hwnd, 0, DCX_CACHE | DCX_USESTYLE ))) return; - window = X11DRV_get_whole_window( toplevel ); - region = get_dc_monitor_region( hwnd, hdc ); - if ((data = get_win_data( toplevel ))) { OffsetRect( &rect_dst, data->rects.client.left - data->rects.visible.left, data->rects.client.top - data->rects.visible.top ); release_win_data( data ); } - else - { - get_dc_drawable( hdc, &rect ); - OffsetRect( &rect_dst, rect.left, rect.top ); - WARN( "Using rect %s for other process window\n", wine_dbgstr_rect( &rect_dst ) ); - } + + if (!(hdc = NtUserGetDCEx( hwnd, 0, DCX_CACHE | DCX_USESTYLE ))) return; + window = X11DRV_get_whole_window( toplevel ); + region = get_dc_monitor_region( hwnd, hdc ); if (get_dc_drawable( surface->hdc_dst, &rect ) != window || !EqualRect( &rect, &rect_dst )) set_dc_drawable( surface->hdc_dst, window, &rect_dst, IncludeInferiors ); From 08bcff3b17e894d2a585026158063330672b30df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 19 Sep 2025 15:46:17 +0200 Subject: [PATCH 440/454] Revert "HACK: win32u: Move AdjustWindowRect hack to only apply to syscall." This reverts commit 264abf7a378617603ee182f6249cc321db463502. --- dlls/win32u/defwnd.c | 14 ++++++++++++++ dlls/win32u/sysparams.c | 15 --------------- dlls/win32u/window.c | 2 +- 3 files changed, 15 insertions(+), 16 deletions(-) diff --git a/dlls/win32u/defwnd.c b/dlls/win32u/defwnd.c index 5bc50bb083e3..4247bb06d626 100644 --- a/dlls/win32u/defwnd.c +++ b/dlls/win32u/defwnd.c @@ -258,6 +258,20 @@ BOOL adjust_window_rect( RECT *rect, DWORD style, BOOL menu, DWORD ex_style, UIN NONCLIENTMETRICSW ncm = {.cbSize = sizeof(ncm)}; int adjust = 0; + if (user_driver->pHasWindowManager( "steamcompmgr" )) + { + /* Disable gamescope undecorated windows hack for following games. They don't expect client + * rect equals to window rect when in windowed mode. */ + const char *sgi = getenv( "SteamGameId" ); + if ( + !((style & WS_POPUP) && (ex_style & WS_EX_TOOLWINDOW)) /* Bug 20038: game splash screens */ + && !(sgi && !strcmp( sgi, "2563800" )) /* Bug 23342: The Last Game */ + && !(sgi && !strcmp( sgi, "1240440" )) /* Bug 23802: Halo Infinite */ + && !(sgi && !strcmp( sgi, "613830" )) /* Bug 25747: CHRONO TRIGGER */ + ) + return TRUE; + } + NtUserSystemParametersInfoForDpi( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0, dpi ); if ((ex_style & (WS_EX_STATICEDGE|WS_EX_DLGMODALFRAME)) == WS_EX_STATICEDGE) diff --git a/dlls/win32u/sysparams.c b/dlls/win32u/sysparams.c index c73cb91f9459..a07a413c2489 100644 --- a/dlls/win32u/sysparams.c +++ b/dlls/win32u/sysparams.c @@ -7362,21 +7362,6 @@ ULONG_PTR WINAPI NtUserCallTwoParam( ULONG_PTR arg1, ULONG_PTR arg2, ULONG code case NtUserCallTwoParam_AdjustWindowRect: { struct adjust_window_rect_params *params = (void *)arg2; - - if (user_driver->pHasWindowManager( "steamcompmgr" )) - { - /* Disable gamescope undecorated windows hack for following games. They don't expect client - * rect equals to window rect when in windowed mode. */ - const char *sgi = getenv( "SteamGameId" ); - if ( - !((params->style & WS_POPUP) && (params->ex_style & WS_EX_TOOLWINDOW)) /* Bug 20038: game splash screens */ - && !(sgi && !strcmp( sgi, "2563800" )) /* Bug 23342: The Last Game */ - && !(sgi && !strcmp( sgi, "1240440" )) /* Bug 23802: Halo Infinite */ - && !(sgi && !strcmp( sgi, "613830" )) /* Bug 25747: CHRONO TRIGGER */ - ) - return TRUE; - } - return adjust_window_rect( (RECT *)arg1, params->style, params->menu, params->ex_style, params->dpi ); } diff --git a/dlls/win32u/window.c b/dlls/win32u/window.c index 0d5666edd7af..614920f3c069 100644 --- a/dlls/win32u/window.c +++ b/dlls/win32u/window.c @@ -1907,7 +1907,7 @@ static RECT get_visible_rect( HWND hwnd, BOOL shaped, UINT style, UINT ex_style, if (IsRectEmpty( &rects->window ) || EqualRect( &rects->window, &rects->client ) || shaped || !decorated_mode) return rects->window; if (!user_driver->pGetWindowStyleMasks( hwnd, style, ex_style, &style_mask, &ex_style_mask )) return rects->window; - if (!adjust_window_rect( &rect, style & style_mask, FALSE, ex_style & ex_style_mask, dpi )) return rects->window; + if (!NtUserAdjustWindowRect( &rect, style & style_mask, FALSE, ex_style & ex_style_mask, dpi )) return rects->window; visible_rect = rects->window; visible_rect.left -= rect.left; From b7dff4aa4e272b54362d65adb0456daa4be8c2ea Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 19 Sep 2025 14:38:33 -0600 Subject: [PATCH 441/454] ntdll: HACK: Add a workaround to free more reserved memory on 32 bit. And enabled it for Surgeon Simulator: Experience Reality. CW-Bug-Id: #25966 --- dlls/ntdll/unix/loader.c | 10 ++++++++++ dlls/ntdll/unix/unix_private.h | 1 + dlls/ntdll/unix/virtual.c | 7 +++++-- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c index fe6e59057fca..33e9dc274f74 100644 --- a/dlls/ntdll/unix/loader.c +++ b/dlls/ntdll/unix/loader.c @@ -2134,6 +2134,7 @@ BOOL simulate_writecopy; BOOL wine_allocs_2g_limit; SIZE_T kernel_stack_size = 0x100000; long long ram_reporting_bias; +char *release_reserved_memory_low_bound; static void hacks_init(void) { @@ -2262,6 +2263,15 @@ static void hacks_init(void) setenv("vk_x11_override_min_image_count", "2", 0); setenv("vk_x11_strict_image_count", "true", 0); } + +#ifndef __x86_64__ + if ((env_str = getenv( "WINE_RES_MEM_LOW_BOUND" ))) + release_reserved_memory_low_bound = (void *)strtol( env_str, NULL, 0x10 ); + else if (sgi && ( + !strcmp( sgi, "518920" ) + )) + release_reserved_memory_low_bound = (void *)0x00200000; +#endif } /*********************************************************************** diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h index a2163d4e19f3..dc8c23a5aa9a 100644 --- a/dlls/ntdll/unix/unix_private.h +++ b/dlls/ntdll/unix/unix_private.h @@ -201,6 +201,7 @@ extern BOOL localsystem_sid; extern BOOL simulate_writecopy; extern long long ram_reporting_bias; extern BOOL wine_allocs_2g_limit; +extern char *release_reserved_memory_low_bound; extern void init_environment(void); extern void init_startup_info(void); diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c index 51981550a949..f9535370331b 100644 --- a/dlls/ntdll/unix/virtual.c +++ b/dlls/ntdll/unix/virtual.c @@ -5126,9 +5126,12 @@ static void free_reserved_memory( char *base, char *limit ) static void virtual_release_address_space(void) { #ifndef __APPLE__ /* On macOS, we still want to free some of low memory, for OpenGL resources */ - if (user_space_limit > (void *)limit_2g) return; + if (user_space_limit > (void *)limit_2g && !release_reserved_memory_low_bound) return; #endif - free_reserved_memory( (char *)0x20000000, (char *)0x7f000000 ); + if (release_reserved_memory_low_bound) + ERR( "HACK: release_reserved_memory_low_bound %p.\n", release_reserved_memory_low_bound ); + free_reserved_memory( release_reserved_memory_low_bound ? release_reserved_memory_low_bound : (char *)0x20000000, + (char *)0x7f000000 ); } #endif /* _WIN64 */ From 231736389bc2bf24c3c2fe06d648d7d51a47871c Mon Sep 17 00:00:00 2001 From: Hans Leidekker Date: Thu, 18 Sep 2025 08:43:04 +0200 Subject: [PATCH 442/454] wbemprox: Support WBEM_FLAG_NONSYSTEM_ONLY in class_object_Next(). (cherry picked from commit 2994dc53896df841ec8a36d8124cfe2b5affb7cf) --- dlls/wbemprox/class.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/dlls/wbemprox/class.c b/dlls/wbemprox/class.c index 19f8d79d51bd..63df0e42688c 100644 --- a/dlls/wbemprox/class.c +++ b/dlls/wbemprox/class.c @@ -276,6 +276,7 @@ struct class_object LONG refs; WCHAR *name; IEnumWbemClassObject *iter; + LONG flags; UINT index; UINT index_method; UINT index_property; @@ -519,9 +520,10 @@ static HRESULT WINAPI class_object_BeginEnumeration( TRACE( "%p, %#lx\n", iface, lEnumFlags ); - if (lEnumFlags) FIXME( "flags %#lx not supported\n", lEnumFlags ); + if (lEnumFlags & ~WBEM_FLAG_NONSYSTEM_ONLY) FIXME( "flags %#lx not supported\n", lEnumFlags ); co->index_property = 0; + co->flags = lEnumFlags; return S_OK; } @@ -549,7 +551,9 @@ static HRESULT WINAPI class_object_Next( return WBEM_E_INVALID_PARAMETER; } - view_idx_start = obj->record ? 0 : system_prop_count; + if (obj->record || (obj->flags & WBEM_FLAG_NONSYSTEM_ONLY)) view_idx_start = 0; + else view_idx_start = system_prop_count; + for (i = obj->index_property; i < table->num_cols + view_idx_start; i++) { if (i < view_idx_start) From 525f674be4a82f641623326a61e27e59484186ff Mon Sep 17 00:00:00 2001 From: Hans Leidekker Date: Thu, 18 Sep 2025 08:46:49 +0200 Subject: [PATCH 443/454] wmic: Only list non-system properties. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=58710 (cherry picked from commit d83ab3654f653d922b6eae8ad3f8a64c87f00340) --- programs/wmic/main.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/programs/wmic/main.c b/programs/wmic/main.c index 02a07f03d2eb..6ca3e6806549 100644 --- a/programs/wmic/main.c +++ b/programs/wmic/main.c @@ -287,7 +287,7 @@ static int query_prop( const WCHAR *class, int argc, WCHAR *argv[] ) IEnumWbemClassObject_Next( result, WBEM_INFINITE, 1, &obj, &count ); if (!count) break; - IWbemClassObject_BeginEnumeration( obj, 0 ); + IWbemClassObject_BeginEnumeration( obj, WBEM_FLAG_NONSYSTEM_ONLY ); while (IWbemClassObject_Next( obj, 0, &name, &v, NULL, NULL ) == S_OK) { convert_to_bstr( &v ); @@ -305,7 +305,7 @@ static int query_prop( const WCHAR *class, int argc, WCHAR *argv[] ) IEnumWbemClassObject_Next( result, WBEM_INFINITE, 1, &obj, &count ); if (count) { - IWbemClassObject_BeginEnumeration( obj, 0 ); + IWbemClassObject_BeginEnumeration( obj, WBEM_FLAG_NONSYSTEM_ONLY ); while (IWbemClassObject_Next( obj, 0, &name, NULL, NULL, NULL ) == S_OK) { output_text( name, width ); @@ -321,7 +321,7 @@ static int query_prop( const WCHAR *class, int argc, WCHAR *argv[] ) { IEnumWbemClassObject_Next( result, WBEM_INFINITE, 1, &obj, &count ); if (!count) break; - IWbemClassObject_BeginEnumeration( obj, 0 ); + IWbemClassObject_BeginEnumeration( obj, WBEM_FLAG_NONSYSTEM_ONLY ); while (IWbemClassObject_Next( obj, 0, NULL, &v, NULL, NULL ) == S_OK) { convert_to_bstr( &v ); From ef9475390a16112e528f731eff0df3bd7a6acb48 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 17 Sep 2025 10:36:57 -0600 Subject: [PATCH 444/454] nsiproxy.sys: Only enumerate active routes in ipv6_forward_enumerate_all() on Linux. (cherry picked from commit 9cfee3907a225c50eba5d1d5554f8b199ee5a2ac) --- dlls/nsiproxy.sys/ip.c | 1 + 1 file changed, 1 insertion(+) diff --git a/dlls/nsiproxy.sys/ip.c b/dlls/nsiproxy.sys/ip.c index 5bb834fee5a8..d3b250ccc98c 100644 --- a/dlls/nsiproxy.sys/ip.c +++ b/dlls/nsiproxy.sys/ip.c @@ -1817,6 +1817,7 @@ static NTSTATUS ipv6_forward_enumerate_all( void *key_data, UINT key_size, void strtoul( ptr + 1, &ptr, 16 ); /* refcount, skip */ strtoul( ptr + 1, &ptr, 16 ); /* use, skip */ rtf_flags = strtoul( ptr + 1, &ptr, 16); + if (!(rtf_flags & RTF_UP)) continue; entry.protocol = (rtf_flags & RTF_GATEWAY) ? MIB_IPPROTO_NETMGMT : MIB_IPPROTO_LOCAL; entry.loopback = entry.protocol == MIB_IPPROTO_LOCAL && entry.prefix_len == 32; From 4baa3dbd52f71b8b4350da0c3931c31e1088c862 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 17 Sep 2025 10:37:47 -0600 Subject: [PATCH 445/454] nsiproxy.sys: Improve loopback detection in ipv6_forward_enumerate_all() on Linux. (cherry picked from commit d5f76b03b0f2842296ff9bff7e53d50c4fc4a849) --- dlls/nsiproxy.sys/ip.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/nsiproxy.sys/ip.c b/dlls/nsiproxy.sys/ip.c index d3b250ccc98c..cc14871c7503 100644 --- a/dlls/nsiproxy.sys/ip.c +++ b/dlls/nsiproxy.sys/ip.c @@ -1819,7 +1819,7 @@ static NTSTATUS ipv6_forward_enumerate_all( void *key_data, UINT key_size, void rtf_flags = strtoul( ptr + 1, &ptr, 16); if (!(rtf_flags & RTF_UP)) continue; entry.protocol = (rtf_flags & RTF_GATEWAY) ? MIB_IPPROTO_NETMGMT : MIB_IPPROTO_LOCAL; - entry.loopback = entry.protocol == MIB_IPPROTO_LOCAL && entry.prefix_len == 32; + entry.loopback = entry.prefix_len == 128 && IN6_IS_ADDR_LOOPBACK(&entry.prefix); while (isspace( *ptr )) ptr++; end = ptr; From 38dc9f9ddbfa634f132be010049d3d9ab090da8a Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 17 Sep 2025 10:42:04 -0600 Subject: [PATCH 446/454] iphlpapi: Fully zero init address in unicast_row_fill(). (cherry picked from commit 79fd4c41b225c1941789ffde6ad5afc294d151fd) --- dlls/iphlpapi/iphlpapi_main.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/dlls/iphlpapi/iphlpapi_main.c b/dlls/iphlpapi/iphlpapi_main.c index 01d1c4c318a4..320c1efa6449 100644 --- a/dlls/iphlpapi/iphlpapi_main.c +++ b/dlls/iphlpapi/iphlpapi_main.c @@ -3654,19 +3654,16 @@ static void unicast_row_fill( MIB_UNICASTIPADDRESS_ROW *row, USHORT fam, void *k struct nsi_ipv4_unicast_key *key4 = (struct nsi_ipv4_unicast_key *)key; struct nsi_ipv6_unicast_key *key6 = (struct nsi_ipv6_unicast_key *)key; + memset( &row->Address, 0, sizeof(row->Address) ); if (fam == AF_INET) { row->Address.Ipv4.sin_family = fam; - row->Address.Ipv4.sin_port = 0; row->Address.Ipv4.sin_addr = key4->addr; - memset( row->Address.Ipv4.sin_zero, 0, sizeof(row->Address.Ipv4.sin_zero) ); row->InterfaceLuid.Value = key4->luid.Value; } else { row->Address.Ipv6.sin6_family = fam; - row->Address.Ipv6.sin6_port = 0; - row->Address.Ipv6.sin6_flowinfo = 0; row->Address.Ipv6.sin6_addr = key6->addr; row->Address.Ipv6.sin6_scope_id = dyn->scope_id; row->InterfaceLuid.Value = key6->luid.Value; From 83a6e4d030d7ed809d2dca46863489364da134ce Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 11 Sep 2025 18:55:29 -0600 Subject: [PATCH 447/454] iphlpapi: Implement GetBestRoute2(). (cherry picked from commit 52eb54849c96a776d5202cfa9135267f31f433cd) --- dlls/iphlpapi/iphlpapi_main.c | 195 ++++++++++++++++++++++++++++++++-- include/netioapi.h | 3 + 2 files changed, 187 insertions(+), 11 deletions(-) diff --git a/dlls/iphlpapi/iphlpapi_main.c b/dlls/iphlpapi/iphlpapi_main.c index 320c1efa6449..9ec2ecdaa2c0 100644 --- a/dlls/iphlpapi/iphlpapi_main.c +++ b/dlls/iphlpapi/iphlpapi_main.c @@ -4659,24 +4659,197 @@ DWORD WINAPI GetIpInterfaceEntry( MIB_IPINTERFACE_ROW *row ) } +static BOOL match_ip_address_with_prefix( const SOCKADDR_INET *addr, const IP_ADDRESS_PREFIX *pfx ) +{ + const BYTE *p1, *p2; + unsigned int len; + BYTE mask; + + if (addr->si_family != pfx->Prefix.si_family) return FALSE; + if (!(len = pfx->PrefixLength)) return TRUE; + + if (addr->si_family == AF_INET6) + { + if (len > 128) return FALSE; + p1 = (const BYTE *)&addr->Ipv6.sin6_addr; + p2 = (const BYTE *)&pfx->Prefix.Ipv6.sin6_addr; + } + else + { + if (len > 32) return FALSE; + p1 = (const BYTE *)&addr->Ipv4.sin_addr; + p2 = (const BYTE *)&pfx->Prefix.Ipv4.sin_addr; + } + if (memcmp( p1, p2, len / 8 )) return FALSE; + mask = 0xffu << (8 - (len % 8)); + if (!mask) return TRUE; + return (p1[len / 8] & mask) == (p2[len / 8] & mask); +} + +static MIB_UNICASTIPADDRESS_ROW *find_first_matching_unicast_addr( MIB_UNICASTIPADDRESS_TABLE *uni, BOOL v6, + const SOCKADDR_INET *src, const SOCKADDR_INET *dst, + NET_IFINDEX if_index ) +{ + unsigned int i; + + for (i = 0; i < uni->NumEntries; ++i) + { + if (if_index && uni->Table[i].InterfaceIndex != if_index) continue; + if (src) + { + if (v6) + { + if (src->Ipv6.sin6_scope_id && IN6_IS_ADDR_LINKLOCAL(&src->Ipv6.sin6_addr) + && src->Ipv6.sin6_scope_id != uni->Table[i].InterfaceIndex) continue; + if (memcmp( &src->Ipv6.sin6_addr, &uni->Table[i].Address.Ipv6.sin6_addr, sizeof(src->Ipv6.sin6_addr) )) + continue; + } + else if (src->Ipv4.sin_addr.s_addr != uni->Table[i].Address.Ipv4.sin_addr.s_addr) continue; + } + if (v6 && dst) + { + if (IN6_IS_ADDR_LINKLOCAL(&dst->Ipv6.sin6_addr) + != IN6_IS_ADDR_LINKLOCAL(&uni->Table[i].Address.Ipv6.sin6_addr)) + continue; + } + return &uni->Table[i]; + } + return NULL; +} + /****************************************************************** * GetBestRoute2 (IPHLPAPI.@) */ -DWORD WINAPI GetBestRoute2(NET_LUID *luid, NET_IFINDEX index, - const SOCKADDR_INET *source, const SOCKADDR_INET *destination, - ULONG options, PMIB_IPFORWARD_ROW2 bestroute, - SOCKADDR_INET *bestaddress) -{ - static int once; +DWORD WINAPI GetBestRoute2( NET_LUID *luid, NET_IFINDEX index, + const SOCKADDR_INET *src, const SOCKADDR_INET *dst, + ULONG options, PMIB_IPFORWARD_ROW2 bestroute, + SOCKADDR_INET *bestaddress ) +{ + MIB_UNICASTIPADDRESS_ROW *uni_row = NULL; + MIB_UNICASTIPADDRESS_TABLE *uni; + MIB_IPFORWARD_TABLE2 *fwd; + unsigned int i, best_idx; + int max_prefix_len; + DWORD ret; + BOOL v6; + + TRACE( "(%p, %ld, %p, %p, 0x%08lx, %p, %p).\n", luid, index, src, dst, options, bestroute, bestaddress ); + + if (!dst || !bestroute || !bestaddress) + return ERROR_INVALID_PARAMETER; + + memset( bestroute, 0, sizeof(*bestroute) ); + memset( bestaddress, 0, sizeof(*bestaddress) ); - if (!once++) - FIXME("(%p, %ld, %p, %p, 0x%08lx, %p, %p): stub\n", luid, index, source, - destination, options, bestroute, bestaddress); + if (dst->si_family != AF_INET && dst->si_family != AF_INET6) return ERROR_INVALID_PARAMETER; + v6 = dst->si_family == AF_INET6; + if (src) + { + if (src->si_family != dst->si_family) src = NULL; + else if (v6 && IN6_IS_ADDR_UNSPECIFIED(&src->Ipv6.sin6_addr)) src = NULL; + else if (!v6 && !src->Ipv4.sin_addr.s_addr) src = NULL; + } - if (!destination || !bestroute || !bestaddress) + if (v6 && !IN6_IS_ADDR_LINKLOCAL(&dst->Ipv6.sin6_addr) && dst->Ipv6.sin6_scope_id) return ERROR_INVALID_PARAMETER; + if (v6 && src && !IN6_IS_ADDR_LINKLOCAL(&src->Ipv6.sin6_addr) && src->Ipv6.sin6_scope_id) + return ERROR_INVALID_PARAMETER; + + if ((ret = GetUnicastIpAddressTable( dst->si_family, &uni ))) return ret; + if ((ret = GetIpForwardTable2( dst->si_family, &fwd ))) + { + FreeMibTable( &uni ); + return ret; + } - return ERROR_NOT_SUPPORTED; + if (!luid && index) + { + for (i = 0; i < uni->NumEntries; ++i) + { + if (uni->Table[i].InterfaceIndex == index) break; + } + if (i == uni->NumEntries) + { + ret = ERROR_FILE_NOT_FOUND; + goto done; + } + luid = &uni->Table[i].InterfaceLuid; + } + if (src && !(uni_row = find_first_matching_unicast_addr( uni, v6, src, NULL, 0 ))) + { + ret = ERROR_NOT_FOUND; + goto done; + } + if (!uni_row && luid && luid->Value) + { + for (i = 0; i < uni->NumEntries; ++i) + { + if (uni->Table[i].InterfaceLuid.Value == luid->Value) break; + } + if (i == uni->NumEntries) + { + ret = ERROR_NOT_FOUND; + goto done; + } + uni_row = &uni->Table[i]; + } + + if (uni_row) index = uni_row->InterfaceIndex; + else index = 0; + + if (v6) + { + if (!index) index = dst->Ipv6.sin6_scope_id; + if (IN6_IS_ADDR_LINKLOCAL(&dst->Ipv6.sin6_addr) && dst->Ipv6.sin6_scope_id + && dst->Ipv6.sin6_scope_id != index) + { + ret = ERROR_INVALID_PARAMETER; + goto done; + } + } + + max_prefix_len = -1; + best_idx = 0; + for (i = 0; i < fwd->NumEntries; ++i) + { + if (index && fwd->Table[i].InterfaceIndex != index) continue; + + if (match_ip_address_with_prefix( dst, &fwd->Table[i].DestinationPrefix ) + && max_prefix_len < fwd->Table[i].DestinationPrefix.PrefixLength ) + { + max_prefix_len = fwd->Table[i].DestinationPrefix.PrefixLength; + best_idx = i; + } + } + + if (max_prefix_len == -1) + { + ret = ERROR_NETWORK_UNREACHABLE; + goto done; + } + index = fwd->Table[best_idx].InterfaceIndex; + if (!fwd->Table[best_idx].Loopback && ((v6 && IN6_IS_ADDR_LOOPBACK(&dst->Ipv6.sin6_addr)) + || (!v6 && dst->Ipv4.sin_addr.S_un.S_un_b.s_b1 == 127))) + { + ret = ERROR_INVALID_PARAMETER; + goto done; + } + + if (!(uni_row = find_first_matching_unicast_addr( uni, v6, src, dst, index ))) + { + WARN( "Could not find source address.\n" ); + ret = ERROR_NOT_FOUND; + goto done; + } + *bestaddress = uni_row->Address; + *bestroute = fwd->Table[best_idx]; + + ret = ERROR_SUCCESS; +done: + FreeMibTable( fwd ); + FreeMibTable( uni ); + TRACE( "-> %lu.\n", ret ); + return ret; } /****************************************************************** diff --git a/include/netioapi.h b/include/netioapi.h index 09824f946e4b..9994e5b19ac6 100644 --- a/include/netioapi.h +++ b/include/netioapi.h @@ -269,6 +269,9 @@ IPHLPAPI_DLL_LINKAGE DWORD WINAPI ConvertInterfaceNameToLuidW(const WCHAR*,NET_L IPHLPAPI_DLL_LINKAGE DWORD WINAPI ConvertLengthToIpv4Mask(ULONG,ULONG*); IPHLPAPI_DLL_LINKAGE void WINAPI FreeMibTable(void*); IPHLPAPI_DLL_LINKAGE DWORD WINAPI GetAnycastIpAddressTable(ADDRESS_FAMILY,MIB_ANYCASTIPADDRESS_TABLE**); +IPHLPAPI_DLL_LINKAGE DWORD WINAPI GetBestRoute2(NET_LUID *luid, NET_IFINDEX index, const SOCKADDR_INET *source, + const SOCKADDR_INET *destination, ULONG options, + PMIB_IPFORWARD_ROW2 bestroute, SOCKADDR_INET *bestaddress); IPHLPAPI_DLL_LINKAGE NET_IF_COMPARTMENT_ID WINAPI GetCurrentThreadCompartmentId(void); IPHLPAPI_DLL_LINKAGE DWORD WINAPI GetIfEntry2(MIB_IF_ROW2*); IPHLPAPI_DLL_LINKAGE DWORD WINAPI GetIfEntry2Ex(MIB_IF_TABLE_LEVEL,MIB_IF_ROW2*); From ca94ebf9b9d7ce556f1ca8f52d426227071025c6 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 17 Sep 2025 10:39:49 -0600 Subject: [PATCH 448/454] iphlpapi: Reimplement GetBestInterfaceEx() on top of GetBestRoute2(). (cherry picked from commit 632d9363940a52f5c56e7bbdc7e8e5675e5a86d1) --- dlls/iphlpapi/iphlpapi_main.c | 30 ++++++++++++------------------ include/iphlpapi.h | 6 +++--- 2 files changed, 15 insertions(+), 21 deletions(-) diff --git a/dlls/iphlpapi/iphlpapi_main.c b/dlls/iphlpapi/iphlpapi_main.c index 9ec2ecdaa2c0..65aebc6c394b 100644 --- a/dlls/iphlpapi/iphlpapi_main.c +++ b/dlls/iphlpapi/iphlpapi_main.c @@ -1389,27 +1389,21 @@ DWORD WINAPI GetBestInterface(IPAddr dwDestAddr, PDWORD pdwBestIfIndex) * Success: NO_ERROR * Failure: error code from winerror.h */ -DWORD WINAPI GetBestInterfaceEx(struct sockaddr *pDestAddr, PDWORD pdwBestIfIndex) +DWORD WINAPI GetBestInterfaceEx( struct sockaddr *dst, DWORD *best_index ) { - DWORD ret; + SOCKADDR_INET best_address; + MIB_IPFORWARD_ROW2 row; + DWORD ret; - TRACE("pDestAddr %p, pdwBestIfIndex %p\n", pDestAddr, pdwBestIfIndex); - if (!pDestAddr || !pdwBestIfIndex) - ret = ERROR_INVALID_PARAMETER; - else { - MIB_IPFORWARDROW ipRow; + TRACE( "dst %p, best_index %p\n", dst, best_index ); - if (pDestAddr->sa_family == AF_INET) { - ret = GetBestRoute(((struct sockaddr_in *)pDestAddr)->sin_addr.S_un.S_addr, 0, &ipRow); - if (ret == ERROR_SUCCESS) - *pdwBestIfIndex = ipRow.dwForwardIfIndex; - } else { - FIXME("address family %d not supported\n", pDestAddr->sa_family); - ret = ERROR_NOT_SUPPORTED; - } - } - TRACE("returning %ld\n", ret); - return ret; + if (!dst || !best_index) return ERROR_INVALID_PARAMETER; + + ret = GetBestRoute2( NULL, 0, NULL, (const SOCKADDR_INET *)dst, 0, &row, &best_address ); + if (!ret) *best_index = row.InterfaceIndex; + + TRACE( "returning %ld\n", ret ); + return ret; } diff --git a/include/iphlpapi.h b/include/iphlpapi.h index 84b9b7db34cb..5794f6b2a68b 100644 --- a/include/iphlpapi.h +++ b/include/iphlpapi.h @@ -95,11 +95,11 @@ IPHLPAPI_DLL_LINKAGE DWORD WINAPI GetBestInterface(IPAddr dwDestAddr, PDWORD pdw #ifdef __WINE_WINSOCKAPI_STDLIB_H IPHLPAPI_DLL_LINKAGE DWORD WINAPI GetBestInterfaceEx( #ifdef USE_WS_PREFIX - struct WS_sockaddr *pDestAddr, + struct WS_sockaddr *dst, #else - struct sockaddr *pDestAddr, + struct sockaddr *dst, #endif - PDWORD pdwBestIfIndex); + DWORD *best_index); #endif IPHLPAPI_DLL_LINKAGE DWORD WINAPI GetBestRoute(DWORD dwDestAddr, DWORD dwSourceAddr, PMIB_IPFORWARDROW pBestRoute); From 69d249126df78415b4a736100841ca4e04e03540 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 17 Sep 2025 10:34:38 -0600 Subject: [PATCH 449/454] iphlpapi/tests: Add tests for best routes. (cherry picked from commit 21ee481e11ccf3be0d0a7ae8aa15238ef2761092) --- dlls/iphlpapi/tests/iphlpapi.c | 485 +++++++++++++++++++++++++++++++++ 1 file changed, 485 insertions(+) diff --git a/dlls/iphlpapi/tests/iphlpapi.c b/dlls/iphlpapi/tests/iphlpapi.c index ebd69a37ad89..49c3059b5df8 100644 --- a/dlls/iphlpapi/tests/iphlpapi.c +++ b/dlls/iphlpapi/tests/iphlpapi.c @@ -3488,6 +3488,490 @@ static void test_GetIpInterface(void) FreeMibTable( if_table ); } +static MIB_IPFORWARD_ROW2 *find_ipforward_row( MIB_IPFORWARD_TABLE2 *table, DWORD ifindex ) +{ + unsigned int i; + + for (i = 0; i < table->NumEntries; ++i) + { + if (ifindex == table->Table[i].InterfaceIndex) return &table->Table[i]; + } + return NULL; +} + +static void test_best_routes(void) +{ + static const IN6_ADDR link_local_prefix = {{ IN6ADDR_LINKLOCALPREFIX_INIT }}; + + DWORD ret, index, link_local_index = ~0u, loopback_index = ~0u, default_route_index = ~0u; + struct sockaddr_in6 dst6, src6, best6, link_local_addr6, global_addr6; + struct sockaddr_in dst4, src4, global_addr4; + NET_LUID link_local_luid = { 0 }, luid; + MIB_UNICASTIPADDRESS_ROW uni_row; + MIB_IPFORWARD_ROW2 fwd_row, *r; + MIB_IPFORWARD_TABLE2 *table; + char s[256], s2[256]; + SOCKADDR_INET best4; + unsigned int i; + + memset( &dst6, 0, sizeof(src6) ); + dst6.sin6_family = AF_INET6; + ret = inet_pton( AF_INET6, "::1", &dst6.sin6_addr); + ok(ret, "got error %u.\n", WSAGetLastError()); + ret = GetBestRoute2( NULL, 0, NULL, (SOCKADDR_INET *)&dst6, 0, NULL, (SOCKADDR_INET *)&best6 ); + ok( ret == ERROR_INVALID_PARAMETER, "got error %lu.\n", ret ); + ret = GetBestRoute2( NULL, 0, NULL, NULL, 0, &fwd_row, (SOCKADDR_INET *)&best6 ); + ok( ret == ERROR_INVALID_PARAMETER, "got error %lu.\n", ret ); + ret = GetBestRoute2( NULL, 0, NULL, (SOCKADDR_INET *)&dst6, 0, &fwd_row, NULL ); + ok( ret == ERROR_INVALID_PARAMETER, "got error %lu.\n", ret ); + + ret = GetIpForwardTable2( AF_INET6, &table ); + ok( !ret, "got error %lu.\n", ret ); + + for (i = 0; i < table->NumEntries; ++i) + { + if (IN6_IS_ADDR_LINKLOCAL(&table->Table[i].DestinationPrefix.Prefix.Ipv6.sin6_addr)) + { + if (link_local_index == ~0u) + { + link_local_index = table->Table[i].InterfaceIndex; + link_local_luid = table->Table[i].InterfaceLuid; + } + } + if (IN6_IS_ADDR_LOOPBACK(&table->Table[i].DestinationPrefix.Prefix.Ipv6.sin6_addr)) + loopback_index = table->Table[i].InterfaceIndex; + } + ok( link_local_index != ~0u, "could not find any link local route.\n" ); + ok( loopback_index != ~0u, "could not find loopback route.\n" ); + + /* Test with link local address. */ + memset( &dst6, 0, sizeof(dst6) ); + dst6.sin6_family = AF_INET6; + dst6.sin6_addr = link_local_prefix; + dst6.sin6_scope_id = link_local_index; + dst6.sin6_addr.u.Byte[15] = 1; + ret = GetBestInterfaceEx( (struct sockaddr *)&dst6, &index ); + ok( !ret, "got error %lu.\n", ret ); + ok( index == link_local_index, "got %lu, expected %lu.\n", index, link_local_index ); + + memset( &fwd_row, 0xcc, sizeof(fwd_row) ); + ret = GetBestRoute2( NULL, 0xdeadbeef, NULL, (SOCKADDR_INET *)&dst6, 0, &fwd_row, (SOCKADDR_INET *)&best6 ); + ok( ret == ERROR_FILE_NOT_FOUND, "got error %lu.\n", ret ); + ok( !fwd_row.InterfaceIndex, "got %#lx.\n", fwd_row.InterfaceIndex ); + + memset( &fwd_row, 0xcc, sizeof(fwd_row) ); + dst6.sin6_scope_id = loopback_index; + ret = GetBestRoute2( NULL, link_local_index, NULL, (SOCKADDR_INET *)&dst6, 0, &fwd_row, (SOCKADDR_INET *)&best6 ); + ok( ret == ERROR_INVALID_PARAMETER, "got error %lu.\n", ret ); + ok( !fwd_row.InterfaceIndex, "got %#lx.\n", fwd_row.InterfaceIndex ); + + memset( &fwd_row, 0xcc, sizeof(fwd_row) ); + dst6.sin6_scope_id = link_local_index; + ret = GetBestRoute2( NULL, loopback_index, NULL, (SOCKADDR_INET *)&dst6, 0, &fwd_row, (SOCKADDR_INET *)&best6 ); + ok( ret == ERROR_INVALID_PARAMETER, "got error %lu.\n", ret ); + ok( !fwd_row.InterfaceIndex, "got %#lx.\n", fwd_row.InterfaceIndex ); + + memset( &fwd_row, 0xcc, sizeof(fwd_row) ); + dst6.sin6_scope_id = 0xdeadbeef; + ret = GetBestRoute2( NULL, link_local_index, NULL, (SOCKADDR_INET *)&dst6, 0, &fwd_row, (SOCKADDR_INET *)&best6 ); + ok( ret == ERROR_INVALID_PARAMETER, "got error %lu.\n", ret ); + ok( !fwd_row.InterfaceIndex, "got %#lx.\n", fwd_row.InterfaceIndex ); + + dst6.sin6_scope_id = link_local_index; + + memset( &fwd_row, 0xcc, sizeof(fwd_row) ); + memset( &best6, 0xcc, sizeof(best6) ); + ret = GetBestRoute2( NULL, link_local_index, NULL, (SOCKADDR_INET *)&dst6, 0, &fwd_row, (SOCKADDR_INET *)&best6 ); + ok( !ret, "got error %lu.\n", ret ); + ok( fwd_row.InterfaceIndex == link_local_index, "got %lu, expected %lu.\n", fwd_row.InterfaceIndex, link_local_index ); + ok( !fwd_row.Loopback, "got %d.\n", fwd_row.Loopback ); + ok( IN6_IS_ADDR_LINKLOCAL(&fwd_row.DestinationPrefix.Prefix.Ipv6.sin6_addr), "expected link local prefix.\n" ); + ok( IN6_IS_ADDR_UNSPECIFIED(&fwd_row.NextHop.Ipv6.sin6_addr), "expected unspecifed address.\n" ); + ok( best6.sin6_family == AF_INET6, "got %u.\n", best6.sin6_family ); + ok( best6.sin6_scope_id == link_local_index, "got %lu, expected %lu.\n", best6.sin6_scope_id, link_local_index ); + memset( &uni_row, 0, sizeof(uni_row) ); + *(struct sockaddr_in6 *)&uni_row.Address = best6; + uni_row.InterfaceIndex = fwd_row.InterfaceIndex; + ret = GetUnicastIpAddressEntry( &uni_row ); + ok( !ret, "got error %lu.\n", ret ); + ok( !memcmp( &uni_row.Address, &best6, sizeof(best6) ), "got different address.\n" ); + link_local_addr6 = best6; + + memset( &fwd_row, 0xcc, sizeof(fwd_row) ); + dst6.sin6_scope_id = link_local_index; + ret = GetBestRoute2( &link_local_luid, 0xdeadbeef, NULL, (SOCKADDR_INET *)&dst6, 0, &fwd_row, + (SOCKADDR_INET *)&best6 ); + ok( !ret, "got error %lu.\n", ret ); + ok( fwd_row.InterfaceIndex == link_local_index, "got %lu, expected %lu.\n", fwd_row.InterfaceIndex, link_local_index ); + ok( IN6_IS_ADDR_UNSPECIFIED(&fwd_row.NextHop.Ipv6.sin6_addr), "expected unspecifed address.\n" ); + + dst6.sin6_scope_id = link_local_index; + memset( &fwd_row, 0xcc, sizeof(fwd_row) ); + ret = GetBestRoute2( NULL, 0, NULL, (SOCKADDR_INET *)&dst6, 0, &fwd_row, (SOCKADDR_INET *)&best6 ); + ok( !ret, "got error %lu.\n", ret ); + ok( fwd_row.InterfaceIndex == link_local_index, "got %lu, expected %lu.\n", fwd_row.InterfaceIndex, link_local_index ); + ok( !fwd_row.Loopback, "got %d.\n", fwd_row.Loopback ); + + dst6.sin6_scope_id = 0; + memset( &fwd_row, 0xcc, sizeof(fwd_row) ); + ret = GetBestRoute2( NULL, link_local_index, NULL, (SOCKADDR_INET *)&dst6, 0, &fwd_row, (SOCKADDR_INET *)&best6 ); + ok( !ret, "got error %lu.\n", ret ); + ok( fwd_row.InterfaceIndex == link_local_index, "got %lu, expected %lu.\n", fwd_row.InterfaceIndex, link_local_index ); + ok( !fwd_row.Loopback, "got %d.\n", fwd_row.Loopback ); + ok( best6.sin6_scope_id == link_local_index, "got %lu, expected %lu.\n", best6.sin6_scope_id, link_local_index ); + ok( IN6_IS_ADDR_UNSPECIFIED(&fwd_row.NextHop.Ipv6.sin6_addr), "expected unspecifed address.\n" ); + + /* With both scope_id in destination address and interface unspecified Windows returns a row for a random + * matching route, not even necessarily a link local one. */ + dst6.sin6_scope_id = 0; + memset( &fwd_row, 0xcc, sizeof(fwd_row) ); + ret = GetBestRoute2( NULL, 0, NULL, (SOCKADDR_INET *)&dst6, 0, &fwd_row, (SOCKADDR_INET *)&best6 ); + ok( !ret, "got error %lu.\n", ret ); + r = find_ipforward_row( table, fwd_row.InterfaceIndex ); + ok( !!r, "got NULL.\n" ); + ok( best6.sin6_scope_id == fwd_row.InterfaceIndex, "got %lu, expected %lu.\n", best6.sin6_scope_id, + fwd_row.InterfaceIndex ); + ok( IN6_IS_ADDR_UNSPECIFIED(&fwd_row.NextHop.Ipv6.sin6_addr), "expected unspecifed address.\n" ); + + dst6.sin6_scope_id = 0; + ret = GetBestInterfaceEx( (struct sockaddr *)&dst6, &index ); + ok( !ret, "got error %lu.\n", ret ); + r = find_ipforward_row( table, index ); + ok( !!r, "got NULL.\n" ); + + memset( &src6, 0, sizeof(src6) ); + src6.sin6_family = AF_INET6; + ret = inet_pton( AF_INET6, "dead:beaf:dead:beaf::beaf", &src6.sin6_addr); + ok(ret, "got error %u.\n", WSAGetLastError()); + dst6.sin6_scope_id = 0; + ret = GetBestRoute2( NULL, 0, (SOCKADDR_INET *)&src6, (SOCKADDR_INET *)&dst6, 0, &fwd_row, (SOCKADDR_INET *)&best6 ); + ok( ret == ERROR_NOT_FOUND, "got error %lu.\n", ret ); + + ret = GetBestRoute2( NULL, 0, (SOCKADDR_INET *)&link_local_addr6, (SOCKADDR_INET *)&dst6, 0, &fwd_row, + (SOCKADDR_INET *)&best6 ); + ok( !ret, "got error %lu.\n", ret ); + ok( best6.sin6_scope_id == link_local_index, "got %lu, expected %lu.\n", best6.sin6_scope_id, link_local_index ); + + dst6.sin6_scope_id = 0; + ret = GetBestRoute2( &link_local_luid, 0, (SOCKADDR_INET *)&src6, (SOCKADDR_INET *)&dst6, 0, &fwd_row, + (SOCKADDR_INET *)&best6 ); + ok( ret == ERROR_NOT_FOUND, "got error %lu.\n", ret ); + + dst6.sin6_scope_id = link_local_index; + ret = GetBestRoute2( &link_local_luid, 0, (SOCKADDR_INET *)&link_local_addr6, (SOCKADDR_INET *)&dst6, 0, &fwd_row, + (SOCKADDR_INET *)&best6 ); + ok( !ret, "got error %lu.\n", ret ); + ok( best6.sin6_scope_id == link_local_index, "got %lu, expected %lu.\n", best6.sin6_scope_id, link_local_index ); + + /* src address with non-matching or unknown family is ignored. */ + src6.sin6_family = AF_INET; + ret = GetBestRoute2( NULL, 0, (SOCKADDR_INET *)&src6, (SOCKADDR_INET *)&dst6, 0, &fwd_row, (SOCKADDR_INET *)&best6 ); + ok( !ret, "got error %lu.\n", ret ); + src6.sin6_family = 0xbeed; + ret = GetBestRoute2( NULL, 0, (SOCKADDR_INET *)&src6, (SOCKADDR_INET *)&dst6, 0, &fwd_row, (SOCKADDR_INET *)&best6 ); + ok( !ret, "got error %lu.\n", ret ); + + /* zero src address is ignored. */ + memset( &src6, 0, sizeof(src6) ); + src6.sin6_family = AF_INET6; + ret = GetBestRoute2( NULL, 0, (SOCKADDR_INET *)&src6, (SOCKADDR_INET *)&dst6, 0, &fwd_row, (SOCKADDR_INET *)&best6 ); + ok( !ret, "got error %lu.\n", ret ); + + /* Specified source address not matching destination, result is weird. */ + memset( &src6, 0, sizeof(src6) ); + src6.sin6_family = AF_INET6; + ret = inet_pton( AF_INET6, "::1", &src6.sin6_addr); + ok(ret, "got error %u.\n", WSAGetLastError()); + dst6.sin6_scope_id = 0; + memset( &best6, 0xcc, sizeof(best6) ); + memset( &fwd_row, 0xcc, sizeof(fwd_row) ); + ret = GetBestRoute2( &link_local_luid, 0, (SOCKADDR_INET *)&src6, (SOCKADDR_INET *)&dst6, 0, &fwd_row, + (SOCKADDR_INET *)&best6 ); + todo_wine ok( !ret, "got error %lu.\n", ret ); + todo_wine ok( !memcmp( &src6, &best6, sizeof(best6) ), "got different address %s.\n", + inet_ntop( AF_INET6, &best6.sin6_addr, s, sizeof(s) )); + ok( !fwd_row.InterfaceIndex, "got %lu.\n", fwd_row.InterfaceIndex ); + ok( IN6_IS_ADDR_UNSPECIFIED(&fwd_row.DestinationPrefix.Prefix.Ipv6.sin6_addr), "expected unspecifed address.\n" ); + + /* Specified source address takes precedence over specified interface. */ + dst6.sin6_scope_id = 0; + memset( &best6, 0xcc, sizeof(best6) ); + memset( &fwd_row, 0xcc, sizeof(fwd_row) ); + src6 = link_local_addr6; + src6.sin6_port = 1; + ret = GetBestRoute2( &link_local_luid, loopback_index, (SOCKADDR_INET *)&src6, (SOCKADDR_INET *)&dst6, 0, &fwd_row, + (SOCKADDR_INET *)&best6 ); + ok( !ret, "got error %lu.\n", ret ); + ok( fwd_row.InterfaceIndex == link_local_index, "got %lu, expected %lu.\n", fwd_row.InterfaceIndex, link_local_index ); + ok( !fwd_row.Loopback, "got %d.\n", fwd_row.Loopback ); + ok( best6.sin6_scope_id == link_local_index, "got %lu, expected %lu.\n", best6.sin6_scope_id, link_local_index ); + ok( !memcmp( &best6, &link_local_addr6, sizeof(best6) ), "got different address.\n" ); + + src6.sin6_scope_id = 0; + ret = GetBestRoute2( NULL, loopback_index, (SOCKADDR_INET *)&src6, (SOCKADDR_INET *)&dst6, 0, &fwd_row, + (SOCKADDR_INET *)&best6 ); + ok( !ret, "got error %lu.\n", ret ); + ok( fwd_row.InterfaceIndex == link_local_index, "got %lu, expected %lu.\n", fwd_row.InterfaceIndex, link_local_index ); + + src6.sin6_scope_id = 0xffff; + ret = GetBestRoute2( NULL, loopback_index, (SOCKADDR_INET *)&src6, (SOCKADDR_INET *)&dst6, 0, &fwd_row, + (SOCKADDR_INET *)&best6 ); + ok( ret == ERROR_NOT_FOUND, "got error %lu.\n", ret ); + ok( !fwd_row.InterfaceIndex, "got %lu, expected %lu.\n", fwd_row.InterfaceIndex, link_local_index ); + + src6.sin6_scope_id = link_local_index; + ret = GetBestRoute2( NULL, loopback_index, (SOCKADDR_INET *)&src6, (SOCKADDR_INET *)&dst6, 0, &fwd_row, + (SOCKADDR_INET *)&best6 ); + ok( !ret, "got error %lu.\n", ret ); + ok( fwd_row.InterfaceIndex == link_local_index, "got %lu, expected %lu.\n", fwd_row.InterfaceIndex, link_local_index ); + + luid.Value = 0xdeadbeef; + ret = GetBestRoute2( &luid, 0xdeadbeef, (SOCKADDR_INET *)&link_local_addr6, (SOCKADDR_INET *)&dst6, 0, &fwd_row, + (SOCKADDR_INET *)&best6 ); + ok( !ret, "got error %lu.\n", ret ); + ok( fwd_row.InterfaceIndex == link_local_index, "got %lu, expected %lu.\n", fwd_row.InterfaceIndex, link_local_index ); + + /* ... but if iface index is specified without luid that will still fail for invalid index. */ + ret = GetBestRoute2( NULL, 0xdeadbeef, (SOCKADDR_INET *)&link_local_addr6, (SOCKADDR_INET *)&dst6, 0, &fwd_row, + (SOCKADDR_INET *)&best6 ); + ok( ret == ERROR_FILE_NOT_FOUND, "got error %lu.\n", ret ); + + luid.Value = 0xdeadbeef; + ret = GetBestRoute2( &luid, 0xdeadbeef, NULL, (SOCKADDR_INET *)&dst6, 0, &fwd_row, (SOCKADDR_INET *)&best6 ); + ok( ret == ERROR_NOT_FOUND, "got error %lu.\n", ret ); + + /* Test with global address. */ + ret = inet_pton( AF_INET6, "2ead:beaf:dead:beaf::beaf", &dst6.sin6_addr); + ok(ret, "got error %u.\n", WSAGetLastError()); + + dst6.sin6_scope_id = 0xdeadbeef; + ret = GetBestInterfaceEx( (struct sockaddr *)&dst6, &index ); + ok( ret == ERROR_INVALID_PARAMETER, "got error %lu.\n", ret ); + + dst6.sin6_scope_id = link_local_index; + ret = GetBestInterfaceEx( (struct sockaddr *)&dst6, &index ); + ok( ret == ERROR_INVALID_PARAMETER, "got error %lu.\n", ret ); + + dst6.sin6_scope_id = 0; + ret = GetBestInterfaceEx( (struct sockaddr *)&dst6, &index ); + if (ret == ERROR_NETWORK_UNREACHABLE) + { + skip( "Global IPv6 address is unreachable.\n" ); + goto loopback_ipv6; + } + ok( !ret, "got error %lu.\n", ret ); + r = find_ipforward_row( table, index ); + ok( !!r, "got NULL.\n" ); + default_route_index = index; + dst6.sin6_scope_id = index; + ret = GetBestInterfaceEx( (struct sockaddr *)&dst6, &index ); + ok( ret == ERROR_INVALID_PARAMETER, "got error %lu.\n", ret ); + + ret = GetBestRoute2( NULL, 0, NULL, (SOCKADDR_INET *)&dst6, 0, &fwd_row, (SOCKADDR_INET *)&best6 ); + ok( ret == ERROR_INVALID_PARAMETER, "got error %lu.\n", ret ); + + dst6.sin6_scope_id = 0; + ret = GetBestRoute2( NULL, 0, NULL, (SOCKADDR_INET *)&dst6, 0, &fwd_row, (SOCKADDR_INET *)&best6 ); + ok( !ret, "got error %lu.\n", ret ); + ok( fwd_row.InterfaceIndex == index, "got %lu, %lu.\n", fwd_row.InterfaceIndex, index ); + ok( !IN6_IS_ADDR_UNSPECIFIED(&fwd_row.NextHop.Ipv6.sin6_addr) + || broken( IN6_IS_ADDR_UNSPECIFIED(&fwd_row.NextHop.Ipv6.sin6_addr) /* Win10 1507-1709 */), + "got unspecified address.\n" ); + + ret = GetBestRoute2( NULL, index, NULL, (SOCKADDR_INET *)&dst6, 0, &fwd_row, (SOCKADDR_INET *)&best6 ); + ok( !ret, "got error %lu.\n", ret ); + ok( fwd_row.InterfaceIndex == index, "got %lu, %lu.\n", fwd_row.InterfaceIndex, index ); + global_addr6 = best6; + + memset( &src6, 0, sizeof(src6) ); + src6.sin6_family = AF_INET6; + ret = inet_pton( AF_INET6, "dead:beaf:dead:beaf::beaf", &src6.sin6_addr); + ok(ret, "got error %u.\n", WSAGetLastError()); + + ret = GetBestRoute2( NULL, 0, (SOCKADDR_INET *)&src6, (SOCKADDR_INET *)&dst6, 0, &fwd_row, (SOCKADDR_INET *)&best6 ); + ok( ret == ERROR_NOT_FOUND, "got error %lu.\n", ret ); + + src6 = global_addr6; + src6.sin6_port = 28; + ret = GetBestRoute2( NULL, link_local_index, (SOCKADDR_INET *)&src6, (SOCKADDR_INET *)&dst6, 0, &fwd_row, + (SOCKADDR_INET *)&best6 ); + ok( !ret, "got error %lu.\n", ret ); + ok( fwd_row.InterfaceIndex == index, "got %lu, %lu.\n", fwd_row.InterfaceIndex, index ); + ok( !best6.sin6_scope_id, "got %lu.\n", best6.sin6_scope_id ); + ok( !memcmp( &global_addr6, &best6, sizeof(best6) ), "got different address.\n" ); + ok( !IN6_IS_ADDR_UNSPECIFIED(&fwd_row.NextHop.Ipv6.sin6_addr) + || broken( IN6_IS_ADDR_UNSPECIFIED(&fwd_row.NextHop.Ipv6.sin6_addr) /* Win10 1507-1709 */), + "got unspecified address.\n" ); + + src6.sin6_scope_id = 1; + ret = GetBestRoute2( NULL, link_local_index, (SOCKADDR_INET *)&src6, (SOCKADDR_INET *)&dst6, 0, &fwd_row, + (SOCKADDR_INET *)&best6 ); + ok( ret == ERROR_INVALID_PARAMETER, "got error %lu.\n", ret ); + + memset( &src6, 0, sizeof(src6) ); + src6.sin6_family = AF_INET6; + ret = inet_pton( AF_INET6, "::1", &src6.sin6_addr); + ok(ret, "got error %u.\n", WSAGetLastError()); + memset( &best6, 0xcc, sizeof(best6) ); + memset( &fwd_row, 0xcc, sizeof(fwd_row) ); + ret = GetBestRoute2( NULL, 0, (SOCKADDR_INET *)&src6, (SOCKADDR_INET *)&dst6, 0, &fwd_row, (SOCKADDR_INET *)&best6 ); + ok( ret == ERROR_NETWORK_UNREACHABLE, "got error %lu.\n", ret ); + ok( !fwd_row.InterfaceIndex, "got %lu.\n", fwd_row.InterfaceIndex ); + ok( !fwd_row.InterfaceLuid.Value, "got %#I64x.\n", fwd_row.InterfaceLuid.Value ); + ok( IN6_IS_ADDR_UNSPECIFIED(&best6.sin6_addr), "expected unspecifed address.\n" ); + ok( IN6_IS_ADDR_UNSPECIFIED(&fwd_row.NextHop.Ipv6.sin6_addr), "expected unspecifed address.\n" ); + + luid.Value = 0; + ret = GetBestRoute2( &luid, 0xdeadbeef, NULL, (SOCKADDR_INET *)&dst6, 0, &fwd_row, (SOCKADDR_INET *)&best6 ); + ok( !ret, "got error %lu.\n", ret ); + ok( fwd_row.InterfaceIndex == index, "got %lu, %lu.\n", fwd_row.InterfaceIndex, index ); + ok( !best6.sin6_scope_id, "got %lu.\n", best6.sin6_scope_id ); + memset( &uni_row, 0, sizeof(uni_row) ); + *(struct sockaddr_in6 *)&uni_row.Address = best6; + uni_row.InterfaceIndex = fwd_row.InterfaceIndex; + ret = GetUnicastIpAddressEntry( &uni_row ); + ok( !ret, "got error %lu.\n", ret ); + ok( !memcmp( &uni_row.Address, &best6, sizeof(best6) ), "got different address.\n" ); + ok( !IN6_IS_ADDR_UNSPECIFIED(&fwd_row.NextHop.Ipv6.sin6_addr) + || broken( IN6_IS_ADDR_UNSPECIFIED(&fwd_row.NextHop.Ipv6.sin6_addr) /* Win10 1507-1709 */), + "got unspecified address.\n" ); + + memset( &fwd_row, 0xcc, sizeof(fwd_row) ); + memset( &best6, 0xcc, sizeof(best6) ); + ret = GetBestRoute2( NULL, loopback_index, NULL, (SOCKADDR_INET *)&dst6, 0, &fwd_row, (SOCKADDR_INET *)&best6 ); + ok( ret == ERROR_NETWORK_UNREACHABLE, "got error %lu.\n", ret ); + ok( !fwd_row.InterfaceIndex, "got %#lx.\n", fwd_row.InterfaceIndex ); + ok( !best6.sin6_scope_id, "got %lu.\n", best6.sin6_scope_id ); + ok( IN6_IS_ADDR_UNSPECIFIED(&fwd_row.NextHop.Ipv6.sin6_addr), "expected unspecifed address.\n" ); + + memset( &dst6.sin6_addr, 0, sizeof(dst6.sin6_addr) ); + ret = GetBestRoute2( NULL, 0, NULL, (SOCKADDR_INET *)&dst6, 0, &fwd_row, (SOCKADDR_INET *)&best6 ); + ok( !ret, "got error %lu.\n", ret ); + ok( fwd_row.InterfaceIndex == index, "got %lu, %lu.\n", fwd_row.InterfaceIndex, index ); + ok( !memcmp( &global_addr6, &best6, sizeof(best6) ), "got different address.\n" ); + + /* Test with loopback address. */ +loopback_ipv6: + ret = inet_pton( AF_INET6, "::1", &dst6.sin6_addr); + ok(ret, "got error %u.\n", WSAGetLastError()); + dst6.sin6_scope_id = loopback_index; + ret = GetBestInterfaceEx( (struct sockaddr *)&dst6, &index ); + ok( ret == ERROR_INVALID_PARAMETER, "got error %lu.\n", ret ); + + dst6.sin6_scope_id = 0; + ret = GetBestInterfaceEx( (struct sockaddr *)&dst6, &index ); + ok( !ret, "got error %lu.\n", ret ); + ok( index == loopback_index, "got %lu, expected %lu.\n", index, loopback_index ); + ok( !best6.sin6_scope_id, "got %lu.\n", best6.sin6_scope_id ); + + dst6.sin6_scope_id = loopback_index; + ret = GetBestRoute2( NULL, loopback_index, NULL, (SOCKADDR_INET *)&dst6, 0, &fwd_row, (SOCKADDR_INET *)&best6 ); + ok( ret == ERROR_INVALID_PARAMETER, "got error %lu.\n", ret ); + + dst6.sin6_scope_id = 0; + ret = GetBestRoute2( NULL, loopback_index, NULL, (SOCKADDR_INET *)&dst6, 0, &fwd_row, (SOCKADDR_INET *)&best6 ); + ok( !ret, "got error %lu.\n", ret ); + ok( fwd_row.InterfaceIndex == loopback_index, "got %lu, %lu.\n", fwd_row.InterfaceIndex, loopback_index ); + ret = GetBestRoute2( NULL, 0, NULL, (SOCKADDR_INET *)&dst6, 0, &fwd_row, (SOCKADDR_INET *)&best6 ); + ok( !ret, "got error %lu.\n", ret ); + ok( fwd_row.InterfaceIndex == loopback_index, "got %lu, %lu.\n", fwd_row.InterfaceIndex, loopback_index ); + ok( !best6.sin6_scope_id, "got %lu.\n", best6.sin6_scope_id ); + ok( !memcmp( &dst6, &best6, sizeof(best6) ), "got different address.\n" ); + + memset( &best6, 0xcc, sizeof(best6) ); + if (default_route_index != ~0u) + { + ok( default_route_index != loopback_index, "got same interfaces.\n" ); + ret = GetBestRoute2( NULL, default_route_index, NULL, (SOCKADDR_INET *)&dst6, 0, &fwd_row, + (SOCKADDR_INET *)&best6 ); + ok( ret == ERROR_INVALID_PARAMETER, "got error %lu.\n", ret ); + ok( !fwd_row.InterfaceIndex, "got %#lx.\n", fwd_row.InterfaceIndex ); + } + + /* Test with ipv4 */ + memset( &dst4, 0xcc, sizeof(dst4) ); + dst4.sin_family = AF_INET; + ret = inet_pton( AF_INET, "25.25.0.0", &dst4.sin_addr); + ok(ret, "got error %u.\n", WSAGetLastError()); + + ret = GetBestInterfaceEx( (struct sockaddr *)&dst4, &index ); + if (ret == ERROR_NETWORK_UNREACHABLE) + { + skip( "Global IPv4 address is unreachable.\n" ); + goto done; + } + ok( !ret, "got error %lu.\n", ret ); + default_route_index = index; + + memset( &fwd_row, 0xcc, sizeof(fwd_row) ); + memset( &best4, 0xcc, sizeof(best4) ); + ret = GetBestRoute2( NULL, default_route_index, NULL, (SOCKADDR_INET *)&dst4, 0, &fwd_row, (SOCKADDR_INET *)&best4 ); + ok( !ret, "got error %lu.\n", ret ); + ok( fwd_row.InterfaceIndex == default_route_index, "got %lu, expected %lu.\n", fwd_row.InterfaceIndex, + default_route_index ); + ok( fwd_row.NextHop.Ipv4.sin_addr.s_addr, "expected specified next hop.\n" ); + memset( &uni_row, 0, sizeof(uni_row) ); + *(struct sockaddr_in *)&uni_row.Address = best4.Ipv4; + uni_row.InterfaceIndex = fwd_row.InterfaceIndex; + ret = GetUnicastIpAddressEntry( &uni_row ); + ok( !ret, "got error %lu.\n", ret ); + ok( !memcmp( &uni_row.Address, &best4, sizeof(best4) ), "got different address, %s, %s.\n", + inet_ntop( AF_INET, &best4.Ipv4.sin_addr, s, sizeof(s) ), inet_ntop( AF_INET, + &uni_row.Address.Ipv4.sin_addr, s2, sizeof(s2) ) ); + /* GetBestRoute2 zeroes the whole SOCKADDR_INET, not just IPv4 part. */ + ok( !best4.Ipv6.sin6_addr.u.Word[7], "got %#x.\n", best4.Ipv6.sin6_addr.u.Word[7] ); + global_addr4 = best4.Ipv4; + + src4.sin_family = AF_INET; + ret = inet_pton( AF_INET, "127.0.0.1", &src4.sin_addr ); + ok(ret, "got error %u.\n", WSAGetLastError()); + memset( &best4, 0xcc, sizeof(best4) ); + ret = GetBestRoute2( NULL, 0, (SOCKADDR_INET *)&src4, (SOCKADDR_INET *)&dst4, 0, &fwd_row, (SOCKADDR_INET *)&best4 ); + ok( ret == ERROR_NETWORK_UNREACHABLE, "got error %lu.\n", ret ); + ok( !best4.Ipv4.sin_family, "got %u.\n", best4.Ipv4.sin_family ); + ok( !best4.Ipv6.sin6_addr.u.Word[7], "got %#x.\n", best4.Ipv6.sin6_addr.u.Word[7] ); + + src4 = global_addr4; + ret = GetBestRoute2( NULL, loopback_index, (SOCKADDR_INET *)&src4, (SOCKADDR_INET *)&dst4, 0, &fwd_row, + (SOCKADDR_INET *)&best4 ); + ok( !ret, "got error %lu.\n", ret ); + ok( fwd_row.InterfaceIndex == default_route_index, "got %lu, expected %lu.\n", fwd_row.InterfaceIndex, + link_local_index ); + ok( fwd_row.NextHop.Ipv4.sin_addr.s_addr, "expected specified next hop.\n" ); + ok( !memcmp( &global_addr4, &best4, sizeof(best4.Ipv4) ), "got different address.\n" ); + + memset( &src4, 0xcc, sizeof(src4) ); + memset( &fwd_row, 0xcc, sizeof(fwd_row) ); + ret = GetBestRoute2( NULL, default_route_index, (SOCKADDR_INET *)&src4, (SOCKADDR_INET *)&dst4, 0, &fwd_row, + (SOCKADDR_INET *)&best4 ); + ok( !ret, "got error %lu.\n", ret ); + ok( fwd_row.InterfaceIndex == default_route_index, "got %lu, expected %lu.\n", fwd_row.InterfaceIndex, + link_local_index ); + + src4 = best4.Ipv4; + ret = GetBestRoute2( NULL, default_route_index, (SOCKADDR_INET *)&src4, (SOCKADDR_INET *)&dst4, 0, &fwd_row, + (SOCKADDR_INET *)&best4 ); + ok( !ret, "got error %lu.\n", ret ); + ok( fwd_row.InterfaceIndex == default_route_index, "got %lu, expected %lu.\n", fwd_row.InterfaceIndex, + link_local_index ); + ok( fwd_row.NextHop.Ipv4.sin_addr.s_addr, "expected specified next hop.\n" ); + + ret = GetBestRoute2( NULL, loopback_index, NULL, (SOCKADDR_INET *)&dst4, 0, &fwd_row, (SOCKADDR_INET *)&best4 ); + ok( ret == ERROR_NETWORK_UNREACHABLE, "got error %lu.\n", ret ); + + ret = inet_pton( AF_INET, "127.0.0.1", &dst4.sin_addr); + ok(ret, "got error %u.\n", WSAGetLastError()); + ret = GetBestRoute2( NULL, default_route_index, NULL, (SOCKADDR_INET *)&dst4, 0, &fwd_row, (SOCKADDR_INET *)&best4 ); + ok( ret == ERROR_INVALID_PARAMETER, "got error %lu.\n", ret ); + + ret = GetBestRoute2( NULL, 0, NULL, (SOCKADDR_INET *)&dst4, 0, &fwd_row, (SOCKADDR_INET *)&best4 ); + ok( !ret, "got error %lu.\n", ret ); + ok( !fwd_row.NextHop.Ipv4.sin_addr.s_addr, "expected unspecified next hop.\n" ); + +done: + FreeMibTable( table ); +} + START_TEST(iphlpapi) { WSADATA wsa_data; @@ -3530,6 +4014,7 @@ START_TEST(iphlpapi) test_ConvertGuidToString(); test_compartments(); test_GetIpInterface(); + test_best_routes(); freeIPHlpApi(); } From e8c1b3d5ea37f5c6f4b3313a01519e3b1f7ada8f Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 17 Sep 2025 12:20:14 -0600 Subject: [PATCH 450/454] iphlpapi: Try to disambiguate addresses in GetBestRoute2() by probing system assigned ones. (cherry picked from commit ada093a6d20f9bb1d57542c5e9df0ed8fcb35821) --- dlls/iphlpapi/iphlpapi_main.c | 62 ++++++++++++++++++++++++++++++++--- 1 file changed, 58 insertions(+), 4 deletions(-) diff --git a/dlls/iphlpapi/iphlpapi_main.c b/dlls/iphlpapi/iphlpapi_main.c index 65aebc6c394b..7f39d771f619 100644 --- a/dlls/iphlpapi/iphlpapi_main.c +++ b/dlls/iphlpapi/iphlpapi_main.c @@ -4711,6 +4711,13 @@ static MIB_UNICASTIPADDRESS_ROW *find_first_matching_unicast_addr( MIB_UNICASTIP return NULL; } +static BOOL is_addr_unspecified( const SOCKADDR_INET *addr, BOOL v6 ) +{ + if (v6 && IN6_IS_ADDR_UNSPECIFIED(&addr->Ipv6.sin6_addr)) return TRUE; + if (!v6 && !addr->Ipv4.sin_addr.s_addr) return TRUE; + return FALSE; +} + /****************************************************************** * GetBestRoute2 (IPHLPAPI.@) */ @@ -4739,9 +4746,8 @@ DWORD WINAPI GetBestRoute2( NET_LUID *luid, NET_IFINDEX index, v6 = dst->si_family == AF_INET6; if (src) { - if (src->si_family != dst->si_family) src = NULL; - else if (v6 && IN6_IS_ADDR_UNSPECIFIED(&src->Ipv6.sin6_addr)) src = NULL; - else if (!v6 && !src->Ipv4.sin_addr.s_addr) src = NULL; + if (src->si_family != dst->si_family) src = NULL; + else if (is_addr_unspecified( src, v6 )) src = NULL; } if (v6 && !IN6_IS_ADDR_LINKLOCAL(&dst->Ipv6.sin6_addr) && dst->Ipv6.sin6_scope_id) @@ -4802,6 +4808,54 @@ DWORD WINAPI GetBestRoute2( NET_LUID *luid, NET_IFINDEX index, } } + uni_row = NULL; + if (!src && !(v6 && IN6_IS_ADDR_LINKLOCAL(&dst->Ipv6.sin6_addr))) + { + static const struct in6_addr ipv6_default_addr = {{{ 0x20, 0x01, 0x0d, 0xb8 }}}; + SOCKADDR_INET src_addr, dst_addr; + WORD ver = MAKEWORD (2, 2); + WSADATA data; + SOCKET s; + int len; + + WSAStartup ( ver, &data ); + if ((s = socket( dst->si_family, SOCK_DGRAM, IPPROTO_UDP )) == -1) goto skip_system_route; + dst_addr = *dst; + if (is_addr_unspecified( dst, v6 )) + { + /* GetBestRoute2() should return default route in this case while connect() while unspecified address for + * connect() has different semantics. So use some global IP address instead. */ + if (v6) dst_addr.Ipv6.sin6_addr = ipv6_default_addr; + else dst_addr.Ipv4.sin_addr.s_addr = 0x01010101; + } + if (connect( s, (struct sockaddr *)&dst_addr, sizeof(dst_addr) ) == -1) + { + WARN( "Resolving destination address failed.\n" ); + goto skip_system_route; + } + len = sizeof(src_addr); + if (getsockname( s, (struct sockaddr *)&src_addr, &len )) + { + WARN( "getsockname failed, error %d.\n", WSAGetLastError() ); + goto skip_system_route; + } + if (!(uni_row = find_first_matching_unicast_addr( uni, v6, &src_addr, &dst_addr, index ))) + { + WARN( "Could not find matching unicast addr for system route.\n" ); + goto skip_system_route; + } + if (index && uni_row->InterfaceIndex != index) + { + WARN( "Specified index %lu doesn't match system %lu.\n", index, uni_row->InterfaceIndex ); + uni_row = NULL; + goto skip_system_route; + } + index = uni_row->InterfaceIndex; +skip_system_route: + if (s != -1) closesocket(s); + WSACleanup(); + } + max_prefix_len = -1; best_idx = 0; for (i = 0; i < fwd->NumEntries; ++i) @@ -4829,7 +4883,7 @@ DWORD WINAPI GetBestRoute2( NET_LUID *luid, NET_IFINDEX index, goto done; } - if (!(uni_row = find_first_matching_unicast_addr( uni, v6, src, dst, index ))) + if (!uni_row && !(uni_row = find_first_matching_unicast_addr( uni, v6, src, dst, index ))) { WARN( "Could not find source address.\n" ); ret = ERROR_NOT_FOUND; From f200cffdf954393376a0410b33eb60e8598eed55 Mon Sep 17 00:00:00 2001 From: Etaash Mathamsetty <45927311+Etaash-mathamsetty@users.noreply.github.com> Date: Fri, 22 Aug 2025 00:59:57 -0400 Subject: [PATCH 451/454] amdxc: Port to SDK headers. --- dlls/amdxc64/amdxc_interfaces.idl | 274 +++++++++++++++++++++++++++++- dlls/amdxc64/main.c | 104 +++++++----- 2 files changed, 325 insertions(+), 53 deletions(-) diff --git a/dlls/amdxc64/amdxc_interfaces.idl b/dlls/amdxc64/amdxc_interfaces.idl index 0c88ec175409..e150a83e6ac3 100644 --- a/dlls/amdxc64/amdxc_interfaces.idl +++ b/dlls/amdxc64/amdxc_interfaces.idl @@ -1,5 +1,6 @@ /* * Copyright 2025 Etaash Mathamsetty + * Copyright (c) 2016-2025 Advanced Micro Devices, Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -19,6 +20,10 @@ import "wtypes.idl"; import "unknwn.idl"; +import "d3d12.idl"; + + +/* the following are publicly available */ [ object, @@ -40,14 +45,62 @@ interface IAmdExtAntiLagApi : IUnknown HRESULT UpdateAntiLagState([in, out] void* pData); } +/* The following were reverse engineered, but updated with interface, function names & structures from AMD headers */ + +typedef struct +{ + unsigned int waveSize; + unsigned int minWaveSize; + unsigned int maxWaveSize; + unsigned int reserved[5]; +} AmdExtD3DShaderIntrinsicsInfo; + +typedef enum +{ + AmdExtD3DShaderIntrinsicsSupport_Readfirstlane = 0x1, + AmdExtD3DShaderIntrinsicsSupport_Readlane = 0x2, + AmdExtD3DShaderIntrinsicsSupport_LaneId = 0x3, + AmdExtD3DShaderIntrinsicsSupport_Swizzle = 0x4, + AmdExtD3DShaderIntrinsicsSupport_Ballot = 0x5, + AmdExtD3DShaderIntrinsicsSupport_MBCnt = 0x6, + AmdExtD3DShaderIntrinsicsSupport_Compare3 = 0x7, + AmdExtD3DShaderIntrinsicsSupport_Barycentrics = 0x8, + AmdExtD3DShaderIntrinsicsSupport_WaveReduce = 0x9, + AmdExtD3DShaderIntrinsicsSupport_WaveScan = 0xA, + AmdExtD3DShaderIntrinsicsSupport_LoadDwordAtAddr = 0xB, + AmdExtD3DShaderIntrinsicsSupport_Reserved1 = 0xC, + AmdExtD3DShaderIntrinsicsSupport_IntersectInternal = 0xD, + AmdExtD3DShaderIntrinsicsSupport_DrawIndex = 0xE, + AmdExtD3DShaderIntrinsicsSupport_AtomicU64 = 0xF, + AmdExtD3DShaderIntrinsicsSupport_BaseInstance = 0x10, + AmdExtD3DShaderIntrinsicsSupport_BaseVertex = 0x11, + AmdExtD3DShaderIntrinsicsSupport_FloatConversion = 0x12, + AmdExtD3DShaderIntrinsicsSupport_GetWaveSize = 0x13, + AmdExtD3DShaderIntrinsicsSupport_ReadlaneAt = 0x14, + AmdExtD3DShaderIntrinsicsSupport_RayTraceHitToken = 0x15, + AmdExtD3DShaderIntrinsicsSupport_ShaderClock = 0x16, + AmdExtD3DShaderIntrinsicsSupport_ShaderRealtimeClock = 0x17, + AmdExtD3DShaderIntrinsicsSupport_Halt = 0x18, + AmdExtD3DShaderIntrinsicsSupport_IntersectBvhNode = 0x19, + AmdExtD3DShaderIntrinsicsSupport_BufferStoreByte = 0x1A, + AmdExtD3DShaderIntrinsicsSupport_BufferStoreShort = 0x1B, + AmdExtD3DShaderIntrinsicsSupport_ShaderMarker = 0x1C, + AmdExtD3DShaderIntrinsicsSupport_FloatOpWithRoundMode = 0x1D, + AmdExtD3DShaderIntrinsicsSupport_Reserved2 = 0x1E, + AmdExtD3DShaderIntrinsicsSupport_WaveMatrix = 0x1F, + AmdExtD3DShaderIntrinsicsSupport_Float8Conversion = 0x20, + AmdExtD3DShaderIntrinsicsSupport_Builtins = 0x21, + AmdExtD3DShaderIntrinsicsSupport_LoadByteAtAddr = 0x22, +} AmdExtD3DShaderIntrinsicsSupport; + [ object, uuid(014937ec-9288-446f-a9ac-d75a8e3a984f), local ] -interface IAmdExtStub1 : IUnknown +interface IAmdExtD3DFactory : IUnknown { - HRESULT QueryInterface2([in, out] void* unk, [in] REFIID iid, [in, out] void** out); + HRESULT CreateInterface([in] IUnknown* outer, [in] REFIID iid, [in, out] void** out); } [ @@ -55,9 +108,218 @@ interface IAmdExtStub1 : IUnknown uuid(ba019d53-ccab-4cbd-b56a-7230ed4330ad), local ] -interface IAmdExtStub2 : IUnknown +interface IAmdExtD3DShaderIntrinsics : IUnknown +{ + HRESULT GetInfo([in, out] AmdExtD3DShaderIntrinsicsInfo *pInfo); + HRESULT CheckSupport([in] AmdExtD3DShaderIntrinsicsSupport opcodeSupport); + HRESULT Enable(); +} + +/* The following were taken from AMD headers */ + +typedef enum +{ + AmdExtD3DStructUnknown, + AmdExtD3DStructPipelineState, + AmdExtD3DStructPipelineElf, + AmdExtD3DStructPipelineCrossCompile +} AmdExtD3DStructType; + +typedef struct +{ + AmdExtD3DStructType type; + void *pNext; +} AmdExtD3DCreateInfo; + +#define AMD_EXT_DEPTH_BOUNDS_TEST_ENABLE (1 << 0) +#define AMD_EXT_ABORT_IF_NOT_PIPELINE_CACHED (1 << 1) +#define AMD_EXT_TOPOLOGY_TYPE_RECTANGLE (1 << 2) + +typedef struct +{ + ULONG flags; +} AmdExtD3DPipelineFlags; + +typedef struct +{ + AmdExtD3DCreateInfo info; + AmdExtD3DPipelineFlags flags; +} AmdExtD3DPipelineCreateInfo; + +typedef enum +{ + Flags, +} AmdExtD3DCheckFeatureSupportType; + +typedef enum +{ + AmdExtD3DPrimitiveTopologyUndefined = 0, + AmdExtD3DPrimitiveTopologyRectangleList = 1, +} AmdExtD3DPrimitiveTopology; + +typedef struct +{ + USHORT major; + USHORT minor; +} AmdExtD3DGpuRtVersion; + +#define AMD_EXT_WMMA_TYPE_FP16 0 +#define AMD_EXT_WMMA_TYPE_FP32 1 +#define AMD_EXT_WMMA_TYPE_FP8 11 + +typedef struct +{ + SIZE_T mSize; + SIZE_T nSize; + SIZE_T kSize; + ULONG aType; + ULONG bType; + ULONG cType; + ULONG resultType; + BOOL saturatingAccumulation; +} AmdExtWaveMatrixProperties; + +typedef struct +{ + AmdExtD3DCreateInfo info; + const void *elfBinary; + SIZE_T elfSize; + struct + { + ULONG width; + ULONG height; + ULONG depth; + } threadsPerGroup; +} AmdExtD3DPipelineElfInfo; + +typedef struct +{ + AmdExtD3DCreateInfo info; + const void *blob; + SIZE_T blobSize; + struct + { + UINT dimx; + UINT dimy; + UINT dimz; + } threadsPerGroup; + ULONG shaderType; + const void *options; + SIZE_T optionSize; + const char *kernelName; +} AmdExtD3DPipelineCrossCompileInfo; + +[ + object, + uuid(E6144584-03DE-439C-9C0B-43AE6D009BC6), + local +] +interface IAmdExtD3DShaderIntrinsics1 : IAmdExtD3DShaderIntrinsics +{ + HRESULT SetExtensionUavBinding([in] UINT registerIndex, [in] UINT registerSpace); +} + +[ + object, + uuid(8104C0FC-7413-410F-8E83-AA617E908648), + local +] +interface IAmdExtD3DDevice : IUnknown +{ + HRESULT CreateGraphicsPipelineState([in] const AmdExtD3DCreateInfo* pAmdExtCreateInfo, + [in] const D3D12_GRAPHICS_PIPELINE_STATE_DESC *pDesc, + [in] REFIID iid, [in, out] void **ppPipelineState); +} + +[ + object, + uuid(4BBCAF68-EAF7-4FA4-B653-CB458C334A4E), + local +] +interface IAmdExtD3DDevice1 : IAmdExtD3DDevice +{ + void PushMarker([in] ID3D12GraphicsCommandList *pGfxCmdList, [in] const char *pMarkerData); + void PopMarker([in] ID3D12GraphicsCommandList *pGfxCmdList); + void SetMarker([in] ID3D12GraphicsCommandList *pGfxCmdList, [in] const char *pMarkerData); +} + +[ + object, + uuid(A7BECF5D-2930-4FDA-8EEE-C797D8A52B7E), + local +] +interface IAmdExtD3DDevice2 : IAmdExtD3DDevice1 +{ + HRESULT CheckExtFeatureSupport([in] AmdExtD3DCheckFeatureSupportType featureType, + [in, out] void *pFeatureData, [in] SIZE_T featureDataSize); + HRESULT CreateComputePipelineState([in] const AmdExtD3DCreateInfo* pAmdExtCreateInfo, + [in] const D3D12_COMPUTE_PIPELINE_STATE_DESC* pDesc, + [in] REFIID iid, [in, out] void **ppPipelineState); +} + +[ + object, + uuid(397E3533-111E-4A9D-A171-2BAE8EF6CB24), + local +] +interface IAmdExtD3DDevice3 : IAmdExtD3DDevice2 +{ + HRESULT CreatePipelineState([in] const AmdExtD3DCreateInfo *pAmdExtCreateInfo, + [in] const D3D12_PIPELINE_STATE_STREAM_DESC* pDesc, + [in] REFIID iid, [in, out] void **ppPipelineState); +} + +[ + object, + uuid(BE9A8C6A-868E-490D-8FBF-29DAC2650F3B), + local +] +interface IAmdExtD3DDevice4 : IAmdExtD3DDevice3 +{ + void SetPrimitiveTopology([in] ID3D12GraphicsCommandList *pGfxCmdList, [in] AmdExtD3DPrimitiveTopology topology); +} + +[ + object, + uuid(BDC14598-B7D2-4A8D-9CA5-67848E2AF745), + local +] +interface IAmdExtD3DDevice5 : IAmdExtD3DDevice4 +{ + HRESULT CreateComputePipelineFromElf([in] AmdExtD3DPipelineElfInfo *pAmdExtCreateInfo, + REFIID iid, void **ppPipelineState); + void SetKernelArguments([in] ID3D12GraphicsCommandList *pCmdList, [in] ULONG first, + [in] ULONG count, [in] const void *ppValues); +} + +[ + object, + uuid(F764A768-48B4-46A5-9779-928ED6896D2A), + local +] +interface IAmdExtD3DDevice6 : IAmdExtD3DDevice5 +{ + void GetGpuRtInterfaceVersion([in, out] AmdExtD3DGpuRtVersion* pInterfaceVersion); + void GetGpuRtBinaryVersion([in, out] AmdExtD3DGpuRtVersion* pBinaryVersion); +} + +[ + object, + uuid(FEE37AFC-3C50-4ABF-86CC-1622349B29C0), + local +] +interface IAmdExtD3DDevice7 : IAmdExtD3DDevice6 +{ + HRESULT CreateComputePipelineCrossCompile([in] const AmdExtD3DPipelineCrossCompileInfo* pAmdExtCreateInfo, + [in] REFIID iid, [in, out] void** ppPipelineState); +} + +[ + object, + uuid(F714E11A-B54E-4E0F-ABC5-DF58B18133D1), + local +] +interface IAmdExtD3DDevice8 : IAmdExtD3DDevice7 { - void stub(); - void stub2([in] unsigned int unk); - void stub3(); + HRESULT GetWaveMatrixProperties([in, out] SIZE_T *pCount, [in, out] AmdExtWaveMatrixProperties *pProperties); } diff --git a/dlls/amdxc64/main.c b/dlls/amdxc64/main.c index b4eb97f34a99..8180027ecda8 100644 --- a/dlls/amdxc64/main.c +++ b/dlls/amdxc64/main.c @@ -110,97 +110,106 @@ static const struct IAmdExtFfxApiVtbl AMDFSR4FFX_vtable = { AMDFSR4FFX_UpdateFfxApiProvider }; -struct AMDExtStub2 +struct AmdExtD3DShaderIntrinsics { - IAmdExtStub2 IAmdExtStub2_iface; + IAmdExtD3DShaderIntrinsics IAmdExtD3DShaderIntrinsics_iface; LONG ref; }; -struct AMDExtStub2* impl_from_IAMDExtStub2(IAmdExtStub2 *iface) +struct AmdExtD3DShaderIntrinsics* impl_from_IAmdExtD3DShaderIntrinsics(IAmdExtD3DShaderIntrinsics *iface) { - return CONTAINING_RECORD(iface, struct AMDExtStub2, IAmdExtStub2_iface); + return CONTAINING_RECORD(iface, struct AmdExtD3DShaderIntrinsics, IAmdExtD3DShaderIntrinsics_iface); } -ULONG STDMETHODCALLTYPE AMDExtStub2_AddRef(IAmdExtStub2 *iface) +ULONG STDMETHODCALLTYPE AmdExtD3DShaderIntrinsics_AddRef(IAmdExtD3DShaderIntrinsics *iface) { - struct AMDExtStub2 *this = impl_from_IAMDExtStub2(iface); + struct AmdExtD3DShaderIntrinsics *this = impl_from_IAmdExtD3DShaderIntrinsics(iface); return InterlockedIncrement(&this->ref); } -ULONG STDMETHODCALLTYPE AMDExtStub2_Release(IAmdExtStub2 *iface) +ULONG STDMETHODCALLTYPE AmdExtD3DShaderIntrinsics_Release(IAmdExtD3DShaderIntrinsics *iface) { - struct AMDExtStub2 *this = impl_from_IAMDExtStub2(iface); + struct AmdExtD3DShaderIntrinsics *this = impl_from_IAmdExtD3DShaderIntrinsics(iface); ULONG ret = InterlockedDecrement(&this->ref); if (!ret) free(this); return ret; } -HRESULT STDMETHODCALLTYPE AMDExtStub2_QueryInterface(IAmdExtStub2 *iface, REFIID iid, void **out) +HRESULT STDMETHODCALLTYPE AmdExtD3DShaderIntrinsics_QueryInterface(IAmdExtD3DShaderIntrinsics *iface, REFIID iid, void **out) { FIXME("%p %s %p stub!\n", iface, debugstr_guid(iid), out); return E_NOINTERFACE; } -void STDMETHODCALLTYPE AMDExtStub2_stub1(IAmdExtStub2 *iface) +HRESULT STDMETHODCALLTYPE AmdExtD3DShaderIntrinsics_GetInfo(IAmdExtD3DShaderIntrinsics *iface, + AmdExtD3DShaderIntrinsicsInfo *info) { - FIXME("%p stub!\n", iface); + FIXME("%p %p stub!\n", iface, info); + return S_OK; } -void STDMETHODCALLTYPE AMDExtStub2_stub2(IAmdExtStub2 *iface, unsigned int unk) +HRESULT STDMETHODCALLTYPE AmdExtD3DShaderIntrinsics_CheckSupport(IAmdExtD3DShaderIntrinsics *iface, + AmdExtD3DShaderIntrinsicsSupport opcode) { - FIXME("%p %u stub!\n", iface, unk); + if (opcode == AmdExtD3DShaderIntrinsicsSupport_Float8Conversion) return S_OK; + if (opcode == AmdExtD3DShaderIntrinsicsSupport_WaveMatrix) return S_OK; + + FIXME("%p %u stub!\n", iface, opcode); + return S_OK; } -void STDMETHODCALLTYPE AMDExtStub2_stub3(IAmdExtStub2 *iface) +HRESULT STDMETHODCALLTYPE AmdExtD3DShaderIntrinsics_Enable(IAmdExtD3DShaderIntrinsics *iface) { - FIXME("%p stub!\n", iface); + TRACE("%p\n", iface); + /* shader intrinsics are always handled by vkd3d-proton */ + return S_OK; } -const static struct IAmdExtStub2Vtbl AMDSTUB2_vtable = { - AMDExtStub2_QueryInterface, - AMDExtStub2_AddRef, - AMDExtStub2_Release, - AMDExtStub2_stub1, - AMDExtStub2_stub2, - AMDExtStub2_stub3 +const static struct IAmdExtD3DShaderIntrinsicsVtbl AmdExtD3DShaderIntrinsics_vtable = { + AmdExtD3DShaderIntrinsics_QueryInterface, + AmdExtD3DShaderIntrinsics_AddRef, + AmdExtD3DShaderIntrinsics_Release, + AmdExtD3DShaderIntrinsics_GetInfo, + AmdExtD3DShaderIntrinsics_CheckSupport, + AmdExtD3DShaderIntrinsics_Enable }; -struct AMDExtStub1 +struct AmdExtD3DFactory { - IAmdExtStub1 IAmdExtStub1_iface; + IAmdExtD3DFactory IAmdExtD3DFactory_iface; LONG ref; }; -struct AMDExtStub1* impl_from_IAMDExtStub1(IAmdExtStub1 *iface) +struct AmdExtD3DFactory* impl_from_IAmdExtD3DFactory(IAmdExtD3DFactory *iface) { - return CONTAINING_RECORD(iface, struct AMDExtStub1, IAmdExtStub1_iface); + return CONTAINING_RECORD(iface, struct AmdExtD3DFactory, IAmdExtD3DFactory_iface); } -ULONG STDMETHODCALLTYPE AMDExtStub1_AddRef(IAmdExtStub1 *iface) +ULONG STDMETHODCALLTYPE AmdExtD3DFactory_AddRef(IAmdExtD3DFactory *iface) { - struct AMDExtStub1 *this = impl_from_IAMDExtStub1(iface); + struct AmdExtD3DFactory *this = impl_from_IAmdExtD3DFactory(iface); return InterlockedIncrement(&this->ref); } -ULONG STDMETHODCALLTYPE AMDExtStub1_Release(IAmdExtStub1 *iface) +ULONG STDMETHODCALLTYPE AmdExtD3DFactory_Release(IAmdExtD3DFactory *iface) { - struct AMDExtStub1 *this = impl_from_IAMDExtStub1(iface); + struct AmdExtD3DFactory *this = impl_from_IAmdExtD3DFactory(iface); ULONG ret = InterlockedDecrement(&this->ref); if (!ret) free(this); return ret; } -HRESULT STDMETHODCALLTYPE AmdExtStub1_QueryInterface2(IAmdExtStub1 *iface, void* unk, REFIID iid, void **out) +HRESULT STDMETHODCALLTYPE AmdExtD3DFactory_CreateInterface(IAmdExtD3DFactory *iface, IUnknown *outer, REFIID iid, void **out) { - TRACE("%p %p %s %p\n", iface, unk, debugstr_guid(iid), out); + TRACE("%p %p %s %p\n", iface, outer, debugstr_guid(iid), out); - if(IsEqualGUID(iid, &IID_IAmdExtStub2)) + if(IsEqualGUID(iid, &IID_IAmdExtD3DShaderIntrinsics)) { - struct AMDExtStub2 *this = calloc(1, sizeof(struct AMDExtStub2)); + struct AmdExtD3DShaderIntrinsics *this = calloc(1, sizeof(struct AmdExtD3DShaderIntrinsics)); - this->IAmdExtStub2_iface.lpVtbl = &AMDSTUB2_vtable; + this->IAmdExtD3DShaderIntrinsics_iface.lpVtbl = &AmdExtD3DShaderIntrinsics_vtable; this->ref = 1; - *out = &this->IAmdExtStub2_iface; + *out = &this->IAmdExtD3DShaderIntrinsics_iface; return S_OK; } else { FIXME("unknown guid %s\n", debugstr_guid(iid)); @@ -209,16 +218,17 @@ HRESULT STDMETHODCALLTYPE AmdExtStub1_QueryInterface2(IAmdExtStub1 *iface, void* return E_NOINTERFACE; } -HRESULT STDMETHODCALLTYPE AmdExtStub1_QueryInterface(IAmdExtStub1 *iface, REFIID iid, void **out) +HRESULT STDMETHODCALLTYPE AmdExtD3DFactory_QueryInterface(IAmdExtD3DFactory *iface, REFIID iid, void **out) { - return AmdExtStub1_QueryInterface2(iface, NULL, iid, out); + TRACE("%p %s %p", iface, debugstr_guid(iid), out); + return E_NOINTERFACE; } -static const struct IAmdExtStub1Vtbl AMDSTUB1_vtable = { - AmdExtStub1_QueryInterface, - AMDExtStub1_AddRef, - AMDExtStub1_Release, - AmdExtStub1_QueryInterface2 +static const struct IAmdExtD3DFactoryVtbl AmdExtD3DFactory_vtable = { + AmdExtD3DFactory_QueryInterface, + AmdExtD3DFactory_AddRef, + AmdExtD3DFactory_Release, + AmdExtD3DFactory_CreateInterface }; HRESULT CDECL AmdExtD3DCreateInterface(IUnknown *outer, REFIID iid, void **obj) @@ -234,11 +244,11 @@ HRESULT CDECL AmdExtD3DCreateInterface(IUnknown *outer, REFIID iid, void **obj) return S_OK; } else if (IsEqualGUID(iid, &IID_IAmdExtAntiLagApi)) { return ID3D12Device_QueryInterface((ID3D12Device *)outer, &IID_IAmdExtAntiLagApi, obj); - } else if(IsEqualGUID(iid, &IID_IAmdExtStub1)) { - struct AMDExtStub1 *this = calloc(1, sizeof(struct AMDExtStub1)); - this->IAmdExtStub1_iface.lpVtbl = &AMDSTUB1_vtable; + } else if(IsEqualGUID(iid, &IID_IAmdExtD3DFactory)) { + struct AmdExtD3DFactory *this = calloc(1, sizeof(struct AmdExtD3DFactory)); + this->IAmdExtD3DFactory_iface.lpVtbl = &AmdExtD3DFactory_vtable; this->ref = 1; - *obj = &this->IAmdExtStub1_iface; + *obj = &this->IAmdExtD3DFactory_iface; return S_OK; } else { FIXME("unknown guid: %s\n", debugstr_guid(iid)); From 6845f1dd6a790f5d1619bda38ace7b2f80fba569 Mon Sep 17 00:00:00 2001 From: Etaash Mathamsetty <45927311+Etaash-mathamsetty@users.noreply.github.com> Date: Sat, 23 Aug 2025 10:35:00 -0400 Subject: [PATCH 452/454] amdxc: Add stub interface for IAmdExtD3DDevice8. Fixes FSR 4.0.2 --- dlls/amdxc64/amdxc_interfaces.idl | 12 +-- dlls/amdxc64/main.c | 174 +++++++++++++++++++++++++++++- 2 files changed, 178 insertions(+), 8 deletions(-) diff --git a/dlls/amdxc64/amdxc_interfaces.idl b/dlls/amdxc64/amdxc_interfaces.idl index e150a83e6ac3..d446d0c4a2e6 100644 --- a/dlls/amdxc64/amdxc_interfaces.idl +++ b/dlls/amdxc64/amdxc_interfaces.idl @@ -131,9 +131,9 @@ typedef struct void *pNext; } AmdExtD3DCreateInfo; -#define AMD_EXT_DEPTH_BOUNDS_TEST_ENABLE (1 << 0) -#define AMD_EXT_ABORT_IF_NOT_PIPELINE_CACHED (1 << 1) -#define AMD_EXT_TOPOLOGY_TYPE_RECTANGLE (1 << 2) +cpp_quote("#define AMD_EXT_DEPTH_BOUNDS_TEST_ENABLE (1 << 0)") +cpp_quote("#define AMD_EXT_ABORT_IF_NOT_PIPELINE_CACHED (1 << 1)") +cpp_quote("#define AMD_EXT_TOPOLOGY_TYPE_RECTANGLE (1 << 2)") typedef struct { @@ -163,9 +163,9 @@ typedef struct USHORT minor; } AmdExtD3DGpuRtVersion; -#define AMD_EXT_WMMA_TYPE_FP16 0 -#define AMD_EXT_WMMA_TYPE_FP32 1 -#define AMD_EXT_WMMA_TYPE_FP8 11 +cpp_quote("#define AMD_EXT_WMMA_TYPE_FP16 0") +cpp_quote("#define AMD_EXT_WMMA_TYPE_FP32 1") +cpp_quote("#define AMD_EXT_WMMA_TYPE_FP8 11") typedef struct { diff --git a/dlls/amdxc64/main.c b/dlls/amdxc64/main.c index 8180027ecda8..6df95578748e 100644 --- a/dlls/amdxc64/main.c +++ b/dlls/amdxc64/main.c @@ -174,6 +174,171 @@ const static struct IAmdExtD3DShaderIntrinsicsVtbl AmdExtD3DShaderIntrinsics_vta AmdExtD3DShaderIntrinsics_Enable }; +struct AmdExtD3DDevice8 +{ + IAmdExtD3DDevice8 IAmdExtD3DDevice8_iface; + LONG ref; +}; + +struct AmdExtD3DDevice8 *impl_from_IAmdExtD3DDevice8(IAmdExtD3DDevice8 *iface) +{ + return CONTAINING_RECORD(iface, struct AmdExtD3DDevice8, IAmdExtD3DDevice8_iface); +} + +HRESULT STDMETHODCALLTYPE AmdExtD3DDevice8_QueryInterface(IAmdExtD3DDevice8 *iface, REFIID iid, void **out) +{ + TRACE("%p %s %p\n", iface, debugstr_guid(iid), out); + return E_NOINTERFACE; +} + +ULONG STDMETHODCALLTYPE AmdExtD3DDevice8_AddRef(IAmdExtD3DDevice8 *iface) +{ + struct AmdExtD3DDevice8* this = impl_from_IAmdExtD3DDevice8(iface); + return InterlockedIncrement(&this->ref); +} + +ULONG STDMETHODCALLTYPE AmdExtD3DDevice8_Release(IAmdExtD3DDevice8 *iface) +{ + struct AmdExtD3DDevice8* this = impl_from_IAmdExtD3DDevice8(iface); + ULONG ret = InterlockedDecrement(&this->ref); + if (!ret) free(this); + return ret; +} + +HRESULT STDMETHODCALLTYPE AmdExtD3DDevice8_CreateGraphicsPipelineState(IAmdExtD3DDevice8 *iface, + const AmdExtD3DCreateInfo *pCreateInfo, + const D3D12_GRAPHICS_PIPELINE_STATE_DESC *pDesc, + REFIID iid, void **ppPipelineState) +{ + FIXME("%p %p %p %s %p stub!\n", iface, pCreateInfo, pDesc, debugstr_guid(iid), ppPipelineState); + return E_NOTIMPL; +} + +void STDMETHODCALLTYPE AmdExtD3DDevice8_PushMarker(IAmdExtD3DDevice8 *iface, ID3D12GraphicsCommandList *pGfxCmdList, + const char *pMarkerData) +{ + FIXME("%p %p %s stub!\n", iface, pGfxCmdList, pMarkerData); +} + +void STDMETHODCALLTYPE AmdExtD3DDevice8_PopMarker(IAmdExtD3DDevice8 *iface, ID3D12GraphicsCommandList *pGfxCmdList) +{ + FIXME("%p %p stub!\n", iface, pGfxCmdList); +} + +void STDMETHODCALLTYPE AmdExtD3DDevice8_SetMarker(IAmdExtD3DDevice8 *iface, ID3D12GraphicsCommandList *pGfxCmdList, + const char *pMarkerData) +{ + FIXME("%p %p %s stub!\n", iface, pGfxCmdList, pMarkerData); +} + +HRESULT STDMETHODCALLTYPE AmdExtD3DDevice8_CheckExtFeatureSupport(IAmdExtD3DDevice8 *iface, AmdExtD3DCheckFeatureSupportType type, + void *data, SIZE_T size) +{ + FIXME("%p %u %p %lu stub!\n", iface, type, data, (ULONG)size); + return E_NOTIMPL; +} + +HRESULT STDMETHODCALLTYPE AmdExtD3DDevice8_CreateComputePipelineState(IAmdExtD3DDevice8 *iface, + const AmdExtD3DCreateInfo* pAmdExtCreateInfo, + const D3D12_COMPUTE_PIPELINE_STATE_DESC* pDesc, + REFIID iid, void **ppPipelineState) +{ + FIXME("%p %p %p %s %p stub!\n", iface, pAmdExtCreateInfo, pDesc, debugstr_guid(iid), ppPipelineState); + return E_NOTIMPL; +} + +HRESULT STDMETHODCALLTYPE AmdExtD3DDevice8_CreatePipelineState(IAmdExtD3DDevice8 *iface, + const AmdExtD3DCreateInfo *pAmdExtCreateInfo, + const D3D12_PIPELINE_STATE_STREAM_DESC* pDesc, + REFIID iid, void **ppPipelineState) +{ + FIXME("%p %p %p %s %p stub!\n", iface, pAmdExtCreateInfo, pDesc, debugstr_guid(iid), ppPipelineState); + return E_NOTIMPL; +} + +void STDMETHODCALLTYPE AmdExtD3DDevice8_SetPrimitiveTopology(IAmdExtD3DDevice8 *iface, + ID3D12GraphicsCommandList *pGfxCmdList, + AmdExtD3DPrimitiveTopology topology) +{ + FIXME("%p %p %u stub!\n", iface, pGfxCmdList, topology); +} + +HRESULT STDMETHODCALLTYPE AmdExtD3DDevice8_CreateComputePipelineFromElf(IAmdExtD3DDevice8 *iface, + AmdExtD3DPipelineElfInfo *pAmdExtCreateInfo, + REFIID iid, void **ppPipelineState) +{ + FIXME("%p %p %s %p stub!\n", iface, pAmdExtCreateInfo, debugstr_guid(iid), ppPipelineState); + return E_NOTIMPL; +} + +void STDMETHODCALLTYPE AmdExtD3DDevice8_SetKernelArguments(IAmdExtD3DDevice8 *iface, + ID3D12GraphicsCommandList *pCmdList, + ULONG first, ULONG count, const void *ppValues) +{ + FIXME("%p %p %lu %lu %p stub!\n", iface, pCmdList, first, count, ppValues); +} + +void STDMETHODCALLTYPE AmdExtD3DDevice8_GetGpuRtInterfaceVersion(IAmdExtD3DDevice8 *iface, + AmdExtD3DGpuRtVersion *pInterfaceVersion) +{ + FIXME("%p %p stub!\n", iface, pInterfaceVersion); +} + +void STDMETHODCALLTYPE AmdExtD3DDevice8_GetGpuRtBinaryVersion(IAmdExtD3DDevice8 *iface, + AmdExtD3DGpuRtVersion *pBinaryVersion) +{ + FIXME("%p %p stub!\n", iface, pBinaryVersion); +} + +HRESULT STDMETHODCALLTYPE AmdExtD3DDevice8_CreateComputePipelineCrossCompile(IAmdExtD3DDevice8 *iface, + const AmdExtD3DPipelineCrossCompileInfo* pAmdExtCreateInfo, + REFIID iid, void **ppPipelineState) +{ + FIXME("%p %p %s %p stub!\n", iface, pAmdExtCreateInfo, debugstr_guid(iid), ppPipelineState); + return E_NOTIMPL; +} + +HRESULT STDMETHODCALLTYPE AmdExtD3DDevice8_GetWaveMatrixProperties(IAmdExtD3DDevice8 *iface, + SIZE_T *pCount, AmdExtWaveMatrixProperties *pProperties) +{ + static AmdExtWaveMatrixProperties prop[1] = {{ + 16, 16, 16, AMD_EXT_WMMA_TYPE_FP8, AMD_EXT_WMMA_TYPE_FP8, + AMD_EXT_WMMA_TYPE_FP32, AMD_EXT_WMMA_TYPE_FP32, FALSE}}; + + TRACE("%p %p %p\n", iface, pCount, pProperties); + + if (!pCount) return E_INVALIDARG; + + if (*pCount >= 1) + { + *pCount = 1; + memcpy(pProperties, prop, sizeof(prop)); + return S_OK; + } /* FIXME: Handle pCount == 0 */ + + return S_OK; +} + +static const struct IAmdExtD3DDevice8Vtbl AmdExtD3DDevice8_vtable = { + AmdExtD3DDevice8_QueryInterface, + AmdExtD3DDevice8_AddRef, + AmdExtD3DDevice8_Release, + AmdExtD3DDevice8_CreateGraphicsPipelineState, + AmdExtD3DDevice8_PushMarker, + AmdExtD3DDevice8_PopMarker, + AmdExtD3DDevice8_SetMarker, + AmdExtD3DDevice8_CheckExtFeatureSupport, + AmdExtD3DDevice8_CreateComputePipelineState, + AmdExtD3DDevice8_CreatePipelineState, + AmdExtD3DDevice8_SetPrimitiveTopology, + AmdExtD3DDevice8_CreateComputePipelineFromElf, + AmdExtD3DDevice8_SetKernelArguments, + AmdExtD3DDevice8_GetGpuRtInterfaceVersion, + AmdExtD3DDevice8_GetGpuRtBinaryVersion, + AmdExtD3DDevice8_CreateComputePipelineCrossCompile, + AmdExtD3DDevice8_GetWaveMatrixProperties +}; + struct AmdExtD3DFactory { IAmdExtD3DFactory IAmdExtD3DFactory_iface; @@ -203,14 +368,19 @@ HRESULT STDMETHODCALLTYPE AmdExtD3DFactory_CreateInterface(IAmdExtD3DFactory *if { TRACE("%p %p %s %p\n", iface, outer, debugstr_guid(iid), out); - if(IsEqualGUID(iid, &IID_IAmdExtD3DShaderIntrinsics)) + if (IsEqualGUID(iid, &IID_IAmdExtD3DShaderIntrinsics)) { struct AmdExtD3DShaderIntrinsics *this = calloc(1, sizeof(struct AmdExtD3DShaderIntrinsics)); - this->IAmdExtD3DShaderIntrinsics_iface.lpVtbl = &AmdExtD3DShaderIntrinsics_vtable; this->ref = 1; *out = &this->IAmdExtD3DShaderIntrinsics_iface; return S_OK; + } else if (IsEqualGUID(iid, &IID_IAmdExtD3DDevice8)) { + struct AmdExtD3DDevice8 *this = calloc(1, sizeof(struct AmdExtD3DDevice8)); + this->IAmdExtD3DDevice8_iface.lpVtbl = &AmdExtD3DDevice8_vtable; + this->ref = 1; + *out = &this->IAmdExtD3DDevice8_iface; + return S_OK; } else { FIXME("unknown guid %s\n", debugstr_guid(iid)); } From 436ecc0a7ca6c1e1866d34c7059c3903eebd4216 Mon Sep 17 00:00:00 2001 From: Etaash Mathamsetty <45927311+Etaash-mathamsetty@users.noreply.github.com> Date: Mon, 1 Sep 2025 17:04:15 -0500 Subject: [PATCH 453/454] amdxc: Add support for MLSR-WATERMARK env. --- dlls/amdxc64/main.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/dlls/amdxc64/main.c b/dlls/amdxc64/main.c index 6df95578748e..4b04848a862c 100644 --- a/dlls/amdxc64/main.c +++ b/dlls/amdxc64/main.c @@ -390,7 +390,7 @@ HRESULT STDMETHODCALLTYPE AmdExtD3DFactory_CreateInterface(IAmdExtD3DFactory *if HRESULT STDMETHODCALLTYPE AmdExtD3DFactory_QueryInterface(IAmdExtD3DFactory *iface, REFIID iid, void **out) { - TRACE("%p %s %p", iface, debugstr_guid(iid), out); + TRACE("%p %s %p\n", iface, debugstr_guid(iid), out); return E_NOINTERFACE; } @@ -431,3 +431,23 @@ HMODULE WINAPI AmdGetDxcModuleHandle(void) { return GetModuleHandleA(NULL); } + +BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, void *reserved) +{ + const char *env; + + switch (reason) + { + case DLL_PROCESS_ATTACH: + { + if ((env = getenv("FSR4_WATERMARK")) && !strcmp(env, "1")) + { + _putenv("MLSR-WATERMARK=1"); + } + break; + } + default: break; + } + + return TRUE; +} From 1afea7b0e257dd9fda43f65318231f7970f97215 Mon Sep 17 00:00:00 2001 From: Etaash Mathamsetty <45927311+Etaash-mathamsetty@users.noreply.github.com> Date: Fri, 29 Aug 2025 18:22:03 -0500 Subject: [PATCH 454/454] amdxc: Properly check for WMMA support. --- dlls/amdxc64/amdxc_interfaces.idl | 26 ++++++++ dlls/amdxc64/main.c | 102 ++++++++++++++++++++++++++++-- 2 files changed, 124 insertions(+), 4 deletions(-) diff --git a/dlls/amdxc64/amdxc_interfaces.idl b/dlls/amdxc64/amdxc_interfaces.idl index d446d0c4a2e6..8a6c8e0011ee 100644 --- a/dlls/amdxc64/amdxc_interfaces.idl +++ b/dlls/amdxc64/amdxc_interfaces.idl @@ -22,6 +22,10 @@ import "wtypes.idl"; import "unknwn.idl"; import "d3d12.idl"; +typedef struct VkInstance_T *VkInstance; +typedef struct VkDevice_T *VkDevice; +typedef struct VkPhysicalDevice_T *VkPhysicalDevice; +typedef struct VkQueue_T *VkQueue; /* the following are publicly available */ @@ -323,3 +327,25 @@ interface IAmdExtD3DDevice8 : IAmdExtD3DDevice7 { HRESULT GetWaveMatrixProperties([in, out] SIZE_T *pCount, [in, out] AmdExtWaveMatrixProperties *pProperties); } + +/* taken from vkd3d-proton */ + +[ + uuid(39da4e09-bd1c-4198-9fae-86bbe3be41fd), + object, + local, + pointer_default(unique) +] +interface ID3D12DXVKInteropDevice : IUnknown +{ + HRESULT GetDXGIAdapter(REFIID iid, void **object); + HRESULT GetInstanceExtensions(UINT *extension_count, const char **extensions); + HRESULT GetDeviceExtensions(UINT *extension_count, const char **extensions); + HRESULT GetDeviceFeatures(const void **features); + HRESULT GetVulkanHandles(VkInstance *vk_instance, VkPhysicalDevice *vk_physical_device, VkDevice *vk_device); + HRESULT GetVulkanQueueInfo(ID3D12CommandQueue *queue, VkQueue *vk_queue, UINT32 *vk_queue_family); + void GetVulkanImageLayout(ID3D12Resource *resource, D3D12_RESOURCE_STATES state, void *vk_layout); + HRESULT GetVulkanResourceInfo(ID3D12Resource *resource, UINT64 *vk_handle, UINT64 *buffer_offset); + HRESULT LockCommandQueue(ID3D12CommandQueue *queue); + HRESULT UnlockCommandQueue(ID3D12CommandQueue *queue); +} diff --git a/dlls/amdxc64/main.c b/dlls/amdxc64/main.c index 4b04848a862c..d96406b62121 100644 --- a/dlls/amdxc64/main.c +++ b/dlls/amdxc64/main.c @@ -28,6 +28,7 @@ #include "winternl.h" #include "wine/debug.h" #include "wine/heap.h" +#include "wine/vulkan.h" #define COBJMACROS #include "initguid.h" @@ -37,10 +38,84 @@ WINE_DEFAULT_DEBUG_CHANNEL(amdxc); +static BOOL check_fsr4_supported(ID3D12Device *device) +{ + ID3D12DXVKInteropDevice *interop; + VkInstance instance; + VkDevice vk_device; + VkPhysicalDevice phys_device; + VkPhysicalDeviceProperties2 prop = {0}; + VkPhysicalDeviceDriverProperties driver_prop = {0}; + const char **extensions = NULL; + UINT extension_count = 0; + UINT major, minor; + BOOL has_float8 = FALSE, has_coopmat2 = FALSE, has_coopmat = FALSE; + BOOL rdna3_workaround = FALSE, ret = FALSE; + const char *env = getenv("DXIL_SPIRV_CONFIG"); + + if (FAILED(ID3D12Device_QueryInterface(device, &IID_ID3D12DXVKInteropDevice, (void **)&interop))) + return FALSE; + + if (FAILED(ID3D12DXVKInteropDevice_GetVulkanHandles(interop, &instance, &phys_device, &vk_device))) + goto fail; + + prop.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; + prop.pNext = &driver_prop; + driver_prop.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES; + + vkGetPhysicalDeviceProperties2(phys_device, &prop); + + if (prop.properties.vendorID != 0x1002) goto fail; + /* only RADV supports FSR4 */ + if (driver_prop.driverID != VK_DRIVER_ID_MESA_RADV) goto fail; + + major = VK_API_VERSION_MAJOR(prop.properties.driverVersion); + minor = VK_API_VERSION_MINOR(prop.properties.driverVersion); + + if (FAILED(ID3D12DXVKInteropDevice_GetDeviceExtensions(interop, &extension_count, NULL))) + goto fail; + + extensions = malloc(sizeof(*extensions) * extension_count); + + if (FAILED(ID3D12DXVKInteropDevice_GetDeviceExtensions(interop, &extension_count, extensions))) + goto fail; + + for (UINT i = 0; i < extension_count; i++) + { + if (!strcmp("VK_NV_cooperative_matrix2", extensions[i])) + has_coopmat2 = TRUE; + if (!strcmp("VK_KHR_cooperative_matrix", extensions[i])) + has_coopmat = TRUE; + if (!strcmp("VK_EXT_shader_float8", extensions[i])) + has_float8 = TRUE; + } + + if (env && strstr(env, "wmma_rdna3_workaround")) + rdna3_workaround = TRUE; + + if (major > 25 || (major == 25 && minor >= 2)) + { + /* RDNA 4+ */ + if (has_coopmat2 && has_float8) ret = TRUE; + /* + * RDNA3 (or RDNA 2/1 with layer), + * ensure the user is doing stuff correctly + */ + if (has_coopmat && rdna3_workaround) ret = TRUE; + } + +fail: + if (extensions) free(extensions); + ID3D12DXVKInteropDevice_Release(interop); + + return ret; +} + struct AMDFSR4FFX { IAmdExtFfxApi IAmdExtFfxApi_iface; LONG ref; + BOOL fsr4_supported; }; static struct AMDFSR4FFX* impl_from_IAmdExtFfxApi(IAmdExtFfxApi* iface) @@ -77,6 +152,7 @@ HRESULT STDMETHODCALLTYPE AMDFSR4FFX_UpdateFfxApiProvider(IAmdExtFfxApi *iface, const char *env; updateffxapi_pfn pfn; HMODULE amdffx; + struct AMDFSR4FFX *this = impl_from_IAmdExtFfxApi(iface); TRACE("%p %p %u\n", iface, data, size); @@ -87,7 +163,13 @@ HRESULT STDMETHODCALLTYPE AMDFSR4FFX_UpdateFfxApiProvider(IAmdExtFfxApi *iface, amdffx = LoadLibraryA("amdxcffx64"); if (!amdffx) { - ERR("Failed to load FSR4 dll (amdxcffx)!\n"); + ERR("Failed to load FSR4 dll (amdxcffx64)!\n"); + return E_NOINTERFACE; + } + + if (!this->fsr4_supported) + { + ERR("FSR4 not supported on this system!\n"); return E_NOINTERFACE; } @@ -178,6 +260,7 @@ struct AmdExtD3DDevice8 { IAmdExtD3DDevice8 IAmdExtD3DDevice8_iface; LONG ref; + BOOL fsr4_supported; }; struct AmdExtD3DDevice8 *impl_from_IAmdExtD3DDevice8(IAmdExtD3DDevice8 *iface) @@ -301,6 +384,7 @@ HRESULT STDMETHODCALLTYPE AmdExtD3DDevice8_CreateComputePipelineCrossCompile(IAm HRESULT STDMETHODCALLTYPE AmdExtD3DDevice8_GetWaveMatrixProperties(IAmdExtD3DDevice8 *iface, SIZE_T *pCount, AmdExtWaveMatrixProperties *pProperties) { + struct AmdExtD3DDevice8 *this = impl_from_IAmdExtD3DDevice8(iface); static AmdExtWaveMatrixProperties prop[1] = {{ 16, 16, 16, AMD_EXT_WMMA_TYPE_FP8, AMD_EXT_WMMA_TYPE_FP8, AMD_EXT_WMMA_TYPE_FP32, AMD_EXT_WMMA_TYPE_FP32, FALSE}}; @@ -311,9 +395,15 @@ HRESULT STDMETHODCALLTYPE AmdExtD3DDevice8_GetWaveMatrixProperties(IAmdExtD3DDev if (*pCount >= 1) { - *pCount = 1; - memcpy(pProperties, prop, sizeof(prop)); - return S_OK; + if (this->fsr4_supported) + { + *pCount = 1; + memcpy(pProperties, prop, sizeof(prop)); + return S_OK; + } else { + *pCount = 0; + return S_OK; + } } /* FIXME: Handle pCount == 0 */ return S_OK; @@ -379,6 +469,8 @@ HRESULT STDMETHODCALLTYPE AmdExtD3DFactory_CreateInterface(IAmdExtD3DFactory *if struct AmdExtD3DDevice8 *this = calloc(1, sizeof(struct AmdExtD3DDevice8)); this->IAmdExtD3DDevice8_iface.lpVtbl = &AmdExtD3DDevice8_vtable; this->ref = 1; + this->fsr4_supported = check_fsr4_supported((ID3D12Device *)outer); + TRACE("FSR 4 supported: %d\n", this->fsr4_supported); *out = &this->IAmdExtD3DDevice8_iface; return S_OK; } else { @@ -410,6 +502,8 @@ HRESULT CDECL AmdExtD3DCreateInterface(IUnknown *outer, REFIID iid, void **obj) struct AMDFSR4FFX* ffx = calloc(1, sizeof(struct AMDFSR4FFX)); ffx->IAmdExtFfxApi_iface.lpVtbl = &AMDFSR4FFX_vtable; ffx->ref = 1; + ffx->fsr4_supported = check_fsr4_supported((ID3D12Device *)outer); + TRACE("FSR 4 supported: %d\n", ffx->fsr4_supported); *obj = &ffx->IAmdExtFfxApi_iface; return S_OK; } else if (IsEqualGUID(iid, &IID_IAmdExtAntiLagApi)) {