diff --git a/configure.ac b/configure.ac index 716ba305502..dff8f27cedc 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 @@ -2446,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/advapi32/security.c b/dlls/advapi32/security.c index cbf7f40a9e6..fed8ef5179f 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; } /****************************************************************************** diff --git a/dlls/amd_ags_x64/amd_ags.h b/dlls/amd_ags_x64/amd_ags.h index f6e50cad8f3..2b967436222 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 27c8d885416..32b47f2f553 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 5fa09aba8ce..3ba410cb933 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 7e5bc5bf4d6..d34a162b55c 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) diff --git a/dlls/amdxc64/Makefile.in b/dlls/amdxc64/Makefile.in new file mode 100644 index 00000000000..a27f1da40ff --- /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 00000000000..5c6ecb267df --- /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 00000000000..8a6c8e0011e --- /dev/null +++ b/dlls/amdxc64/amdxc_interfaces.idl @@ -0,0 +1,351 @@ +/* + * 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 + * 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"; +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 */ + +[ + 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); +} + +/* 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 IAmdExtD3DFactory : IUnknown +{ + HRESULT CreateInterface([in] IUnknown* outer, [in] REFIID iid, [in, out] void** out); +} + +[ + object, + uuid(ba019d53-ccab-4cbd-b56a-7230ed4330ad), + local +] +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; + +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 +{ + 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; + +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 +{ + 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 +{ + 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 new file mode 100644 index 00000000000..d96406b6212 --- /dev/null +++ b/dlls/amdxc64/main.c @@ -0,0 +1,547 @@ +/* + * 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" +#include "wine/vulkan.h" + +#define COBJMACROS +#include "initguid.h" +#include "d3d12.h" + +#include "amdxc_interfaces.h" + +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) +{ + 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; + struct AMDFSR4FFX *this = impl_from_IAmdExtFfxApi(iface); + + 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 (amdxcffx64)!\n"); + return E_NOINTERFACE; + } + + if (!this->fsr4_supported) + { + ERR("FSR4 not supported on this system!\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 AmdExtD3DShaderIntrinsics +{ + IAmdExtD3DShaderIntrinsics IAmdExtD3DShaderIntrinsics_iface; + LONG ref; +}; + +struct AmdExtD3DShaderIntrinsics* impl_from_IAmdExtD3DShaderIntrinsics(IAmdExtD3DShaderIntrinsics *iface) +{ + return CONTAINING_RECORD(iface, struct AmdExtD3DShaderIntrinsics, IAmdExtD3DShaderIntrinsics_iface); +} + +ULONG STDMETHODCALLTYPE AmdExtD3DShaderIntrinsics_AddRef(IAmdExtD3DShaderIntrinsics *iface) +{ + struct AmdExtD3DShaderIntrinsics *this = impl_from_IAmdExtD3DShaderIntrinsics(iface); + return InterlockedIncrement(&this->ref); +} + +ULONG STDMETHODCALLTYPE AmdExtD3DShaderIntrinsics_Release(IAmdExtD3DShaderIntrinsics *iface) +{ + struct AmdExtD3DShaderIntrinsics *this = impl_from_IAmdExtD3DShaderIntrinsics(iface); + ULONG ret = InterlockedDecrement(&this->ref); + if (!ret) free(this); + return ret; +} + +HRESULT STDMETHODCALLTYPE AmdExtD3DShaderIntrinsics_QueryInterface(IAmdExtD3DShaderIntrinsics *iface, REFIID iid, void **out) +{ + FIXME("%p %s %p stub!\n", iface, debugstr_guid(iid), out); + return E_NOINTERFACE; +} + +HRESULT STDMETHODCALLTYPE AmdExtD3DShaderIntrinsics_GetInfo(IAmdExtD3DShaderIntrinsics *iface, + AmdExtD3DShaderIntrinsicsInfo *info) +{ + FIXME("%p %p stub!\n", iface, info); + return S_OK; +} + +HRESULT STDMETHODCALLTYPE AmdExtD3DShaderIntrinsics_CheckSupport(IAmdExtD3DShaderIntrinsics *iface, + AmdExtD3DShaderIntrinsicsSupport opcode) +{ + if (opcode == AmdExtD3DShaderIntrinsicsSupport_Float8Conversion) return S_OK; + if (opcode == AmdExtD3DShaderIntrinsicsSupport_WaveMatrix) return S_OK; + + FIXME("%p %u stub!\n", iface, opcode); + return S_OK; +} + +HRESULT STDMETHODCALLTYPE AmdExtD3DShaderIntrinsics_Enable(IAmdExtD3DShaderIntrinsics *iface) +{ + TRACE("%p\n", iface); + /* shader intrinsics are always handled by vkd3d-proton */ + return S_OK; +} + +const static struct IAmdExtD3DShaderIntrinsicsVtbl AmdExtD3DShaderIntrinsics_vtable = { + AmdExtD3DShaderIntrinsics_QueryInterface, + AmdExtD3DShaderIntrinsics_AddRef, + AmdExtD3DShaderIntrinsics_Release, + AmdExtD3DShaderIntrinsics_GetInfo, + AmdExtD3DShaderIntrinsics_CheckSupport, + AmdExtD3DShaderIntrinsics_Enable +}; + +struct AmdExtD3DDevice8 +{ + IAmdExtD3DDevice8 IAmdExtD3DDevice8_iface; + LONG ref; + BOOL fsr4_supported; +}; + +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) +{ + 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}}; + + TRACE("%p %p %p\n", iface, pCount, pProperties); + + if (!pCount) return E_INVALIDARG; + + if (*pCount >= 1) + { + 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; +} + +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; + LONG ref; +}; + +struct AmdExtD3DFactory* impl_from_IAmdExtD3DFactory(IAmdExtD3DFactory *iface) +{ + return CONTAINING_RECORD(iface, struct AmdExtD3DFactory, IAmdExtD3DFactory_iface); +} + +ULONG STDMETHODCALLTYPE AmdExtD3DFactory_AddRef(IAmdExtD3DFactory *iface) +{ + struct AmdExtD3DFactory *this = impl_from_IAmdExtD3DFactory(iface); + return InterlockedIncrement(&this->ref); +} + +ULONG STDMETHODCALLTYPE AmdExtD3DFactory_Release(IAmdExtD3DFactory *iface) +{ + struct AmdExtD3DFactory *this = impl_from_IAmdExtD3DFactory(iface); + ULONG ret = InterlockedDecrement(&this->ref); + if (!ret) free(this); + return ret; +} + +HRESULT STDMETHODCALLTYPE AmdExtD3DFactory_CreateInterface(IAmdExtD3DFactory *iface, IUnknown *outer, REFIID iid, void **out) +{ + TRACE("%p %p %s %p\n", iface, outer, debugstr_guid(iid), out); + + 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; + 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 { + FIXME("unknown guid %s\n", debugstr_guid(iid)); + } + + return E_NOINTERFACE; +} + +HRESULT STDMETHODCALLTYPE AmdExtD3DFactory_QueryInterface(IAmdExtD3DFactory *iface, REFIID iid, void **out) +{ + TRACE("%p %s %p\n", iface, debugstr_guid(iid), out); + return E_NOINTERFACE; +} + +static const struct IAmdExtD3DFactoryVtbl AmdExtD3DFactory_vtable = { + AmdExtD3DFactory_QueryInterface, + AmdExtD3DFactory_AddRef, + AmdExtD3DFactory_Release, + AmdExtD3DFactory_CreateInterface +}; + +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; + 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)) { + return ID3D12Device_QueryInterface((ID3D12Device *)outer, &IID_IAmdExtAntiLagApi, obj); + } 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->IAmdExtD3DFactory_iface; + return S_OK; + } else { + FIXME("unknown guid: %s\n", debugstr_guid(iid)); + } + + return E_NOINTERFACE; +} + +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; +} diff --git a/dlls/appwiz.cpl/addons.c b/dlls/appwiz.cpl/addons.c index 1feaf77103d..d12e9182d0c 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.2.0" #if defined(__i386__) || defined(__x86_64__) #define MONO_ARCH "x86" -#define MONO_SHA "dbaca73e5d09f7a3a7c157ad04289af9ca47c3ced7012d46544a607046902b87" +#define MONO_SHA "4e1ed3f02e92d053133d03ddfbefcf6db4a4dc231a9aed3367b17117a88847d8" #else #define MONO_ARCH "" #define MONO_SHA "???" diff --git a/dlls/bcrypt/bcrypt_internal.h b/dlls/bcrypt/bcrypt_internal.h index dd80869b4c1..ea6acf5b448 100644 --- a/dlls/bcrypt/bcrypt_internal.h +++ b/dlls/bcrypt/bcrypt_internal.h @@ -64,11 +64,13 @@ enum alg_id ALG_ID_DH, ALG_ID_ECDH_P256, ALG_ID_ECDH_P384, + ALG_ID_ECDH_P521, /* signature */ ALG_ID_RSA_SIGN, ALG_ID_ECDSA_P256, ALG_ID_ECDSA_P384, + ALG_ID_ECDSA_P521, ALG_ID_DSA, /* rng */ @@ -278,4 +280,9 @@ enum key_funcs unix_funcs_count, }; +static inline ULONG len_from_bitlen( ULONG bitlen ) +{ + return (bitlen + 7) / 8; +} + #endif /* __BCRYPT_INTERNAL_H */ diff --git a/dlls/bcrypt/bcrypt_main.c b/dlls/bcrypt/bcrypt_main.c index 9c7e052196f..0d94ed8bc5b 100644 --- a/dlls/bcrypt/bcrypt_main.c +++ b/dlls/bcrypt/bcrypt_main.c @@ -118,9 +118,11 @@ 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 }, + { 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 }, @@ -247,11 +249,11 @@ 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 }, - {{ 0 }}, /* ECDSA_P512 */ + {{ MAGIC_ALG }, ALG_ID_ECDSA_P521 }, {{ MAGIC_ALG }, ALG_ID_RSA_SIGN }, }; @@ -809,7 +811,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; @@ -1736,42 +1738,52 @@ 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_ECDH_P521: + bitlen = 521; + magic = BCRYPT_ECDH_PUBLIC_P521_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; + 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; } 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,37 +1797,52 @@ 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_ECDH_P521: + bitlen = 521; + magic = BCRYPT_ECDH_PRIVATE_P521_MAGIC; + break; + case ALG_ID_ECDSA_P256: - key_size = 32; + bitlen = 256; magic = BCRYPT_ECDSA_PRIVATE_P256_MAGIC; break; + case ALG_ID_ECDSA_P384: + bitlen = 384; + 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; } 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; @@ -1921,7 +1948,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 +1981,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; @@ -2181,8 +2208,10 @@ 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: return BCRYPT_ECCPUBLIC_BLOB; case BCRYPT_RSAPUBLIC_MAGIC: @@ -2621,7 +2650,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 48651318289..eb4189a4ca8 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; @@ -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; @@ -1062,6 +1067,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 +1088,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 ); @@ -1104,7 +1114,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 +1183,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; @@ -1523,6 +1533,12 @@ 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 ); + break; + default: FIXME( "algorithm %u not supported\n", key->alg_id ); return STATUS_NOT_SUPPORTED; @@ -1579,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; @@ -1589,6 +1610,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 +1628,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 +1672,11 @@ 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; + default: FIXME( "algorithm %u not yet supported\n", key->alg_id ); return STATUS_NOT_IMPLEMENTED; @@ -1681,7 +1712,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 +1809,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 +1882,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 ); @@ -1911,6 +1942,10 @@ 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; + default: FIXME( "algorithm %u not yet supported\n", key->alg_id ); return STATUS_NOT_IMPLEMENTED; @@ -2023,7 +2058,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; @@ -2069,8 +2104,10 @@ 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: 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 ); @@ -2156,8 +2193,10 @@ 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: 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 +2339,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 +2406,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 +2501,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 +2524,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 +2591,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) @@ -2635,7 +2678,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; @@ -2711,8 +2754,10 @@ 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: { gnutls_ecc_curve_t curve; gnutls_datum_t x, y, k; @@ -2775,8 +2820,10 @@ 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: { gnutls_ecc_curve_t curve; gnutls_datum_t x, y; @@ -3229,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 ); @@ -3402,6 +3455,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) @@ -3434,9 +3488,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 ); @@ -3512,7 +3571,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; diff --git a/dlls/bcrypt/tests/bcrypt.c b/dlls/bcrypt/tests/bcrypt.c index 2a14675cdb5..a990750b462 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[] = @@ -2519,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); @@ -2618,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); @@ -3005,57 +3125,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; @@ -3064,11 +3165,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"); @@ -3084,8 +3185,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); @@ -3105,8 +3206,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); @@ -3116,7 +3217,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; @@ -3127,11 +3228,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); @@ -3146,7 +3247,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; @@ -3154,7 +3255,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: @@ -3166,7 +3267,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 */ @@ -3191,40 +3292,144 @@ 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); +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 + }; - 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 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, + }; - 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 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, + }; - 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, + }, + { + 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; - 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[] = @@ -3587,6 +3792,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) @@ -4018,6 +4249,17 @@ 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 }, + { BCRYPT_ECDH_P521_ALGORITHM, 521 }, + }; static const ULONG length = 1024; BCRYPT_DH_PARAMETER_HEADER *dh_header; BCRYPT_DH_KEY_BLOB *dh_key_blob; @@ -4028,61 +4270,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); diff --git a/dlls/cfgmgr32/tests/Makefile.in b/dlls/cfgmgr32/tests/Makefile.in index 64a01ccf492..90f5ac496a3 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 1f3df8f68a2..ece23562483 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/comctl32/combo.c b/dlls/comctl32/combo.c index 09579952f22..dfa2e1e60fd 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 0ba720a03ce..5af530ad5be 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; + 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; + 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; + 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,12 @@ 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); + 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 +840,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 +1726,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(); diff --git a/dlls/crypt32/msg.c b/dlls/crypt32/msg.c index 5e6d59a1f58..0525eda7a80 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; } @@ -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; @@ -1424,19 +1431,18 @@ 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], &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; @@ -3199,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); @@ -3211,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/sip.c b/dlls/crypt32/sip.c index 132f491b2f1..d9da19b6272 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; } diff --git a/dlls/crypt32/tests/msg.c b/dlls/crypt32/tests/msg.c index fa48d978e17..ef9c05d46f4 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); diff --git a/dlls/d3dx9_36/effect.c b/dlls/d3dx9_36/effect.c index 628d8ebc3eb..a89dc679a77 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; diff --git a/dlls/dbghelp/Makefile.in b/dlls/dbghelp/Makefile.in index 152ef80611a..dd27e159f18 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/coff.c b/dlls/dbghelp/coff.c index 21b4f726dd3..d4d804d935e 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; @@ -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"); @@ -219,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, @@ -242,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, @@ -281,12 +279,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 */, 0)->symt); continue; } @@ -317,15 +315,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 */, 0)->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 */, 0); } i += naux; continue; @@ -354,7 +352,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; } @@ -378,7 +376,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 @@ -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); } @@ -436,15 +435,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/cpu_i386.c b/dlls/dbghelp/cpu_i386.c index a322dfcdda3..42e4c882009 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.c b/dlls/dbghelp/dbghelp.c index 5a023152ec4..a46d052b8c7 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_index2ptr(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? */ @@ -761,6 +764,7 @@ BOOL WINAPI SymSetScopeFromInlineContext(HANDLE hProcess, ULONG64 addr, DWORD in } } +#ifndef _WIN64 /****************************************************************** * reg_cb64to32 (internal) * @@ -806,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) @@ -874,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 a89c2c1d86b..c9ae3bbb73c 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/dbghelp_private.h b/dlls/dbghelp/dbghelp_private.h index 8def6a7dae6..d16468dc07a 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" @@ -45,6 +46,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 { @@ -181,11 +183,13 @@ 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 { 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[]; @@ -201,9 +205,9 @@ 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; - unsigned source; + const char *filename; struct vector vchildren; /* global variables & functions */ void* user; /* when debug info provider needs to store information */ }; @@ -213,8 +217,8 @@ struct symt_data struct symt symt; struct hash_table_elt hash_elt; /* if global symbol */ enum DataKind kind; - struct symt* container; - struct symt* type; + symref_t container; + symref_t type; union /* depends on kind */ { /* DataIs{Global, FileStatic, StaticLocal}: @@ -290,11 +294,12 @@ 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 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 */ 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[]; }; @@ -303,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; }; @@ -311,8 +316,8 @@ struct symt_public { struct symt symt; struct hash_table_elt hash_elt; - struct symt* container; /* compiland */ - BOOL is_function; + symref_t container; /* compiland */ + BOOL is_function; ULONG_PTR address; ULONG_PTR size; }; @@ -321,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 */ @@ -385,7 +390,7 @@ struct symt_typedef { struct symt symt; struct hash_table_elt hash_elt; - struct symt* type; + symref_t type; }; struct symt_udt @@ -410,23 +415,71 @@ enum format_info DFI_MACHO, DFI_DWARF, DFI_PDB, + DFI_OLD_PDB, DFI_LAST }; -struct module_format +struct lineinfo_t { - struct module* module; - void (*remove)(struct process* pcs, struct module_format* modfmt); - void (*loc_compute)(struct process* pcs, - const struct module_format* modfmt, + 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 */ + 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); + 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); + 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, 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); + 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); + 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, + PSYM_ENUMSOURCEFILES_CALLBACKW cb, void *user); +}; + +struct module_format +{ + struct module* module; + const struct module_format_vtable* vtable; + union { 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 macho_module_info* macho_info; + struct old_pdb_module_info* old_pdb_info; struct pdb_module_info* pdb_info; } u; }; @@ -452,6 +505,12 @@ struct module /* specific information for debug types */ struct module_format* format_info[DFI_LAST]; unsigned debug_format_bitmask; + /* 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; @@ -469,7 +528,6 @@ struct module /* types */ struct hash_table ht_types; - struct vector vtypes; /* source files */ unsigned sources_used; @@ -478,6 +536,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 @@ -753,14 +834,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, @@ -773,6 +849,14 @@ 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_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, + 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* @@ -795,6 +879,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, @@ -827,6 +912,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* @@ -834,47 +920,48 @@ 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, 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, DWORD_PTR user); 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, + DWORD_PTR user, 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, @@ -898,22 +985,46 @@ 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, 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); +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));} + 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); +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, + 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* @@ -921,17 +1032,17 @@ 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, 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); @@ -943,11 +1054,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 3190dfecd0a..12b7f28a22b 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; @@ -712,6 +712,141 @@ static BOOL dwarf2_fill_attr(const dwarf2_parse_context_t* ctx, return TRUE; } +static struct symt *symt_get_real_type(struct symt *symt) +{ + while (symt && symt->tag == SymTagTypedef) + symt = (struct 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: + uinteger = attr->u.uvalue; + sinteger = (int)(unsigned int)attr->u.uvalue; + break; + + case DW_FORM_udata: + case DW_FORM_data8: + sinteger = uinteger = attr->u.lluvalue; + break; + + case DW_FORM_sdata: + uinteger = sinteger = 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); + return TRUE; + 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); + } + 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; +} + static dwarf2_debug_info_t* dwarf2_jump_to_debug_info(struct attribute* attr); static BOOL dwarf2_find_attribute(const dwarf2_debug_info_t* di, @@ -1061,12 +1196,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 +1432,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; @@ -1369,7 +1518,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; @@ -1501,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; @@ -1673,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; @@ -1745,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); @@ -1838,14 +1988,22 @@ 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; TRACE("%s\n", dwarf2_debug_di(di)); + 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)) value.u.svalue = 0; - symt_add_enum_element(di->unit_ctx->module_ctx->module, parent, name.u.string, value.u.svalue); + 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"); } @@ -1855,7 +2013,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; @@ -1866,20 +2024,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; } @@ -1961,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: @@ -1982,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; } } @@ -1990,71 +2148,21 @@ 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_udata: - case DW_FORM_addr: - V_VT(&v) = VT_UI4; - V_UI4(&v) = value.u.uvalue; - break; - - 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_I4; - V_I4(&v) = value.u.svalue; - 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, param_type)) 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", 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 { @@ -2065,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 { @@ -2076,11 +2184,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 +2219,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 +2237,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); + symt_ptr_to_symref(dwarf2_parse_subroutine_type(di)), 0, num_ranges); subpgm->current_func = inlined; subpgm->current_block = NULL; @@ -2183,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); } @@ -2254,7 +2351,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 +2402,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 +2448,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, + 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); @@ -2446,8 +2540,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 +2564,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 +2653,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: { @@ -2685,7 +2778,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 +2805,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; @@ -2831,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; } @@ -2858,7 +2951,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; @@ -2955,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; @@ -2971,10 +3065,11 @@ 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); + 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++) @@ -2982,7 +3077,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; @@ -3026,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"); @@ -3037,13 +3132,13 @@ 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 symt** psym = NULL; + struct process *pcs = modfmt->module->process; + struct symt* sym; struct location* pframe; dwarf2_traverse_context_t lctx; enum location_error err; @@ -3051,10 +3146,10 @@ static enum location_error loc_compute_frame(struct process* pcs, 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) @@ -3078,7 +3173,7 @@ static enum location_error loc_compute_frame(struct process* pcs, } 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: @@ -3915,8 +4010,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) { @@ -3934,10 +4028,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; + ip = pcs->localscope_pc - ((struct symt_compiland*)SYMT_SYMREF_TO_PTR(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) { @@ -4095,7 +4190,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); @@ -4114,7 +4209,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 */ @@ -4122,10 +4217,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); + + if (!(*punit_ctx = pool_alloc(&module_ctx->module->pool, sizeof(dwarf2_parse_context_t)))) + return FALSE; - unit_ctx->module_ctx = module_ctx; - dwarf2_parse_compilation_unit_head(unit_ctx, &mod_ctx); + (*punit_ctx)->module_ctx = module_ctx; + dwarf2_parse_compilation_unit_head(*punit_ctx, &mod_ctx); } /* phase2: load content of all CU @@ -4135,7 +4233,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,14 +4287,27 @@ 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; } +static const struct module_format_vtable dwarf2_module_format_vtable = +{ + dwarf2_module_remove, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + dwarf2_location_compute, +}; + BOOL dwarf2_parse(struct module* module, ULONG_PTR load_offset, const struct elf_thunk_area* thunks, struct image_file_map* fmap) @@ -4249,8 +4360,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 7f5c05b4682..a62a36f32f6 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); @@ -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; @@ -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,19 +771,19 @@ 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.) */ 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 +794,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 +805,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 @@ -857,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) { @@ -886,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) { @@ -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, 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"); @@ -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 b1f457d122d..2b1f35c4d78 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, 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; @@ -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 b8848f4010a..e8f2dfeae3b 100644 --- a/dlls/dbghelp/module.c +++ b/dlls/dbghelp/module.c @@ -253,14 +253,13 @@ 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(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 */ 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); module->sources_used = 0; module->sources_alloc = 0; @@ -903,6 +902,7 @@ BOOL image_check_alternate(struct image_file_map* fmap, const struct module* mod return FALSE; } +#ifndef _WIN64 /*********************************************************************** * SymLoadModule (DBGHELP.@) */ @@ -912,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.@) @@ -1080,9 +1081,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); @@ -1094,20 +1094,20 @@ 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; + } } } } - 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); @@ -1129,6 +1129,7 @@ BOOL module_remove(struct process* pcs, struct module* module) return FALSE; } +#ifndef _WIN64 /****************************************************************** * SymUnloadModule (DBGHELP.@) * @@ -1137,6 +1138,7 @@ BOOL WINAPI SymUnloadModule(HANDLE hProcess, DWORD BaseOfDll) { return SymUnloadModule64(hProcess, BaseOfDll); } +#endif /****************************************************************** * SymUnloadModule64 (DBGHELP.@) @@ -1155,6 +1157,7 @@ BOOL WINAPI SymUnloadModule64(HANDLE hProcess, DWORD64 BaseOfDll) return TRUE; } +#ifndef _WIN64 /****************************************************************** * SymEnumerateModules (DBGHELP.@) * @@ -1185,6 +1188,7 @@ BOOL WINAPI SymEnumerateModules(HANDLE hProcess, return SymEnumerateModulesW64(hProcess, enum_modW64_32, &x); } +#endif /****************************************************************** * SymEnumerateModules64 (DBGHELP.@) @@ -1274,6 +1278,7 @@ BOOL WINAPI EnumerateLoadedModules64(HANDLE hProcess, return EnumerateLoadedModulesW64(hProcess, enum_load_modW64_64, &x); } +#ifndef _WIN64 /****************************************************************** * EnumerateLoadedModules (DBGHELP.@) * @@ -1304,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) { @@ -1340,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)) { @@ -1420,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; } @@ -1429,6 +1444,7 @@ static void dbghelp_str_WtoA(const WCHAR *src, char *dst, int dst_len) dst[dst_len - 1] = 0; } +#ifndef _WIN64 /****************************************************************** * SymGetModuleInfo (DBGHELP.@) * @@ -1489,6 +1505,7 @@ BOOL WINAPI SymGetModuleInfoW(HANDLE hProcess, DWORD dwAddr, return TRUE; } +#endif /****************************************************************** * SymGetModuleInfo64 (DBGHELP.@) @@ -1584,6 +1601,7 @@ BOOL WINAPI SymGetModuleInfoW64(HANDLE hProcess, DWORD64 dwAddr, return TRUE; } +#ifndef _WIN64 /*********************************************************************** * SymGetModuleBase (DBGHELP.@) */ @@ -1591,6 +1609,7 @@ DWORD WINAPI SymGetModuleBase(HANDLE hProcess, DWORD dwAddr) { return (DWORD)SymGetModuleBase64(hProcess, dwAddr); } +#endif /*********************************************************************** * SymGetModuleBase64 (DBGHELP.@) @@ -1622,7 +1641,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; @@ -1665,6 +1683,7 @@ BOOL WINAPI SymRefreshModuleList(HANDLE hProcess) return module_refresh_list(pcs); } +#ifndef _WIN64 /*********************************************************************** * SymFunctionTableAccess (DBGHELP.@) */ @@ -1672,6 +1691,7 @@ PVOID WINAPI SymFunctionTableAccess(HANDLE hProcess, DWORD AddrBase) { return SymFunctionTableAccess64(hProcess, AddrBase); } +#endif /*********************************************************************** * SymFunctionTableAccess64 (DBGHELP.@) diff --git a/dlls/dbghelp/msc.c b/dlls/dbghelp/msc.c index 22ec5230f7a..43ee8509a0d 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,7 +61,6 @@ enum pdb_kind {PDB_JG, PDB_DS}; struct pdb_file_info { enum pdb_kind kind; - HANDLE hMap; const char* image; struct pdb_stream_name* stream_dict; unsigned fpoext_stream; @@ -81,7 +79,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]; @@ -137,7 +135,7 @@ 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 */ }; /* FIXME: don't make it static */ #define CV_MAX_MODULES 32 @@ -180,6 +178,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 +188,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 +219,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 +250,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; @@ -273,15 +280,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: @@ -395,7 +402,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 +444,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; @@ -578,9 +586,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); @@ -596,6 +605,7 @@ 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->defined_types[idx]) { if (cv_current_module->defined_types[idx] != dt) @@ -640,6 +650,16 @@ 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) +{ + return symt_ptr_to_symref(codeview_fetch_type(ctp, typeno)); +} + +static symref_t codeview_get_symref(struct module *module, unsigned typeno) +{ + return symt_ptr_to_symref(codeview_get_type(typeno, TRUE)); +} + static UINT32 codeview_compute_hash(const char* ptr, unsigned len) { const char* last = ptr + len; @@ -891,19 +911,21 @@ static BOOL codeview_add_type_enum_field_list(struct codeview_type_parse* ctp, { case LF_ENUMERATE_V1: { - int value, vlen = numeric_leaf(&value, type->enumerate_v1.data); + VARIANT v; + 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]; - 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: { - int value, vlen = numeric_leaf(&value, type->enumerate_v3.data); + VARIANT v; + int vlen = leaf_as_variant(&v, type->enumerate_v3.data); const char* name = (const char*)&type->enumerate_v3.data[vlen]; - 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; } @@ -941,13 +963,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), + 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, - 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; @@ -959,7 +981,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, codeview_get_symref(ctp->module, type), + value, 0, 0); } } @@ -1395,12 +1418,17 @@ 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); 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, @@ -1530,7 +1558,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); } @@ -1545,7 +1574,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 @@ -1569,7 +1598,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; @@ -1587,6 +1616,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++) @@ -1634,11 +1664,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; @@ -1664,7 +1695,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) @@ -1714,6 +1745,7 @@ static void codeview_snarf_linetab2(const struct msc_debug_info* msc_dbg, const } hdr = hdr_next; } + return TRUE; } /*======================================================================== @@ -1754,8 +1786,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; @@ -1785,7 +1818,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); + codeview_get_symref(msc_dbg->module, symtype), name); return; } if (!dontcheck && !in_tls) @@ -1805,7 +1838,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 @@ -1818,7 +1851,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)); + codeview_get_symref(msc_dbg->module, symtype)); } } @@ -2128,6 +2161,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) { @@ -2153,15 +2187,15 @@ 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), - num_ranges); + codeview_get_symref(msc_dbg->module, cvt->func_id_v3.type), + 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_type(cvt->mfunc_id_v3.type, FALSE), - num_ranges); + codeview_get_symref(msc_dbg->module, cvt->mfunc_id_v3.type), + user, num_ranges); break; default: FIXME("unsupported inlinee kind %x\n", cvt->generic.id); @@ -2251,7 +2285,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. @@ -2260,11 +2293,11 @@ 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) - 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, src_idx); + return symt_new_compiland(msc_dbg->module, objname); } static BOOL codeview_snarf(const struct msc_debug_info* msc_dbg, @@ -2276,7 +2309,6 @@ static BOOL codeview_snarf(const struct msc_debug_info* msc_dbg, struct symt_function* curr_func = NULL; int i, length; struct symt_block* block = NULL; - struct symt* symt; struct symt_compiland* compiland = NULL; struct location loc; unsigned top_frame_size = -1; @@ -2374,47 +2406,53 @@ 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); + 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), i))) + { + 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); + 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), i))) + { + 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); + 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), i))) + { + 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. @@ -2427,7 +2465,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), + codeview_get_symref(msc_dbg->module, sym->stack_v1.symtype), terminate_string(&sym->stack_v1.p_name)); break; case S_BPREL32_ST: @@ -2438,7 +2476,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), + codeview_get_symref(msc_dbg->module, sym->stack_v2.symtype), terminate_string(&sym->stack_v2.p_name)); break; case S_BPREL32: @@ -2451,7 +2489,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), + codeview_get_symref(msc_dbg->module, sym->stack_v3.symtype), sym->stack_v3.name); break; case S_REGREL32: @@ -2464,7 +2502,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), + codeview_get_symref(msc_dbg->module, sym->regrel_v3.symtype), sym->regrel_v3.name); break; @@ -2473,8 +2511,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, + codeview_get_symref(msc_dbg->module, sym->register_v1.type), terminate_string(&sym->register_v1.p_name)); break; case S_REGISTER_ST: @@ -2482,8 +2520,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, + codeview_get_symref(msc_dbg->module, sym->register_v2.type), terminate_string(&sym->register_v2.p_name)); break; case S_REGISTER: @@ -2493,8 +2531,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, + codeview_get_symref(msc_dbg->module, sym->register_v3.type), sym->register_v3.name); break; @@ -2585,81 +2623,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), - se, &v); + codeview_get_symref(msc_dbg->module, sym->constant_v1.type), &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), - se, &v); + codeview_get_symref(msc_dbg->module, sym->constant_v2.type), &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, se, &v); + symt_new_constant(msc_dbg->module, compiland, name, + codeview_get_symref(msc_dbg->module, sym->constant_v3.type), &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, - 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), + 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, + symt_new_typedef(msc_dbg->module, codeview_get_symref(msc_dbg->module, sym->udt_v2.type), 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, 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), + sym->udt_v3.name); } break; case S_LOCAL: @@ -2673,7 +2695,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), + codeview_get_symref(msc_dbg->module, sym->local_v3.symtype), sym->local_v3.name); } else @@ -2684,6 +2706,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) @@ -2705,6 +2728,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) @@ -2723,8 +2747,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; @@ -2768,7 +2792,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 @@ -2784,16 +2808,34 @@ 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; + + 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: case S_FRAMECOOKIE: case S_SECTION: case S_COFFGROUP: 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 @@ -2819,7 +2861,6 @@ static BOOL codeview_snarf(const struct msc_debug_info* msc_dbg, break; } } - if (cvmod) codeview_snarf_linetab2(msc_dbg, cvmod); return TRUE; } @@ -2835,8 +2876,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) { @@ -2849,7 +2889,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: @@ -2900,11 +2940,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; } @@ -2928,39 +2970,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, + symt_new_typedef(msc_dbg->module, codeview_get_symref(msc_dbg->module, sym->udt_v1.type), 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, - 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), + 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, 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), + sym->udt_v3.name); } break; default: return FALSE; @@ -3071,12 +3101,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; } @@ -3156,6 +3186,9 @@ static void pdb_free_file(struct pdb_file_info* pdb_file) break; } HeapFree(GetProcessHeap(), 0, pdb_file->stream_dict); + pdb_file->stream_dict = NULL; + UnmapViewOfFile(pdb_file->image); + pdb_file->image = NULL; } static struct pdb_stream_name* pdb_load_stream_name_table(const char* str, unsigned cb) @@ -3229,17 +3262,15 @@ 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; - 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]); - 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); + 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); } HeapFree(GetProcessHeap(), 0, modfmt); } @@ -3484,7 +3515,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"); @@ -3521,7 +3552,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) { @@ -3576,8 +3607,9 @@ 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, - unsigned module_index); + struct old_pdb_module_info *pdb_module_info, + unsigned module_index, + BOOL *has_linenumber_info); DWORD pdb_get_file_indexinfo(void* image, DWORD size, SYMSRV_INDEX_INFOW* info) { @@ -3591,7 +3623,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) { @@ -3635,7 +3667,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) @@ -3693,7 +3725,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) @@ -3712,6 +3744,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"); @@ -3719,7 +3752,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)); } @@ -3738,8 +3771,9 @@ 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, - unsigned module_index) + struct old_pdb_module_info *pdb_module_info, + unsigned module_index, + BOOL *has_linenumber_info) { HANDLE hFile = NULL, hMap = NULL; char* image = NULL; @@ -3749,6 +3783,7 @@ 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, @@ -3761,15 +3796,16 @@ static BOOL pdb_process_internal(const struct process *pcs, CloseHandle(hFile); return FALSE; } + CloseHandle(hFile); + CloseHandle(hMap); + /* old pdb reader */ if (!pdb_init(pdb_file, image)) { - CloseHandle(hMap); UnmapViewOfFile(image); return FALSE; } - pdb_file->hMap = hMap; pdb_file->image = image; symbols_image = pdb_read_stream(pdb_file, 3); if (symbols_image) @@ -3827,9 +3863,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); } } @@ -3851,16 +3888,25 @@ 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, 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"); + FIXME("Both line info present... preferring second\n"); + if (sfile.lineno2_size) + { + if (((SymGetOptions() & SYMOPT_LOAD_LINES) && codeview_snarf_linetab2(msc_dbg, &cvmod))) + *has_linenumber_info = TRUE; + } else if (sfile.lineno_size) - codeview_snarf_linetab(msc_dbg, - modimage + sfile.symbol_size, - sfile.lineno_size, - pdb_file->kind == PDB_JG); - + { + 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; @@ -3876,9 +3922,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))) @@ -3900,37 +3948,69 @@ static BOOL pdb_process_internal(const struct process *pcs, pdb_free(symbols_image); pdb_free(files_image); + pdb_free_file(pdb_file); + return TRUE; } +static const struct module_format_vtable old_pdb_module_format_vtable = +{ + pdb_module_remove, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + 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 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 pdb_module_info)))) + path_find_symbol_file(pcs, msc_dbg->module, filename, TRUE, guid, timestamp, age, &info, &unmatched)) { - BOOL ret; - - 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->u.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_PDB]->u.pdb_info, -1); - codeview_clear_type_table(); if (ret) { msc_dbg->module->module.SymType = SymPdb; @@ -3941,7 +4021,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; @@ -3949,8 +4029,6 @@ static BOOL pdb_process_file(const struct process *pcs, return TRUE; } - msc_dbg->module->format_info[DFI_PDB] = NULL; - HeapFree(GetProcessHeap(), 0, modfmt); } msc_dbg->module->module.SymType = SymNone; if (guid) @@ -3962,356 +4040,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, &v1) || !pev_pop_val(pev, &v2)) 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*), 8); - 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. */ @@ -4340,7 +4068,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); @@ -4381,27 +4110,31 @@ 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) - codeview_snarf_linetab(msc_dbg, msc_dbg->root + next->lfo, - next->cb, TRUE); - - if (prev && prev->iMod == ent->iMod && prev->SubSection == sstSrcModule) - codeview_snarf_linetab(msc_dbg, msc_dbg->root + prev->lfo, - prev->cb, 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; + } } } 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; @@ -4440,8 +4173,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; } @@ -4631,3 +4365,48 @@ DWORD dbg_get_file_indexinfo(void* image, DWORD size, SYMSRV_INDEX_INFOW* info) return msc_get_file_indexinfo(image, dbg, num_directories, info); } + +BOOL old_pdb_virtual_unwind(struct cpu_stack_walk *csw, DWORD_PTR ip, union ctx *context) +{ + struct module_pair pair; + 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_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; + + 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), + &context->x86); + 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 00000000000..c4f4319de43 --- /dev/null +++ b/dlls/dbghelp/pdb.c @@ -0,0 +1,5247 @@ +/* + * 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 "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_block_t)(struct pdb_reader *pdb, unsigned block_no, void **block_buffer); + +struct pdb_reader_walker +{ + unsigned stream_id; + pdbsize_t offset; + pdbsize_t last; +}; + +struct pdb_type_details +{ + pdbsize_t stream_offset; /* inside TPI stream */ + cv_typ_t resolved_cv_typeid; +}; + +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 +{ + enum pdb_action_type action_type : 2; + unsigned action_length : 14; + pdbsize_t stream_offset; + symref_t container_symref; +}; + +struct pdb_type_hash_entry +{ + cv_typ_t cv_typeid; + struct pdb_type_hash_entry *next; +}; + +struct pdb_dbi_hash_entry +{ + pdbsize_t dbi_stream_offset; + 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; + HANDLE file; + /* 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; + struct PDB_DS_TOC *toc; + + /* stream information */ + struct + { + const uint32_t *blocks; /* points into toc */ + char *name; + } *streams; + char *stream_names; + + unsigned source_listed : 1, + TPI_types_invalid : 1, + IPI_types_invalid : 1; + + /* 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; + + PDB_TYPES ipi_header; + struct pdb_reader_walker ipi_walker; + + /* 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; + unsigned short dbi_substreams[16]; /* 0 means non existing stream */ + + /* compilands */ + unsigned num_compilands; + struct pdb_compiland *compilands; + + /* cache PE module sections for mapping... + * we should rather use pe_module information + */ + const IMAGE_SECTION_HEADER *sections; + unsigned num_sections; + + /* 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 +{ + R_PDB_SUCCESS, + R_PDB_IOERROR, + 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, +}; + +#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; +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) +{ + 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 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"; + +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 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; +} + +static inline enum pdb_result pdb_reader_realloc(struct pdb_reader *pdb, void **ptr, size_t size) +{ + void *new = pool_realloc(&pdb->pool, *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) +{ + pool_free(&pdb->pool, 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; + void *block_buffer; + + while (size) + { + toread = min(pdb->block_size - delta, size); + + if ((result = (pdb->fetch)(pdb, *blocks, &block_buffer))) return result; + memcpy(buffer, (char *)block_buffer + delta, toread); + 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; +} + +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 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) +{ + 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; +} + +struct symref_code +{ + 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) +{ + code->kind = symref_code_cv_typeid; + code->cv_typeid = cv_typeid; + 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; + 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) + v = T_MAXPREDEFINEDTYPE + (code->cv_typeid - pdb->tpi_header.first_index); + else + return R_PDB_INVALID_ARGUMENT; + 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; + } + *symref = (v << 2) | 1; + 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 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 container_symref, 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->action_store[pdb->num_action_entries].container_symref = container_symref; + pdb->num_action_entries++; + + return R_PDB_SUCCESS; +} + +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, + const IMAGE_SECTION_HEADER *sections, unsigned num_sections) +{ + 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->module = module; + 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))) + { + 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; + 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_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))) + 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; + } + /* 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; + +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) +{ + CloseHandle(pdb->file); + /* note: pdb is allocated inside its pool, so this must be last line */ + pool_destroy(&pdb->pool); +} + +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; + + 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_IOERROR; + 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 (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))) + 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_PDB_FILE; + } + + 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_NOT_FOUND; +} + +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_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; + 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); +} + +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, PDB_STREAM_DBI, 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 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) +{ + 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) +{ + 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_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; + 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_segment_offset(struct pdb_reader *pdb, unsigned segment, unsigned offset, unsigned *compiland) +{ + enum pdb_result result; + 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_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_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) +{ + 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; + /* 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) + */ + 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; +} + +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) +{ + struct pdb_reader_compiland_iterator compiland_iter; + enum pdb_result result; + + 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; +} + +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) + { + 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; + + 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); +} + +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; + + pdb = pdb_get_current_reader(modfmt); + 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; + + pdb = pdb_get_current_reader(modfmt); + 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; + + 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. + * 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 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; +} + +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_leaf_as_int(const unsigned char *data, int *value) +{ + unsigned short int type; + + type = *(unsigned short int*)data; + if (type < LF_NUMERIC) + { + *value = type; + } + else + { + 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: + PDB_REPORT_UNEXPECTED("numeric leaf", type); + return R_PDB_INVALID_ARGUMENT; + } + } + + 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); + WCHAR *stringW; + + 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 */ +#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 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; + 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; + } + *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) + { + walker.offset = entry->dbi_stream_offset; + if ((result = pdb_reader_alloc_and_read_full_codeview_symbol(pdb, &walker, &full_cv_symbol))) return result; + if (pdb_reader_extract_name_out_of_codeview_symbol(full_cv_symbol, &cv_name, &cv_length) == 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); + } + } + } + TRACE("not found in hash bucket %s\n", debugstr_a(name)); + 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_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; + struct pdb_reader_compiland_iterator compiland_iter; + struct pdb_reader_walker walker; + struct pdb_reader_whole_stream whole; + const union codeview_symbol *cv_global_symbol, *cv_global_symbol2; + unsigned hash; + 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].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; + } + if ((result = pdb_reader_load_DBI_hash_table(pdb))) return result; + + 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++) + { + 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) + { + if (!pdb_reader_whole_stream_access_codeview_symbol(pdb, &whole, entry->dbi_stream_offset, &cv_global_symbol)) + { + switch (cv_global_symbol->generic.id) + { + 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)) + { + 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); + } + break; + } + } + } + } + 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); + + if ((result = pdb_reader_init_DBI_substreams(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; + 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; + + 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; + + /* 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 {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) +{ + struct symref_code code; + symref_t symref; + + if (!is_basic_supported(basic & T_BASICTYPE_MASK)) + { + PDB_REPORT_UNEXPECTED("basic type", basic); + return MR_FAILURE; + } + + switch (req) + { + case TI_GET_BASETYPE: + if (basic >= T_MAXBASICTYPE) return MR_FAILURE; + *((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 & 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; + 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; + 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; + } + return MR_SUCCESS; +} + +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; +} + +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 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 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 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 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; + enum pdb_result result; + 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; + + 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) + { + 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) +{ + 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; + 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; + if (!type_details->stream_offset) type_details->stream_offset = tpi_offset; + } + + *cv_typeid = type_details->resolved_cv_typeid; + 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; + 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; + struct symref_code code; + symref_t symref; + char *name; + VARIANT v; + BOOL ret; + + 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 */ + 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) + { + 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); + result = pdb_reader_alloc_and_read_codeview_type_variablepart(pdb, walker, &cv_type, &v, &name, NULL); + if (!result) + { + 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); + if (!ret) return MR_SUCCESS; + } + } + } + + /* 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) +{ + enum pdb_result result; + struct symref_code code; + symref_t symref; + + if (cv_typeid > T_MAXPREDEFINEDTYPE) + if ((result = pdb_reader_resolve_cv_typeid(pdb, cv_typeid, &cv_typeid))) 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; +} + +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_request_cv_typeid(struct pdb_reader *pdb, cv_typ_t cv_typeid, IMAGEHLP_SYMBOL_TYPE_INFO req, void *data) +{ + 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); +} + +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_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; + + switch (req) + { + case TI_GET_SYMTAG: + *((DWORD*)data) = SymTagArrayType; + return MR_SUCCESS; + case TI_GET_COUNT: + 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); + *((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 pdb_result pdb_reader_TPI_fillin_arglist(struct pdb_reader *pdb, unsigned num_ids, unsigned id_size, pdbsize_t tpi_offset, + TI_FINDCHILDREN_PARAMS *tfp) +{ + enum pdb_result result; + symref_t symref; + unsigned i; + + for (i = 0; i < num_ids; i++, tpi_offset += id_size) + { + 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; +} + +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 ((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 (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); + 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: + 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 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, unsigned *where, TI_FINDCHILDREN_PARAMS *tfp) +{ + enum pdb_result result; + symref_t 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 *where, + TI_FINDCHILDREN_PARAMS *tfp) + { + 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: + length = offsetof(union codeview_fieldtype, enumerate_v3.data); + length += codeview_leaf_as_variant(ptr + length, &v); + length += strlen((const char *)ptr + length) + 1; + if (*where >= tfp->Start + tfp->Count) + { + pdb_reader_free(pdb, cv_reftype); + return R_PDB_BUFFER_TOO_SMALL; + } + 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, + where, tfp))) 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; + 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: + *(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 pdb_result pdb_reader_TPI_fillin_UDTlist(struct pdb_reader *pdb, symref_t container_symref, + cv_typ_t udtlist_cv_typeid, unsigned *where, TI_FINDCHILDREN_PARAMS *tfp) +{ + 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, where, tfp); + 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, where, tfp))) return result; + length = sizeof(cv_field->index_v2); + break; + + default: + result = PDB_REPORT_UNEXPECTED("UDT field list", cv_field->generic.id); + length = 0; /* keep SAST happy */ + } + 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; + unsigned where = 0; + + if ((result = pdb_reader_TPI_fillin_UDTlist(pdb, container_symref, fieldlist_cv_typeid, &where, NULL))) return result; + *count = where; + 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; + 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; + 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; + 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 *tfp = data; + unsigned where = 0; + + 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: + 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_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); +} + +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_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_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; + 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 ((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; + } + } + pdb_reader_free(pdb, cv_field); + 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); + 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; + 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; + 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); + + 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; + break; + } + + return ret; +} + +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_type_details *type_details; + struct symref_code code; + + if (req == TI_GET_SYMINDEX) + { + *((DWORD*)data) = symt_symref_to_index(pdb->module, symref); + return MR_SUCCESS; + } + + if (pdb_reader_decode_symref(pdb, symref, &code)) return MR_FAILURE; + 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_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: + { + 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); + 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; + } + } + return MR_FAILURE; + default: + return MR_FAILURE; + } +} + +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; + + 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); +} + +BOOL cv_hack_ptr_to_symref(struct pdb_reader *pdb, cv_typ_t cv_typeid, symref_t *symref) +{ + struct symref_code code; + + 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 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; + + if ((result = pdb_reader_compiland_iterator_init(pdb, &compiland_iter))) return result; + do + { + if (!compiland--) + { + 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); + 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)) + { + 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; + } + 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) +{ + 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 (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; + 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, &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; + + 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; + + pdb = pdb_get_current_reader(modfmt); + 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 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; + + 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)); +} + +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; + + 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); + + 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 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; + + 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 + * 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 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 = +{ + 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, + 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, +}; + +BOOL pdb_init_modfmt(const struct process *pcs, + const struct msc_debug_info *msc_dbg, + const WCHAR *filename, BOOL *has_linenumber_info) +{ + 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) + { + HeapFree(GetProcessHeap(), 0, modfmt); + return FALSE; + } + + 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; + } + msc_dbg->module->format_info[DFI_PDB] = NULL; + HeapFree(GetProcessHeap(), 0, modfmt); + CloseHandle(file); + 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) +{ + 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'; +} + +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) +{ + 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, WOW64_CONTEXT *context) +{ + 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); + pev_push_context(&pev, context); + pev_push_fpodata(&pev, fpoext); + 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_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); + return FALSE; +} + +BOOL pdb_virtual_unwind(struct cpu_stack_walk *csw, DWORD_PTR ip, union ctx *context) +{ + struct pdb_reader *pdb; + struct pdb_reader_walker walker; + struct module_pair pair; + unsigned fpoext_stream; + PDB_FPO_DATA fpoext; + BOOL ret = FALSE; + + if (!module_init_pair(&pair, csw->hProcess, ip)) 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; + + fpoext_stream = pdb->dbi_substreams[PDB_SIDX_FPOEXT]; + 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, &context->x86); + pdb_reader_free(pdb, cmd); + break; + } + } + } + + return ret; +} diff --git a/dlls/dbghelp/pe_module.c b/dlls/dbghelp/pe_module.c index bd3d6000535..6b5a9a4b425 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); @@ -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, @@ -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/source.c b/dlls/dbghelp/source.c index f2dc84d4769..79b4ff9989c 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) { @@ -145,11 +151,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); @@ -157,10 +164,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 { @@ -168,6 +183,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) { @@ -183,10 +215,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); diff --git a/dlls/dbghelp/stabs.c b/dlls/dbghelp/stabs.c index 2f3ee9fff4e..8aa9907449c 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 ':': { @@ -718,9 +720,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 +733,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++; @@ -1168,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) @@ -1360,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: @@ -1375,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) @@ -1409,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; @@ -1473,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 @@ -1490,21 +1495,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: /* @@ -1539,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, + curr_func = symt_new_function(module, compiland, symname, load_offset + n_value, 0, - &func_type->symt); + symt_ptr_to_symref(&func_type->symt), 0); pending_flush(&pending_func, module, curr_func, NULL); } else @@ -1579,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/stack.c b/dlls/dbghelp/stack.c index 8b573feee77..a0fc0320668 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/storage.c b/dlls/dbghelp/storage.c index f7c08adab8a..d3fea475901 100644 --- a/dlls/dbghelp/storage.c +++ b/dlls/dbghelp/storage.c @@ -48,6 +48,11 @@ 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) +{ + HeapFree(pool->heap, 0, ptr); +} + char* pool_strdup(struct pool* pool, const char* str) { char* ret; @@ -65,10 +70,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; @@ -93,15 +99,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 38382b006d7..29a32ad7dcf 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_ptr2index(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_ptr2index(struct module* module, const struct symt* sym) 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_index2ptr(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,13 +116,13 @@ struct symt* symt_index2ptr(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); + return (id >= vector_length(vector)) ? 0 : *(symref_t *)vector_at(vector, id); } static BOOL symt_grow_sorttab(struct module* module, unsigned sz) @@ -226,28 +226,28 @@ 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(symref_t), 0); } 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; + symref_t* 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->container = symt_ptr_to_symref(&module->top->symt); sym->address = 0; - sym->source = src_idx; - vector_init(&sym->vchildren, sizeof(struct symt*), 32); + sym->filename = pool_strdup(&module->pool, filename); + 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; @@ -288,23 +288,23 @@ 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; + symref_t* 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)))) { 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(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", @@ -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; } @@ -321,20 +321,21 @@ 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, + DWORD_PTR user, 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; + sym->symt.tag = tag; 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); + 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(symref_t), 0); + sym->user = user; sym->num_ranges = num_ranges; } return sym; @@ -344,15 +345,16 @@ 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, 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; + symref_t* p; sym->ranges[0].low = addr; sym->ranges[0].high = addr + size; sym->next_inlinesite = NULL; /* first of list */ @@ -360,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; @@ -370,15 +372,16 @@ 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, + 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; + symref_t* p; assert(container); /* chain inline sites */ @@ -391,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; } @@ -429,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 */ @@ -439,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 */ @@ -461,17 +464,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; + symref_t* 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); @@ -483,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; @@ -504,13 +507,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; + symref_t* 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); @@ -521,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; } @@ -538,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); @@ -547,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*), 4); + 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; } @@ -562,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, @@ -574,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; } @@ -603,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; @@ -620,7 +628,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; @@ -633,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; @@ -662,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; @@ -694,7 +702,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) { @@ -704,17 +712,18 @@ static void 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 || - !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; sym_info->Flags = 0; sym_info->Value = 0; + sym_info->Address = 0; + sym_info->Register = 0; switch (sym->tag) { @@ -733,28 +742,28 @@ 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) { 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: sym_info->Flags |= SYMFLAG_REGISTER; sym_info->Register = loc.reg; - sym_info->Address = 0; break; case loc_regrel: sym_info->Flags |= SYMFLAG_REGREL; @@ -783,7 +792,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); @@ -792,8 +800,8 @@ static void 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)) { @@ -843,7 +851,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 */ @@ -861,6 +868,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 @@ -877,22 +885,55 @@ 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; 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))) { @@ -994,7 +1035,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 */ } @@ -1026,7 +1067,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; @@ -1072,6 +1113,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); @@ -1090,7 +1158,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; @@ -1098,7 +1166,8 @@ static BOOL symt_enum_locals_helper(struct module_pair* pair, for (i=0; itag) { case SymTagBlock: @@ -1237,9 +1306,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; @@ -1417,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; @@ -1443,6 +1513,7 @@ BOOL WINAPI SymEnumerateSymbols(HANDLE hProcess, DWORD BaseOfDll, return SymEnumSymbols(hProcess, BaseOfDll, NULL, sym_enumerate_cb, &se); } +#endif struct sym_enumerate64 { @@ -1574,6 +1645,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; @@ -1583,6 +1655,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))) { @@ -1635,7 +1725,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 */ @@ -1758,108 +1849,87 @@ BOOL WINAPI SymGetSymFromName(HANDLE hProcess, PCSTR Name, PIMAGEHLP_SYMBOL Symb return TRUE; } -struct internal_line_t +static void init_lineinfo(struct lineinfo_t* line_info, BOOL unicode) { - BOOL unicode; - PVOID key; - DWORD line_number; - union - { - CHAR* file_nameA; - WCHAR* file_nameW; - }; - DWORD64 address; -}; - -static void init_internal_line(struct internal_line_t* intl, 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) +#ifndef _WIN64 +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; } +#endif -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) +BOOL lineinfo_set_nameA(struct process* pcs, struct lineinfo_t* line_info, char* str) { 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); - } - else - intl->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 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) { 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); - } - else - intl->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 { 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; @@ -1871,9 +1941,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; } @@ -1883,12 +1953,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)); } 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); HeapFree( GetProcessHeap(), 0, dospath ); } if (ret && pdwDisplacement) *pdwDisplacement = addr - found_dli->u.address; @@ -1904,16 +1974,29 @@ 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; - + 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; - if ((symt = symt_find_symbol_at(pair.effective, addr)) == NULL) return FALSE; - if (symt->symt.tag != SymTagFunction && symt->symt.tag != SymTagInlineSite) return FALSE; - return get_line_from_function(&pair, (struct symt_function*)symt, addr, pdwDisplacement, intl); + 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); + + return ret; } /*********************************************************************** @@ -1932,6 +2015,7 @@ BOOL WINAPI SymGetSymNext64(HANDLE hProcess, PIMAGEHLP_SYMBOL64 Symbol) return FALSE; } +#ifndef _WIN64 /*********************************************************************** * SymGetSymNext (DBGHELP.@) */ @@ -1941,6 +2025,7 @@ BOOL WINAPI SymGetSymNext(HANDLE hProcess, PIMAGEHLP_SYMBOL Symbol) SetLastError(ERROR_CALL_NOT_IMPLEMENTED); return FALSE; } +#endif /*********************************************************************** * SymGetSymPrev64 (DBGHELP.@) @@ -1952,6 +2037,7 @@ BOOL WINAPI SymGetSymPrev64(HANDLE hProcess, PIMAGEHLP_SYMBOL64 Symbol) return FALSE; } +#ifndef _WIN64 /*********************************************************************** * SymGetSymPrev (DBGHELP.@) */ @@ -1961,7 +2047,9 @@ BOOL WINAPI SymGetSymPrev(HANDLE hProcess, PIMAGEHLP_SYMBOL Symbol) SetLastError(ERROR_CALL_NOT_IMPLEMENTED); return FALSE; } +#endif +#ifndef _WIN64 /****************************************************************** * SymGetLineFromAddr (DBGHELP.@) * @@ -1969,15 +2057,16 @@ 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); } +#endif /****************************************************************** * SymGetLineFromAddr64 (DBGHELP.@) @@ -1986,14 +2075,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); } /****************************************************************** @@ -2003,24 +2092,40 @@ 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; struct line_info* srcli; + struct module_format_vtable_iterator iter = {}; if (!module_init_pair(&pair, hProcess, addr)) return FALSE; + line_info->address = addr; + line_info->key = key; + while ((module_format_vtable_iterator_next(pair.effective, &iter, + MODULE_FORMAT_VTABLE_INDEX(advance_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 (key == NULL) return FALSE; li = key; @@ -2030,13 +2135,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)); } } SetLastError(ERROR_NO_MORE_ITEMS); /* FIXME */ @@ -2049,31 +2154,33 @@ 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); } +#ifndef _WIN64 /****************************************************************** * SymGetLinePrev (DBGHELP.@) * */ 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); } +#endif /****************************************************************** * SymGetLinePrevW64 (DBGHELP.@) @@ -2081,25 +2188,38 @@ 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; struct line_info* srcli; + struct module_format_vtable_iterator iter = {}; - if (key == NULL) return FALSE; if (!module_init_pair(&pair, hProcess, addr)) return FALSE; + line_info->address = addr; + line_info->key = key; + while ((module_format_vtable_iterator_next(pair.effective, &iter, + MODULE_FORMAT_VTABLE_INDEX(advance_line_info)))) + { + switch (iter.modfmt->vtable->advance_line_info(iter.modfmt, line_info, TRUE)) + { + case MR_SUCCESS: return TRUE; + default: break; + } + } + + if (key == NULL) return FALSE; /* search current source file */ for (srcli = key; !srcli->is_source_file; srcli--); @@ -2109,10 +2229,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)); } srcli = li; } @@ -2126,31 +2246,33 @@ 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); } +#ifndef _WIN64 /****************************************************************** * SymGetLineNext (DBGHELP.@) * */ 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); } +#endif /****************************************************************** * SymGetLineNextW64 (DBGHELP.@) @@ -2158,16 +2280,17 @@ 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); } +#ifndef _WIN64 /*********************************************************************** * SymUnDName (DBGHELP.@) */ @@ -2176,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.@) @@ -2359,13 +2483,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 +2516,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; } @@ -2524,18 +2659,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; @@ -2555,20 +2704,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, srcfile_regex, 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]) { @@ -2580,7 +2719,7 @@ BOOL WINAPI SymEnumLines(HANDLE hProcess, ULONG64 base, PCSTR compiland, } } } - HeapFree(GetProcessHeap(), 0, srcmask); + HeapFree(GetProcessHeap(), 0, srcfile_regex); return TRUE; } @@ -2615,14 +2754,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_index2ptr(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; } @@ -2730,7 +2870,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; @@ -2740,12 +2880,31 @@ 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 (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 */ 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; @@ -2758,16 +2917,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); } /****************************************************************** @@ -2776,16 +2935,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); } /****************************************************************** diff --git a/dlls/dbghelp/tests/dbghelp.c b/dlls/dbghelp/tests/dbghelp.c index f7313c3ab4f..41398784ef9 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/dbghelp/type.c b/dlls/dbghelp/type.c index b89c63c5b3d..a0529ec37fb 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 */ @@ -160,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; @@ -213,14 +214,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 } }; @@ -260,8 +253,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); - symt_add_type(module, &sym->symt); + vector_init(&sym->vchildren, sizeof(struct symt*), 0); } return sym; } @@ -289,7 +281,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; @@ -318,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; } @@ -345,14 +337,13 @@ 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); - symt_add_type(module, &sym->symt); + vector_init(&sym->vchildren, sizeof(struct symt*), 0); } return sym; } 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; @@ -365,10 +356,9 @@ 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->type = enum_type->base_type; - V_VT(&e->u.value) = VT_I4; - V_I4(&e->u.value) = value; + e->container = symt_ptr_to_symref(&enum_type->symt); + e->type = symt_ptr_to_symref(enum_type->base_type); + e->u.value = *variant; p = vector_add(&enum_type->vchildren, &module->pool); if (!p) return FALSE; /* FIXME we leak e */ @@ -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; } @@ -404,9 +393,8 @@ 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); } 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, symref_t ref, + const char* typename) { struct symt_typedef* sym; @@ -453,55 +440,107 @@ 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; } -/****************************************************************** - * SymEnumTypes (DBGHELP.@) - * - */ -BOOL WINAPI SymEnumTypes(HANDLE hProcess, ULONG64 BaseOfDll, - PSYM_ENUMERATESYMBOLS_CALLBACK EnumSymbolsCallback, - PVOID UserContext) +struct sym_modfmt_type_enum { - struct module_pair pair; - char buffer[sizeof(SYMBOL_INFO) + 256]; - SYMBOL_INFO* sym_info = (SYMBOL_INFO*)buffer; - struct symt* type; - DWORD64 size; - unsigned int i; + struct module *module; + SYMBOL_INFO *sym_info; + PSYM_ENUMERATESYMBOLS_CALLBACK cb; + void *user; + const char *type_name; +}; - TRACE("(%p %I64x %p %p)\n", hProcess, BaseOfDll, EnumSymbolsCallback, UserContext); +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); +} - if (!module_init_pair(&pair, hProcess, BaseOfDll)) return FALSE; +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; sym_info->SizeOfStruct = sizeof(SYMBOL_INFO); sym_info->MaxNameLen = sizeof(buffer) - sizeof(SYMBOL_INFO); - for (i=0; ivtypes); i++) + /* 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)))) { - type = *(struct symt**)vector_at(&pair.effective->vtypes, i); - sym_info->TypeIndex = symt_ptr2index(pair.effective, type); + 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); + return result != MR_FAILURE; + } + + 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; + + sym_info->TypeIndex = symt_ptr_to_index(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->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, symt_get_name(type)); - if (!EnumSymbolsCallback(sym_info, sym_info->Size, UserContext)) break; + 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; } +/****************************************************************** + * 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 +573,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 +601,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 +617,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; } @@ -670,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; @@ -697,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_ptr2index(module, *pt); + if (!(symref = (symref_t *)vector_at(v, tifp->Start + i))) return FALSE; + tifp->ChildId[tifp->Start + i] = symt_symref_to_index(module, *symref); } } break; @@ -842,7 +846,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; @@ -856,7 +860,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; @@ -883,25 +887,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_symref_to_index(module, ((const struct symt_compiland*)type)->container); break; case SymTagBlock: - X(DWORD) = symt_ptr2index(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_ptr2index(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_ptr2index(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_ptr2index(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_ptr2index(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: @@ -914,7 +918,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", @@ -1019,30 +1023,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_symref_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_symref_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_symref_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", @@ -1077,19 +1081,16 @@ 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*)SYMT_SYMREF_TO_PTR(((const struct symt_data*)type)->container), + &loc); + break; } if (loc.kind != loc_absolute) return FALSE; V_VT(&X(VARIANT)) = VT_UI4; /* FIXME */ @@ -1111,7 +1112,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: @@ -1119,7 +1120,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 */ @@ -1153,6 +1154,25 @@ 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_from_symref(module, symt_index_to_symref(module, index), req, pInfo); +} + +BOOL symt_get_info_from_symref(struct module* module, symref_t ref, + IMAGEHLP_SYMBOL_TYPE_INFO req, void* 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); + return result == MR_SUCCESS; + } + return FALSE; +} + /****************************************************************** * SymGetTypeInfo (DBGHELP.@) * @@ -1164,7 +1184,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); } /****************************************************************** @@ -1177,11 +1197,35 @@ BOOL WINAPI SymGetTypeFromName(HANDLE hProcess, ULONG64 BaseOfDll, struct module_pair pair; struct symt* type; DWORD64 size; + 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; + } + if (result == MR_FAILURE) 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; diff --git a/dlls/dinput/dinput_main.c b/dlls/dinput/dinput_main.c index d7c1d4729d5..62365f0b323 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; diff --git a/dlls/dinput/joystick_hid.c b/dlls/dinput/joystick_hid.c index 97a333b8c31..6efb9acd07b 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; @@ -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 @@ -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; @@ -1973,7 +1981,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 +1990,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 +2511,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 +3031,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/dinput/tests/force_feedback.c b/dlls/dinput/tests/force_feedback.c index 14bf9575e8f..89caa86af9d 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: diff --git a/dlls/dinput/tests/joystick8.c b/dlls/dinput/tests/joystick8.c index f765f5ba4ee..afc24e49c8f 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 ); @@ -5215,16 +5214,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/dmloader/loader.c b/dlls/dmloader/loader.c index 3798a0851a0..19c1c2ac991 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); diff --git a/dlls/dsound/dsound_main.c b/dlls/dsound/dsound_main.c index bb373a4304d..19e94c1071d 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; } /*************************************************************************** diff --git a/dlls/imagehlp/imagehlp.spec b/dlls/imagehlp/imagehlp.spec index 7d934dcc96e..4e784f8bf22 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) diff --git a/dlls/iphlpapi/iphlpapi.spec b/dlls/iphlpapi/iphlpapi.spec index facb52e0448..ed2214c99ce 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 ) @@ -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 172afbd62b8..7f39d771f61 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" @@ -1387,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; } @@ -2078,10 +2074,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 )); } /****************************************************************** @@ -3545,9 +3546,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 ) @@ -3644,19 +3648,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; @@ -4530,33 +4531,373 @@ 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; } /****************************************************************** - * GetBestRoute2 (IPHLPAPI.@) + * GetIpInterfaceEntry (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) +DWORD WINAPI GetIpInterfaceEntry( MIB_IPINTERFACE_ROW *row ) { - static int once; + 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; - if (!once++) - FIXME("(%p, %ld, %p, %p, 0x%08lx, %p, %p): stub\n", luid, index, source, - destination, options, bestroute, bestaddress); + key.luid = row->InterfaceLuid; + if (!key.luid.Value && ConvertInterfaceIndexToLuid( row->InterfaceIndex, &key.luid )) return ERROR_NOT_FOUND; - if (!destination || !bestroute || !bestaddress) + 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; +} + + +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; +} + +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.@) + */ +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; - return ERROR_NOT_SUPPORTED; + memset( bestroute, 0, sizeof(*bestroute) ); + memset( bestaddress, 0, sizeof(*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 (is_addr_unspecified( src, v6 )) src = NULL; + } + + 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; + } + + 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; + } + } + + 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) + { + 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 && !(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; } /****************************************************************** @@ -4659,6 +5000,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.@) */ @@ -4696,7 +5074,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; } @@ -4735,96 +5119,114 @@ 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 ); -} - -/*********************************************************************** - * 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; + struct icmp_apc_ctxt *ctxt = NULL; + 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) - { - heap_free( ctxt ); - SetLastError( ERROR_INVALID_PARAMETER ); - return 0; - } - - 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 ); - SetLastError( IP_NO_RESOURCES ); - return 0; - } + if (!in) 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; + 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 ); - request_event = event ? event : (apc_routine ? NULL : CreateEventW( NULL, 0, 0, NULL )); + if (event || apc_routine) + { + 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; + 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; + } + } + } + else + { + iosb = &iosb_local; + request_event = CreateEventW( NULL, 0, 0, NULL ); + } - status = NtDeviceIoControlFile( data->nsi_device, request_event, apc_routine ? icmp_apc_routine : NULL, - apc_routine ? ctxt : apc_ctxt, iosb, IOCTL_NSIPROXY_WINE_ICMP_ECHO, + 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 (!status) - ret = IcmpParseReplies( reply, reply_size ); - - if (!event && request_event) CloseHandle( request_event ); - if (!apc_routine || 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; } /*********************************************************************** @@ -4832,9 +5234,21 @@ 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(); +} + +/****************************************************************** + * 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; } /*********************************************************************** @@ -4844,9 +5258,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 397b2e8fad9..49c3059b5df 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" @@ -67,6 +69,8 @@ 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 ); DWORD WINAPI ConvertGuidToStringW( const GUID *, WCHAR *, DWORD ); @@ -88,6 +92,8 @@ static void loadIPHlpApi(void) pParseNetworkString = (void *)GetProcAddress(hLibrary, "ParseNetworkString"); pNotifyUnicastIpAddressChange = (void *)GetProcAddress(hLibrary, "NotifyUnicastIpAddressChange"); pCancelMibChangeNotify2 = (void *)GetProcAddress(hLibrary, "CancelMibChangeNotify2"); + pGetIpInterfaceTable = (void *)GetProcAddress(hLibrary, "GetIpInterfaceTable"); + pGetIpInterfaceEntry = (void *)GetProcAddress(hLibrary, "GetIpInterfaceEntry"); } } @@ -939,34 +945,55 @@ static void testSetTcpEntry(void) } static BOOL icmp_send_echo_test_apc_expect; -static void WINAPI icmp_send_echo_test_apc_xp(void *context) +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) { - ok(icmp_send_echo_test_apc_expect, "Unexpected APC execution\n"); - ok(context == (void*)0xdeadc0de, "Wrong context: %p\n", context); + 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; } -static void WINAPI icmp_send_echo_test_apc(void *context, IO_STATUS_BLOCK *io_status, ULONG reserved) +struct test_echo_thread_params { - 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); + 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 */ - 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)]; 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)); @@ -1023,6 +1050,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(); @@ -1236,6 +1269,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); @@ -1271,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); @@ -1285,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; @@ -1322,6 +1395,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 */ @@ -1330,16 +1419,22 @@ 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; - /* - 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); + memset(&icmp_send_echo_io, 0xcc, sizeof(icmp_send_echo_io)); + icmp_send_echo_test_line = __LINE__; + 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); + 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"); + 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)); @@ -1385,6 +1480,184 @@ 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 }; + 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(); @@ -1405,6 +1678,8 @@ static void testWinNT4Functions(void) testSetTcpEntry(); testIcmpSendEcho(); testIcmpParseReplies(); + test_Icmp6SendEcho(); + testIcmp6ParseReplies(); } static void testGetInterfaceInfo(void) @@ -1602,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 ); + ok( bestRoute.dwForwardMask == 0xffffffff, "got %#lx.\n", bestRoute.dwForwardMask ); + + if_row.dwIndex = bestRoute.dwForwardIfIndex; + err = GetIfEntry( &if_row ); + ok( !err, "got %lu.\n", err ); + ok( if_row.dwType == IF_TYPE_SOFTWARE_LOOPBACK, "got %#lx.\n", if_row.dwType ); } /* @@ -3106,6 +3389,589 @@ static void test_compartments(void) ok(id == NET_IF_COMPARTMENT_ID_PRIMARY, "got %u\n", id); } +static void test_GetIpInterface(void) +{ + MIB_IPINTERFACE_ROW entry_row; + 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 || !pGetIpInterfaceEntry) + { + win_skip( "GetIpInterfaceTable or GetIpInterfaceEntry 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 ); + + 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 ); + 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; @@ -3147,6 +4013,8 @@ START_TEST(iphlpapi) test_NotifyUnicastIpAddressChange(); test_ConvertGuidToString(); test_compartments(); + test_GetIpInterface(); + test_best_routes(); freeIPHlpApi(); } diff --git a/dlls/kernel32/debugger.c b/dlls/kernel32/debugger.c index 6ccce02a8f3..a1317464e67 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 ); } diff --git a/dlls/kernel32/tests/debugger.c b/dlls/kernel32/tests/debugger.c index 0782b8f21eb..e799187d8f5 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(); } } diff --git a/dlls/kernel32/tests/file.c b/dlls/kernel32/tests/file.c index c57edebb316..f021e940b8e 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/kernel32/tests/heap.c b/dlls/kernel32/tests/heap.c index c62eb449192..3e13c9ef9d5 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/kernel32/tests/loader.c b/dlls/kernel32/tests/loader.c index 0871ae3b57f..6714975d259 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++) diff --git a/dlls/kernel32/tests/locale.c b/dlls/kernel32/tests/locale.c index 60fa7d99a8a..c0cb9aeeded 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/dlls/kernelbase/debug.c b/dlls/kernelbase/debug.c index 6d24abc80ad..81d9a9127ea 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 ) diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c index f0dedfe3b14..c7d8119baf7 100644 --- a/dlls/kernelbase/file.c +++ b/dlls/kernelbase/file.c @@ -3254,36 +3254,25 @@ 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 ); -} - - -/*********************************************************************** - * GetOverlappedResultEx (kernelbase.@) - */ -BOOL WINAPI DECLSPEC_HOTPATCH GetOverlappedResultEx( HANDLE file, OVERLAPPED *overlapped, - DWORD *result, DWORD timeout, BOOL alertable ) { NTSTATUS status; DWORD ret; - TRACE( "(%p %p %p %lu %d)\n", file, overlapped, result, timeout, alertable ); + 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 (!timeout) + if (!wait) { SetLastError( ERROR_IO_INCOMPLETE ); return FALSE; } - ret = WaitForSingleObjectEx( overlapped->hEvent ? overlapped->hEvent : file, timeout, alertable ); - if (ret == WAIT_FAILED) - return FALSE; - else if (ret) + ret = WaitForSingleObject( overlapped->hEvent ? overlapped->hEvent : file, INFINITE ); + if (ret == WAIT_FAILED) return FALSE; + if (ret) { SetLastError( ret ); return FALSE; @@ -3292,11 +3281,55 @@ 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; +} + + +/*********************************************************************** + * GetOverlappedResultEx (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH GetOverlappedResultEx( HANDLE file, OVERLAPPED *overlapped, + DWORD *result, DWORD timeout, BOOL alertable ) +{ + NTSTATUS status = STATUS_PENDING; + BOOL compat_mode; + DWORD ret; + + TRACE( "(%p %p %p %lu %d)\n", file, overlapped, result, timeout, alertable ); + + 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) + { + ret = WaitForSingleObjectEx( overlapped->hEvent ? overlapped->hEvent : file, timeout, alertable ); + 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; } diff --git a/dlls/kernelbase/loader.c b/dlls/kernelbase/loader.c index f684d32e852..b67903c4e57 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; } @@ -561,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 ); diff --git a/dlls/kernelbase/path.c b/dlls/kernelbase/path.c index a737294e655..eea31159daa 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: diff --git a/dlls/kernelbase/process.c b/dlls/kernelbase/process.c index 7a51dfd231d..09e373f45f9 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; @@ -602,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-angle=vulkan"}, {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"}, @@ -618,7 +623,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]; @@ -2006,47 +2010,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) )) - { - 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; - } - } - } - } - - RtlInitUnicodeString( &us_name, name ); if (value) { diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c index 6ae5e529e02..17b414128b2 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) )) diff --git a/dlls/mf/session.c b/dlls/mf/session.c index 3f4eb4f5489..d1995595370 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,58 @@ 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; + } + 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. */ 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 +1200,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 +1214,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; @@ -1484,12 +1519,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. */ @@ -3243,6 +3272,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; @@ -3288,17 +3318,17 @@ static void session_set_source_object_state(struct media_session *session, IUnkn session_set_topo_status(session, S_OK, MF_TOPOSTATUS_STARTED_SOURCE); - 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))) + if (event_type == MESourceStarted || event_type == MEStreamStarted) { - session_set_started(session); - return; + 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)) { MFTIME preroll_time = 0; @@ -3333,15 +3363,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 faf82f676db..b1b4e997970 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); diff --git a/dlls/mf/tests/transform.c b/dlls/mf/tests/transform.c index ba72a1e625d..5dc8df6ac5a 100644 --- a/dlls/mf/tests/transform.c +++ b/dlls/mf/tests/transform.c @@ -8599,9 +8599,18 @@ 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); + 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 +8632,11 @@ 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); + ok(hr == S_OK, "GetOutputStatus returned %#lx.\n", hr); + 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 +8668,12 @@ 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); + ok(hr == S_OK, "GetOutputStatus returned %#lx.\n", hr); + todo_wine + ok(flags == 0, "Unexpected output status %#lx.\n", flags); } ref = IMFTransform_Release(transform); diff --git a/dlls/mfmediaengine/main.c b/dlls/mfmediaengine/main.c index d7d3383ab00..46f1f8e15cd 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; @@ -181,6 +182,7 @@ struct media_engine BYTE *buffer; UINT buffer_size; DXGI_FORMAT output_format; + BOOL format_mismatch; struct { @@ -900,6 +902,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 +969,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); @@ -1159,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))) { @@ -1841,6 +1864,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; @@ -2705,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); } @@ -3375,6 +3408,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); diff --git a/dlls/mfmediaengine/video_frame_sink.c b/dlls/mfmediaengine/video_frame_sink.c index 23d8e19e71e..a79cd5f8644 100644 --- a/dlls/mfmediaengine/video_frame_sink.c +++ b/dlls/mfmediaengine/video_frame_sink.c @@ -1012,11 +1012,9 @@ 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) - { - video_frame_sink_sample_queue_flush(sink); + 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) { diff --git a/dlls/mfplat/buffer.c b/dlls/mfplat/buffer.c index 76894d31175..33e8728c679 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,23 @@ 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") || !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 performance regressions for Resident Evil 7 Biohazard (418370) and + * Metal Gear Solid V (287700). + */ + 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 +342,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 +364,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") || !strcmp(sgi, "287700"))) + { + 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 +394,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 = diff --git a/dlls/mfreadwrite/reader.c b/dlls/mfreadwrite/reader.c index 6a1013ccd6c..ba58e0b007e 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 e90931253fc..2e75e5089b2 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; diff --git a/dlls/mfsrcsnk/media_source.c b/dlls/mfsrcsnk/media_source.c index 6ef8a3f8f4c..40f7ecd1074 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 (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); + 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; diff --git a/dlls/mlang/mlang.c b/dlls/mlang/mlang.c index 3ff3c8b34bb..650af2f7423 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); diff --git a/dlls/mmdevapi/client.c b/dlls/mmdevapi/client.c index d29969b80c5..07390b16527 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 236a4ff71c8..38e28e1ee7d 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) @@ -372,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"); diff --git a/dlls/msado15/connection.c b/dlls/msado15/connection.c index d6ca0c2df39..ea9302e0b81 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 76e1bb35b77..d1e22fa1434 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); diff --git a/dlls/mscoree/metahost.c b/dlls/mscoree/metahost.c index 46e22490e8e..83e52780511 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; diff --git a/dlls/mscoree/mscoree_private.h b/dlls/mscoree/mscoree_private.h index fe1abb872b0..aa542c317b3 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.2.0" /* Mono embedding */ typedef struct _MonoDomain MonoDomain; diff --git a/dlls/msctf/msctf_internal.h b/dlls/msctf/msctf_internal.h index 3696edf5500..e465b985bf2 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); diff --git a/dlls/mshtml/navigate.c b/dlls/mshtml/navigate.c index f05bf0be9c6..19837d247be 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) diff --git a/dlls/mshtml/range.c b/dlls/mshtml/range.c index f10a42a895d..fdd39f65615 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) diff --git a/dlls/msi/action.c b/dlls/msi/action.c index d5e3af9cdba..c5a0927feb4 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/appsearch.c b/dlls/msi/appsearch.c index 88450087196..092a61560a4 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/assembly.c b/dlls/msi/assembly.c index 47e8071502c..2452bb0b8d1 100644 --- a/dlls/msi/assembly.c +++ b/dlls/msi/assembly.c @@ -34,9 +34,9 @@ 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" ); + 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" ))) { diff --git a/dlls/msi/custom.c b/dlls/msi/custom.c index 12a7c3c3676..54e1897a2c5 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/files.c b/dlls/msi/files.c index 35166788dae..1ffa918b4a9 100644 --- a/dlls/msi/files.c +++ b/dlls/msi/files.c @@ -157,27 +157,41 @@ 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; + WCHAR temppath[MAX_PATH]; + DWORD size; + BYTE *buffer = NULL; + msi_disable_fs_redirection( package ); - if (buffer) size = GetFileVersionInfoW( path, 0, buflen, buffer ); - else size = GetFileVersionInfoSizeW( path, &handle ); + 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 )) + { + 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/msi_main.c b/dlls/msi/msi_main.c index a2b6b6a0503..32b4f9ad45d 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 e20ead557ce..bd8f6ec7a48 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 @@ -1114,7 +1116,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 * ); diff --git a/dlls/msi/package.c b/dlls/msi/package.c index b8966a0df90..b81e6913ddc 100644 --- a/dlls/msi/package.c +++ b/dlls/msi/package.c @@ -620,6 +620,29 @@ 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") + || !strcmp(s, "231430") + || !strcmp(s, "1017900") + || !strcmp(s, "285190") + || !strcmp(s, "627270") + ); + 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 +770,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) { @@ -777,7 +801,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 +822,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 ); diff --git a/dlls/msi/tests/install.c b/dlls/msi/tests/install.c index 3bf28cc1e54..4b967d1aa4b 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()); + 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); diff --git a/dlls/msvcp140_atomic_wait/main.c b/dlls/msvcp140_atomic_wait/main.c index 52ecae486eb..b144da3f04c 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" @@ -175,3 +176,127 @@ 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_current_zone +{ + enum tzdb_error error; + 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; + 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); +} + +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); +} + +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 88ed59adad9..e5c9aeec677 100644 --- a/dlls/msvcp140_atomic_wait/msvcp140_atomic_wait.spec +++ b/dlls/msvcp140_atomic_wait/msvcp140_atomic_wait.spec @@ -21,12 +21,12 @@ @ 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 -@ stub __std_tzdb_delete_leap_seconds +@ stdcall __std_tzdb_delete_current_zone(ptr) +@ stdcall __std_tzdb_delete_leap_seconds(ptr) @ stub __std_tzdb_delete_sys_info -@ stub __std_tzdb_delete_time_zones -@ stub __std_tzdb_get_current_zone -@ stub __std_tzdb_get_leap_seconds +@ stdcall __std_tzdb_delete_time_zones(ptr) +@ stdcall __std_tzdb_get_current_zone() +@ stdcall __std_tzdb_get_leap_seconds(ptr ptr) @ 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 882e07c48db..dd1c8a18773 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" @@ -28,6 +29,38 @@ 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; +}; + +struct tzdb_current_zone +{ + enum tzdb_error error; + 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); @@ -39,6 +72,12 @@ 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 *); +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) @@ -60,6 +99,12 @@ 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"); + 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; } @@ -269,6 +314,54 @@ 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_current_zone *c; + struct tzdb_time_zones *z; + void *leap_seconds; + unsigned int i; + size_t size; + + 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)); + 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) + { + 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); + + 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) { HMODULE msvcp; @@ -281,5 +374,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); } diff --git a/dlls/msvcp90/details.c b/dlls/msvcp90/details.c index 984a277ed12..c54ef2a232d 100644 --- a/dlls/msvcp90/details.c +++ b/dlls/msvcp90/details.c @@ -497,12 +497,11 @@ 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); + 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 { @@ -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; } diff --git a/dlls/nsi/tests/Makefile.in b/dlls/nsi/tests/Makefile.in index 45a7d77668f..50e331b3c3b 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 0697e09b1e0..0f1cb6db0b0 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,7 +809,74 @@ static void test_ip_forward( int family ) winetest_pop_context(); } + if (family == AF_INET) + { + 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 ); + NsiFreeTable( key_tbl, rw_tbl, dyn_tbl, stat_tbl ); + 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(); @@ -846,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; @@ -869,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 ); @@ -1111,6 +1191,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 ); diff --git a/dlls/nsiproxy.sys/device.c b/dlls/nsiproxy.sys/device.c index c64f61ed0fe..3e806ca9835 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" @@ -37,8 +38,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 +47,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 @@ -198,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; @@ -205,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 @@ -215,25 +214,103 @@ 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 ) { 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; } +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( &icmp_echo_completion_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; + LeaveCriticalSection( &icmp_echo_completion_cs ); + IoCompleteRequest( irp, IO_NO_INCREMENT ); + 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.hop_limit = in->hop_limit; + params.tos = in->tos; + params.src = &in->src; + 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]) || @@ -245,28 +322,18 @@ 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; } 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,111 +448,14 @@ 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; - - 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; LIST_ENTRY *entry, *next; NTSTATUS status; + SetThreadDescription( GetCurrentThread(), L"wine_nsi_notification" ); + while (!(status = nsiproxy_call( nsi_get_notification, ¶ms ))) { EnterCriticalSection( &nsiproxy_cs ); @@ -532,9 +502,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 ); diff --git a/dlls/nsiproxy.sys/icmp_echo.c b/dlls/nsiproxy.sys/icmp_echo.c index e92fa4394c1..7833704ec93 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; @@ -113,6 +135,12 @@ 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; + int hop_limit; + BOOL ping_socket; }; #define MAX_HANDLES 256 /* Max number of simultaneous pings - could become dynamic if need be */ @@ -185,11 +213,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; @@ -212,13 +242,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) @@ -322,8 +345,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[] = { @@ -352,7 +375,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; @@ -384,6 +407,8 @@ static int ipv4_parse_icmp_hdr_( struct icmp_data *data, struct icmp_hdr *icmp, 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. */ @@ -395,27 +420,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; @@ -468,7 +479,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 ); @@ -498,16 +509,213 @@ 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 +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 ) @@ -580,12 +788,14 @@ 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) ); 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 */ { @@ -599,6 +809,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 )) @@ -625,14 +836,27 @@ 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; + + 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) ); + 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 ); @@ -643,12 +867,10 @@ 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 ); - - dst_len = SOCKADDR_INET_to_sockaddr( params->dst, dst, sizeof(dst_storage) ); + 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, 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) @@ -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/ip.c b/dlls/nsiproxy.sys/ip.c index 32ba6241761..cc14871c750 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. @@ -761,10 +765,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; } } @@ -810,6 +814,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 ) { @@ -1215,7 +1396,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 ); @@ -1317,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 */ @@ -1597,8 +1817,9 @@ 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; + entry.loopback = entry.prefix_len == 128 && IN6_IS_ADDR_LOOPBACK(&entry.prefix); while (isspace( *ptr )) ptr++; end = ptr; @@ -1660,6 +1881,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 +1955,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/nsi.c b/dlls/nsiproxy.sys/nsi.c index 2f6d2d59573..a13d77a47a6 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; diff --git a/dlls/nsiproxy.sys/nsiproxy_private.h b/dlls/nsiproxy.sys/nsiproxy_private.h index 1b6eacf7d08..ab40fa2057a 100644 --- a/dlls/nsiproxy.sys/nsiproxy_private.h +++ b/dlls/nsiproxy.sys/nsiproxy_private.h @@ -41,10 +41,12 @@ struct icmp_listen_params struct icmp_send_echo_params { + SOCKADDR_INET *src; SOCKADDR_INET *dst; void *request, *reply; UINT request_size, reply_len; BYTE bits, ttl, tos; + int hop_limit; icmp_handle *handle; }; diff --git a/dlls/nsiproxy.sys/unix_private.h b/dlls/nsiproxy.sys/unix_private.h index b7c56710e22..5487de5111d 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/dlls/ntdll/heap.c b/dlls/ntdll/heap.c index 8c2a96aca1a..0a401b4cfaf 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) { diff --git a/dlls/ntdll/loader.c b/dlls/ntdll/loader.c index 75669e95b72..bc71fe04c94 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/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c index 190b82dd2fb..aca3744142e 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", diff --git a/dlls/ntdll/tests/info.c b/dlls/ntdll/tests/info.c index 7ee6d8c22c1..4379af9af35 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 ); diff --git a/dlls/ntdll/tests/threadpool.c b/dlls/ntdll/tests/threadpool.c index cea06fed791..c3f327aab7f 100644 --- a/dlls/ntdll/tests/threadpool.c +++ b/dlls/ntdll/tests/threadpool.c @@ -2387,6 +2387,92 @@ 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); + 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); + 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); + 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); + 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 +2494,5 @@ START_TEST(threadpool) test_tp_multi_wait(); test_tp_io(); test_kernel32_tp_io(); + test_tp_wait_early_closure(); } diff --git a/dlls/ntdll/tests/virtual.c b/dlls/ntdll/tests/virtual.c index 48ba57fd7ad..014c4ec07f5 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,13 +2517,51 @@ 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); + 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); + 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 == 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); ok(status == STATUS_SUCCESS, "Unexpected status %08lx.\n", status); + memset(&info, 0xcc, sizeof(info)); + status = NtQueryVirtualMemory(NtCurrentProcess(), ptr, MemoryRegionInformation, &info, sizeof(info), &len); + 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; @@ -2512,6 +2582,69 @@ 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"); + 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); + + *(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); + 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); + *(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); + 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"); + 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/threadpool.c b/dlls/ntdll/threadpool.c index 81cf894d943..5fb11c35698 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++; } @@ -1436,7 +1440,8 @@ 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; + 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/ntdll/unix/fsync.c b/dlls/ntdll/unix/fsync.c index c72f4786670..4d9f5cde7b2 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 f339dc64f9b..33e9dc274f7 100644 --- a/dlls/ntdll/unix/loader.c +++ b/dlls/ntdll/unix/loader.c @@ -2128,11 +2128,13 @@ 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; SIZE_T kernel_stack_size = 0x100000; long long ram_reporting_bias; +char *release_reserved_memory_low_bound; static void hacks_init(void) { @@ -2189,6 +2191,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") || !strcmp(sgi, "438490"); + if (fsync_help_simulated_pulse) + ERR("HACK: fsync: helping simulated pulse event.\n"); + switch (sgi ? atoi( sgi ) : -1) { case 25700: /* Madballs in Babo: Invasion */ @@ -2219,6 +2228,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"); @@ -2246,19 +2256,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 (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")) { @@ -2266,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/loadorder.c b/dlls/ntdll/unix/loadorder.c index bbe50928880..2157f85556d 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)) { diff --git a/dlls/ntdll/unix/signal_x86_64.c b/dlls/ntdll/unix/signal_x86_64.c index 10d39bc1098..473c4b994ad 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/socket.c b/dlls/ntdll/unix/socket.c index 74ff284e920..df786711f32 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; @@ -590,12 +599,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 +615,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 */ @@ -618,6 +636,60 @@ static unsigned short chksum(BYTE *data, unsigned int count) 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 ) { @@ -638,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; @@ -702,10 +777,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; @@ -829,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/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c index 5edca5fcf94..d0bba284e77 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; @@ -2320,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 ) diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h index cd070e0b98f..dc8c23a5aa9 100644 --- a/dlls/ntdll/unix/unix_private.h +++ b/dlls/ntdll/unix/unix_private.h @@ -196,10 +196,12 @@ 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; 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 355a88986a3..f9535370331 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; @@ -5106,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 */ @@ -5681,105 +5704,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; - /* Find the view containing the address */ + *fake_reserved = FALSE; + *region_start = NULL; + *region_end = working_set_limit; - 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) { - char *area_start = area->base; - char *area_end = (char *)area_start + area->size; + if (*region_start < area_end) *region_start = area_end; + continue; + } + if (area_start <= base || area_start <= (char *)address_space_start) + { + 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 (base < next) { - if (area_end < alloc_end) info->RegionSize = area_end - base; - in_reserved = TRUE; - break; + *region_end = min( next, *region_end ); + return NULL; } - /* report the remaining part of the 64K after the view as free */ - if ((UINT_PTR)alloc_base & granularity_mask) - { - 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; - } - /* 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 { @@ -5846,8 +5884,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; @@ -5858,15 +5900,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; - 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_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; + } + + server_leave_uninterrupted_section( &virtual_mutex, &sigset ); if (res_len) *res_len = sizeof(*info); return STATUS_SUCCESS; @@ -6854,20 +6929,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 ); @@ -6951,7 +7035,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/dlls/opengl32/unix_wgl.c b/dlls/opengl32/unix_wgl.c index 5ebe8ce9a38..f3ff2ec1ea4 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" ) ); } @@ -624,6 +625,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++) diff --git a/dlls/protontts/tts.c b/dlls/protontts/tts.c index f0f19d6ba21..06762939e86 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); @@ -166,6 +174,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,9 +202,12 @@ static HRESULT WINAPI ttsengine_Speak(ISpTTSEngine *iface, DWORD flags, REFGUID ISpTTSEngineSite *site) { 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); @@ -190,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))) @@ -208,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; @@ -223,17 +257,23 @@ static HRESULT WINAPI ttsengine_Speak(ISpTTSEngine *iface, DWORD flags, REFGUID void *buf; UINT32 size; - Sleep(50); + Sleep(10); - 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)) diff --git a/dlls/rtworkq/tests/rtworkq.c b/dlls/rtworkq/tests/rtworkq.c index 7655275d721..812c1f03c1e 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,70 @@ 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); + 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); + 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) diff --git a/dlls/sapi/Makefile.in b/dlls/sapi/Makefile.in index a6c4d86037e..8286ff8adc0 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 \ @@ -14,4 +14,5 @@ SOURCES = \ sapi_typelib.idl \ stream.c \ token.c \ - tts.c + tts.c \ + xml.c diff --git a/dlls/sapi/async.c b/dlls/sapi/async.c index 69f962d5502..10097150bd8 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 0369157acbc..5ec17ade0d4 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) diff --git a/dlls/sapi/sapi_private.h b/dlls/sapi/sapi_private.h index 219436f88c1..e47375b129a 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/stream.c b/dlls/sapi/stream.c index 5cd64125ade..6a43128acd5 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,11 @@ struct spstream { ISpStream ISpStream_iface; LONG ref; + + IStream *base_stream; + GUID format; + WAVEFORMATEX *wfx; + BOOL closed; }; static inline struct spstream *impl_from_ISpStream(ISpStream *iface) @@ -52,12 +58,14 @@ 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 { *obj = NULL; - FIXME("interface %s not implemented.\n", debugstr_guid(iid)); return E_NOINTERFACE; } @@ -84,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); } @@ -92,105 +102,228 @@ 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; } -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, - 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 || This->closed) + 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->closed) + return SPERR_STREAM_CLOSED; + else 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, @@ -205,9 +338,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 = @@ -242,6 +385,11 @@ 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; + This->closed = FALSE; + 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 ea2608eed36..cd90cb00399 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,12 +61,226 @@ 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); } +static void test_spstream(void) +{ + ISpStream *stream; + ISpMMSysAudio *mmaudio; + 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, + &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_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_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); + + 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); + + 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); + + /* 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); + + 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_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); + + ISpStream_Release(stream); + IStream_Release(base_stream); + ISpMMSysAudio_Release(mmaudio); +} + START_TEST(stream) { CoInitialize(NULL); test_interfaces(); + test_spstream(); CoUninitialize(); } diff --git a/dlls/sapi/tests/tts.c b/dlls/sapi/tests/tts.c index 38c69e6144a..c89bffae5a0 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,63 +155,102 @@ struct test_engine ISpObjectToken *token; + + const char *output_data; + size_t output_len; + 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)); + + cur->pNext = frag->pNext ? cur + 1 : NULL; - if (prev) - prev->pNext = frag; - else - *ret_frag_list = frag; + if (frag->pTextStart) + { + memcpy(cur_text, frag->pTextStart, frag->ulTextLen * sizeof(WCHAR)); + cur_text[frag->ulTextLen] = L'\0'; - prev = frag; - frag_list = frag_list->pNext; + cur->pTextStart = (WCHAR *)cur_text; + cur_text += frag->ulTextLen + 1; + } } } static void reset_engine_params(struct test_engine *engine) { - SPVTEXTFRAG *frag, *next; - + engine->output_data = NULL; + engine->output_len = 0; 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) + free(engine->frags); + engine->frags = NULL; + 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++) { - next = frag->pNext; - free(frag); + val = floor(32768 * sin(ang_freq * i) + 0.5); + ((int16_t *)data)[i] = min(max(-32768, val), 32767); } - engine->frag_list = NULL; + return data; } static inline struct test_engine *impl_from_ISpTTSEngine(ISpTTSEngine *iface) @@ -249,15 +295,17 @@ 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; 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); @@ -273,16 +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); - buf = calloc(1, 22050 * 2 / 5); - for (i = 0; i < 5; i++) + if (!engine->output_len) + return S_OK; + + 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; } @@ -295,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; @@ -414,9 +464,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"; @@ -431,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; @@ -443,6 +514,7 @@ static void test_spvoice(void) DISPID dispid; DISPPARAMS params; VARIANT args[2], ret; + int i; HRESULT hr; if (waveOutGetNumDevs() == 0) { @@ -450,8 +522,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); @@ -604,6 +674,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); @@ -629,7 +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.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); @@ -637,11 +714,8 @@ 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); + 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); @@ -659,6 +733,9 @@ static void test_spvoice(void) ok(duration < 200, "took %lu ms.\n", duration); reset_engine_params(&test_engine); + 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); @@ -677,15 +754,16 @@ 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); + 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); 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 | SPF_ASYNC, NULL); ok(hr == S_OK, "got %#lx.\n", hr); @@ -696,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); + 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); + + 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); + 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); + 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); @@ -798,17 +943,414 @@ static void test_spvoice(void) ISpVoice_Release(voice); ISpObjectToken_Release(token); ISpMMSysAudio_Release(audio_out); + free(wave_data); 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""; + + 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; + 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); + 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); + 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); + 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); + + hr = ISpVoice_Speak(voice, bad_text1, SPF_IS_XML | SPF_PARSE_SSML, NULL); + ok(hr == SPERR_UNSUPPORTED_FORMAT, "got %#lx.\n", hr); + + hr = ISpVoice_Speak(voice, bad_text2, SPF_IS_XML | SPF_PARSE_SSML, NULL); + ok(hr == SPERR_UNSUPPORTED_FORMAT, "got %#lx.\n", hr); + + hr = ISpVoice_Speak(voice, bad_text3, SPF_IS_XML | SPF_PARSE_SSML, NULL); + 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); + 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); + 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) { + 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); + ok(test_engine.frag_count == 2, "got %Iu.\n", test_engine.frag_count); + + 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); + 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) { + 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); + 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); + 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); + 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); + 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); + + 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); } 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(); } diff --git a/dlls/sapi/tts.c b/dlls/sapi/tts.c index 80f0298b51c..159635a4559 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,12 +48,15 @@ struct speech_voice LONG ref; ISpStreamFormat *output; + IMFTransform *resampler; + BOOL allow_format_changes; ISpObjectToken *engine_token; ISpTTSEngine *engine; LONG cur_stream_num; DWORD actions; USHORT volume; LONG rate; + SPVSTATE state; struct async_queue queue; CRITICAL_SECTION cs; }; @@ -75,6 +83,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) @@ -82,6 +93,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; @@ -169,6 +197,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); @@ -655,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; @@ -691,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; @@ -811,16 +839,80 @@ struct speak_task DWORD flags; }; -static HRESULT set_output_format(ISpStreamFormat *output, ISpTTSEngine *engine, GUID *fmtid, WAVEFORMATEX **wfx) +static void free_frag_list(SPVTEXTFRAG *frag) +{ + SPVTEXTFRAG *next; + + while (frag) + { + next = frag->pNext; + free(frag); + frag = next; + } +} + +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)) { @@ -828,12 +920,23 @@ 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 (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) + *use_resampler = TRUE; + } + else + *use_resampler = TRUE; + + if (*use_resampler) + hr = setup_resampler(voice, *engine_wfx, output_wfx); } done: @@ -846,6 +949,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; @@ -862,7 +966,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); @@ -894,7 +998,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 +1015,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 +1087,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 +1094,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 +1124,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 +1153,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 +1178,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 +1192,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) { @@ -1304,8 +1447,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); } @@ -1340,6 +1484,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); @@ -1349,6 +1556,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); } @@ -1420,6 +1630,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); @@ -1493,12 +1706,18 @@ HRESULT speech_voice_create(IUnknown *outer, REFIID iid, void **obj) This->ref = 1; This->output = NULL; + This->resampler = NULL; + This->allow_format_changes = TRUE; This->engine_token = NULL; This->engine = NULL; This->cur_stream_num = 0; 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 00000000000..e6fb9a17c70 --- /dev/null +++ b/dlls/sapi/xml.c @@ -0,0 +1,800 @@ +/* + * Speech API (SAPI) XML parser implementation. + * + * 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 + * 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 + +#define COBJMACROS + +#include "objbase.h" + +#include "sapiddk.h" +#include "sperror.h" + +#include "wine/debug.h" + +#include "sapi_private.h" + +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 *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; + +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 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; + 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 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) +{ + 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; +} + +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 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) +{ + static const struct string_value rate_values[] = + { + { L"x-slow", -9 }, + { L"slow", -4 }, + { L"medium", 0 }, + { L"fast", 4 }, + { 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 }, + }; + + 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; + 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 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 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)); + 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; + struct xml_elem elem; + BOOL is_text, end; + 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 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 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)); + 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}; + struct xml_elem parent = {0}; + SPVTEXTFRAG head_frag = {0}; + struct xml_elem elem; + HRESULT hr; + + parser.base = parser.ptr = contents; + parser.end = end; + parser.tail_frag = &head_frag; + + /* 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, &elem, state))) + return hr; + + if (next_xml_elem(&parser, &elem, &parent)) + { + ERR("Unexpected element %s after .\n", debugstr_xmlstr(&elem.name)); + return SPERR_UNSUPPORTED_FORMAT; + } + + *frag_list = head_frag.pNext; + return S_OK; +} + +HRESULT parse_sapi_xml(const WCHAR *contents, DWORD parse_flag, BOOL persist, SPVSTATE *global_state, + SPVTEXTFRAG **frag_list) +{ + 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; +} diff --git a/dlls/setupapi/devinst.c b/dlls/setupapi/devinst.c index f6675ee7840..1dc9f8237ca 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; } } diff --git a/dlls/shell32/shellpath.c b/dlls/shell32/shellpath.c index 07295713cb3..c11a1a4c222 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/shlview.c b/dlls/shell32/shlview.c index 9c699ecda08..1aca1871758 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: diff --git a/dlls/shell32/tests/shellpath.c b/dlls/shell32/tests/shellpath.c index 2d9450652a1..0796e04510c 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)]; diff --git a/dlls/user32/combo.c b/dlls/user32/combo.c index a5a81c84a27..b27a5cc0545 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/input.c b/dlls/user32/input.c index db59f21f89b..8d079a0794d 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) { diff --git a/dlls/user32/tests/combo.c b/dlls/user32/tests/combo.c index 2fc0d1ad9c0..77dec2382bd 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; + 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); diff --git a/dlls/user32/tests/win.c b/dlls/user32/tests/win.c index caaf01f6089..91632f79f79 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->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; + 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); diff --git a/dlls/version/tests/info.c b/dlls/version/tests/info.c index 61ff05b2559..fa7ef219193 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(); } diff --git a/dlls/wbemprox/builtin.c b/dlls/wbemprox/builtin.c index 432e3ab6188..7ee2bdf01b5 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; @@ -3283,6 +3357,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 +3410,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) ); @@ -4344,7 +4458,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 ); @@ -4508,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 @@ -4520,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/class.c b/dlls/wbemprox/class.c index 49e0201cc53..63df0e42688 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; } @@ -539,15 +541,31 @@ 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 ); - for (i = obj->index_property; i < table->num_cols; i++) + if (lFlags) + { + WARN( "lFlags %#lx.\n", lFlags ); + return WBEM_E_INVALID_PARAMETER; + } + + 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 (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 ff7be29ed52..868a5fd9a71 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; @@ -1084,10 +1090,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; @@ -1403,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; @@ -1416,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++) @@ -1429,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 7b6f3c35da5..4a212dd8a5a 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[] = @@ -304,7 +366,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 +376,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 +401,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 +2520,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" ); @@ -2504,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 ); @@ -2539,6 +2672,7 @@ START_TEST(query) test_SystemRestore( services ); test_empty_namespace( locator ); test_MSSMBios_RawSMBiosTables( locator ); + test_MSFT_PhysicalDisk( locator ); SysFreeString( path ); IWbemServices_Release( services ); diff --git a/dlls/wbemprox/wbemprox_private.h b/dlls/wbemprox/wbemprox_private.h index 632d448b8cd..b8d2cb56ad5 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; diff --git a/dlls/win32u/defwnd.c b/dlls/win32u/defwnd.c index 6174523ea58..4247bb06d62 100644 --- a/dlls/win32u/defwnd.c +++ b/dlls/win32u/defwnd.c @@ -267,6 +267,7 @@ BOOL adjust_window_rect( RECT *rect, DWORD style, BOOL menu, DWORD ex_style, UIN !((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; } @@ -1887,6 +1888,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/input.c b/dlls/win32u/input.c index 97e651a7ce5..58fa4a291d6 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; @@ -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/dlls/win32u/message.c b/dlls/win32u/message.c index 9331e928aa2..480e41a8c28 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; diff --git a/dlls/win32u/sysparams.c b/dlls/win32u/sysparams.c index b53dbb17f18..a07a413c248 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"; } @@ -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) diff --git a/dlls/win32u/vulkan.c b/dlls/win32u/vulkan.c index 4cb3997f648..f1bd833f53b 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,10 +1453,10 @@ 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; + 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 ) @@ -1488,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 = @@ -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/win32u/window.c b/dlls/win32u/window.c index 16291036001..614920f3c06 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: diff --git a/dlls/windows.gaming.input/controller.c b/dlls/windows.gaming.input/controller.c index 1adbc5cce0b..7e7b570d7f6 100644 --- a/dlls/windows.gaming.input/controller.c +++ b/dlls/windows.gaming.input/controller.c @@ -362,16 +362,16 @@ 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 ); } -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/gamepad.c b/dlls/windows.gaming.input/gamepad.c index def2c9dcf5e..ecac632ec3b 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 = 1. - 2. * state.axes[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 = 1. - 2. * state.axes[4]; + 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 3515d5c9ba6..b0c54044b66 100644 --- a/dlls/windows.gaming.input/provider.c +++ b/dlls/windows.gaming.input/provider.c @@ -141,6 +141,29 @@ 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)}; + 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 ); +} + static BOOL CALLBACK count_ffb_axes( const DIDEVICEOBJECTINSTANCEW *obj, void *args ) { DWORD *count = args; @@ -416,6 +439,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, @@ -605,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; @@ -629,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; diff --git a/dlls/windows.gaming.input/provider.idl b/dlls/windows.gaming.input/provider.idl index a6fcc6e84f3..fb7614b5c1c 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); diff --git a/dlls/windows.media.speech/Makefile.in b/dlls/windows.media.speech/Makefile.in index 5be66d8367e..ea1e4272139 100644 --- a/dlls/windows.media.speech/Makefile.in +++ b/dlls/windows.media.speech/Makefile.in @@ -1,5 +1,6 @@ MODULE = windows.media.speech.dll -IMPORTS = combase uuid +UNIXLIB = windows.media.speech.so +IMPORTS = combase uuid user32 SOURCES = \ async.c \ @@ -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 e772a791588..d53e1599eb8 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 d03fe0e773e..60d09c9f7d1 100644 --- a/dlls/windows.media.speech/private.h +++ b/dlls/windows.media.speech/private.h @@ -22,11 +22,16 @@ #include +#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "winerror.h" +#include "winternl.h" #define COBJMACROS #include "corerror.h" #include "windef.h" #include "winbase.h" #include "winstring.h" +#include "winuser.h" #include "objbase.h" #include "activation.h" @@ -43,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 a98970d35f9..f97fa5bc0b4 100644 --- a/dlls/windows.media.speech/recognizer.c +++ b/dlls/windows.media.speech/recognizer.c @@ -25,8 +25,610 @@ #include "wine/debug.h" +#include "unixlib.h" +#include "wine/unixlib.h" + 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; + 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 map_view_hstring_vector_view_hstring_create(value); +} + +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; + 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 semantic_interpretation_create(value); +} + +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 @@ -171,6 +773,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; @@ -187,15 +791,107 @@ 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; + ISpeechContinuousRecognitionResultGeneratedEventArgs *event_args; + ISpeechRecognitionConstraint *constraint; + ISpeechRecognitionResult *result; 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"); @@ -245,6 +941,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) @@ -264,7 +961,69 @@ 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; + } + + /* 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))) + { + 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); + } + + free(recognized_text); + free(recognition_result_params.result_buf); } else { @@ -319,7 +1078,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 +1106,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); } @@ -725,17 +1489,155 @@ static HRESULT WINAPI recognizer_get_UIOptions( ISpeechRecognizer *iface, ISpeec return E_NOTIMPL; } +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]; + 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; + create_params.grammar = grammar; + create_params.grammar_size = grammar_size; + + 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); + 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 = 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 with grammar.\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, diff --git a/dlls/windows.media.speech/tests/speech.c b/dlls/windows.media.speech/tests/speech.c index ade056a0a39..a6c7d56e1e1 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 @@ -203,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; } @@ -1082,7 +1097,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) { @@ -1308,7 +1323,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) { @@ -1527,7 +1542,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: @@ -1703,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; @@ -1744,12 +1759,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; } @@ -1866,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. */ diff --git a/dlls/windows.media.speech/unixlib.c b/dlls/windows.media.speech/unixlib.c new file mode 100644 index 00000000000..f068fa0c150 --- /dev/null +++ b/dlls/windows.media.speech/unixlib.c @@ -0,0 +1,487 @@ +/* + * 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 +#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_new_grm); +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 ) +{ + 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. " + "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_recognizer_new) + LOAD_FUNCPTR(vosk_recognizer_new_grm) + 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; + +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; +} + +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 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, *appid = getenv("SteamAppId"), *str = NULL; + 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 if (strcmp(appid, "739630") != 0) + 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); + + if (!best_match && !strcmp(appid, "739630")) + { + if ((str = (char *)map_lang_to_phasmophobia_dir(locale, delim - locale))) + best_match = strdup(str); + } + } + + 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, *appid = getenv("SteamAppId"); + 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); + + /* 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; +} + +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'; + + 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); + + if (!vosk_handle) + return STATUS_NOT_SUPPORTED; + + if ((status = find_model_by_locale(params->locale, &model))) + return status; + + 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); + + 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; +} + +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; + + 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); + } + 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 ) \ + 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) +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 */ + +const unixlib_entry_t __wine_unix_call_funcs[] = +{ + process_attach, + process_detach, + speech_create_recognizer, + speech_release_recognizer, + speech_recognize_audio, + speech_get_recognition_result, +}; + +const unixlib_entry_t __wine_unix_call_wow64_funcs[] = +{ + process_attach, + 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 new file mode 100644 index 00000000000..ad2fab738b9 --- /dev/null +++ b/dlls/windows.media.speech/unixlib.h @@ -0,0 +1,81 @@ +/* + * 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" + +typedef UINT64 speech_recognizer_handle; + +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 +{ + speech_recognizer_handle handle; +}; + +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 diff --git a/dlls/winebus.sys/bus_iohid.c b/dlls/winebus.sys/bus_iohid.c index 5a210fc6bbd..648d55dadb8 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 { @@ -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; } @@ -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 c97597dea8b..259ee419c21 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; @@ -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)) + 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; } @@ -280,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); @@ -292,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); @@ -300,10 +304,10 @@ 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); + 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); @@ -402,51 +406,23 @@ 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_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; - 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; - - /* Initialize axis in the report */ - for (i = SDL_CONTROLLER_AXIS_LEFTX; i < SDL_CONTROLLER_AXIS_MAX; i++) - hid_device_set_abs_axis(iface, i, pSDL_GameControllerGetAxis(impl->sdl_controller, i)); + 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); + } state = pSDL_GameControllerGetButton(impl->sdl_controller, SDL_CONTROLLER_BUTTON_DPAD_UP); hid_device_move_hatswitch(iface, 0, 0, state ? -1 : +1); @@ -570,7 +546,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); @@ -580,7 +555,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); @@ -906,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 = 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 == 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; @@ -937,6 +913,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; @@ -990,7 +969,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); @@ -1066,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); @@ -1075,7 +1054,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 +1077,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 +1116,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))) { @@ -1177,6 +1156,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); @@ -1240,10 +1220,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 332e79e3ae5..9079fb2cf35 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); @@ -100,12 +94,13 @@ 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 { struct unix_device unix_device; void (*read_report)(struct unix_device *iface); + BOOL started; struct udev_device *udev_device; char devnode[MAX_PATH]; @@ -129,7 +124,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 */ @@ -166,7 +161,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 */ @@ -180,16 +175,31 @@ 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)]; + LONG abs_min[ABS_CNT]; + LONG abs_max[ABS_CNT]; + 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 hat_count; + int button_count; + BOOL is_gamepad; - int haptics_state; pthread_cond_t haptics_cond; pthread_t haptics_thread; struct ff_effect haptics; @@ -217,6 +227,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 +260,7 @@ static void start_polling_device(struct unix_device *iface) poll_count++; write(deviceloop_control[1], "u", 1); + impl->started = TRUE; } } @@ -821,176 +834,117 @@ 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 void set_abs_axis_value(struct unix_device *iface, int code, int value) { - 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; - BYTE keybits[(KEY_MAX+7)/8]; + struct lnxev_device *impl = lnxev_impl_from_unix_device(iface); - if (ioctl(device_fd, EVIOCGBIT(EV_KEY, sizeof(keybits)), keybits) == -1) + if (code < ABS_HAT0X || code > ABS_HAT3Y) { - WARN("ioctl(EVIOCGBIT, EV_KEY) failed: %d %s\n", errno, strerror(errno)); - return FALSE; - } + LONG min = impl->abs_min[code], range = impl->abs_max[code] - impl->abs_min[code]; + if (!(code = impl->abs_map[code])) return; - for (i = 0; i < ARRAY_SIZE(gamepad_buttons); i++) - { - if (test_bit(keybits, gamepad_buttons[i])) + if (impl->is_gamepad) { - if (map) map[gamepad_buttons[i]] = button_count; - button_count++; + 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 */ } - } - for (i = BTN_DIGI; i < KEY_MAX; i++) + hid_device_set_abs_axis(iface, code - 1, value); + } + else if ((code - ABS_HAT0X) % 2) { - if (test_bit(keybits, i)) + if (!(code = impl->hat_map[code - ABS_HAT0X])) return; + if (impl->is_gamepad) { - if (map) map[i] = button_count; - button_count++; + hid_device_set_button(iface, 10, value < 0); + hid_device_set_button(iface, 12, value > 0); } + hid_device_set_hatswitch_y(iface, code - 1, value); } - return button_count; -} - -static INT count_abs_axis(int device_fd) -{ - BYTE absbits[(ABS_MAX+7)/8]; - int abs_count = 0; - int i; - - if (ioctl(device_fd, EVIOCGBIT(EV_ABS, sizeof(absbits)), absbits) == -1) + else { - WARN("ioctl(EVIOCGBIT, EV_ABS) failed: %d %s\n", errno, strerror(errno)); - return 0; + 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_hatswitch_x(iface, code - 1, value); } - - 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; + 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); - 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; - if (!hid_device_begin_input_report(iface, &device_usage)) - return STATUS_NO_MEMORY; - - abs_count = 0; - for (i = 0; i < ARRAY_SIZE(absolute_usages); i++) + if (impl->is_gamepad) { - usage = absolute_usages[i]; - if (!test_bit(absbits, i)) continue; - ioctl(impl->base.device_fd, EVIOCGABS(i), abs_info + i); - if (!usage.UsagePage || !usage.Usage) continue; - 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; - - impl->abs_map[i] = abs_count++; } - - rel_count = 0; - for (i = 0; i < ARRAY_SIZE(relative_usages); i++) + else { - usage = relative_usages[i]; - if (!test_bit(relbits, i)) continue; - if (!usage.UsagePage || !usage.Usage) 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; - impl->rel_map[i] = rel_count++; - } - - hat_count = 0; - for (i = ABS_HAT0X; i <= ABS_HAT3X; i += 2) - { - if (!test_bit(absbits, i)) continue; - impl->hat_map[i - ABS_HAT0X] = hat_count; - impl->hat_map[i - ABS_HAT0X + 1] = hat_count++; - } + 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; + } - if (hat_count && !hid_device_add_hatswitch(iface, hat_count)) - 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; + } - /* For now lump all buttons just into incremental usages, Ignore Keys */ - button_count = count_buttons(impl->base.device_fd, 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; + 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; - 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 (!hid_device_add_physical(iface, usages, count)) + 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; } @@ -998,15 +952,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; - 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) - hid_device_set_hatswitch_y(iface, impl->hat_map[i - ABS_HAT0X], abs_info[i].value); - else - hid_device_set_hatswitch_x(iface, impl->hat_map[i - ABS_HAT0X], abs_info[i].value); + USAGE_AND_PAGE usage = absolute_usages[i]; + if (!usage.UsagePage || !usage.Usage) continue; + if (!test_bit(info->abs, i)) continue; + set_abs_axis_value(iface, i, abs_info[i].value); } return STATUS_SUCCESS; @@ -1017,7 +968,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, button; switch (ie->type) { @@ -1035,15 +986,18 @@ 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; + 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); + } + 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); - else if ((ie->code - ABS_HAT0X) % 2) - hid_device_set_hatswitch_y(iface, impl->hat_map[ie->code - ABS_HAT0X], ie->value); - else - hid_device_set_hatswitch_x(iface, impl->hat_map[ie->code - ABS_HAT0X], 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); @@ -1071,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 == (uint16_t)-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)) @@ -1111,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); @@ -1126,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); @@ -1164,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); @@ -1182,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); @@ -1210,24 +1161,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); @@ -1271,7 +1204,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) @@ -1281,7 +1213,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"); @@ -1563,93 +1494,216 @@ 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; - int bus = 0; + char buffer[MAX_PATH]; - if (!(devnode = udev_device_get_devnode(dev))) + 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}; + int axis_count = 0, button_count = 0; + struct lnxev_info info = {0}; + struct lnxev_device *impl; + + 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 (!(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; + + for (int i = 0; i < ABS_CNT; i++) { - if (fd >= 0) close(fd); - return; + 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; } - if (fd < 0 && (fd = open(devnode, O_RDWR)) == -1) + for (int i = 0, count = 0; i < REL_CNT; i++) { - WARN("Unable to open udev device %s: %s\n", debugstr_a(devnode), strerror(errno)); - return; + 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; } - TRACE("udev %s syspath %s\n", debugstr_a(devnode), udev_device_get_syspath(dev)); - - get_device_subsystem_info(dev, "hid", NULL, &desc, &bus); - get_device_subsystem_info(dev, "input", NULL, &desc, &bus); - 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 (!strcmp(subsystem, "hidraw")) + for (int i = ABS_HAT0X; i <= ABS_HAT3X; i += 2) { - static const WCHAR hidraw[] = {'h','i','d','r','a','w',0}; -#ifdef HAVE_LINUX_HIDRAW_H - char product[MAX_PATH]; -#endif + 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; + } - if (!desc.manufacturer[0]) memcpy(desc.manufacturer, hidraw, sizeof(hidraw)); - desc.is_hidraw = TRUE; + for (int i = BTN_MISC; i < KEY_MAX; i++) + { + if (!test_bit(info.key, i)) continue; + impl->button_map[i] = ++impl->button_count; + } -#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 + 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 */ } -#ifdef HAS_PROPER_INPUT_HEADER - else if (!strcmp(subsystem, "input")) + else if (is_xbox_gamepad(desc.vid, desc.pid)) desc.is_gamepad = TRUE; + else if (axis_count == 6 && button_count >= (impl->hat_count ? 10 : 14)) desc.is_gamepad = TRUE; + + if ((impl->is_gamepad = desc.is_gamepad)) { - static const WCHAR evdev[] = {'e','v','d','e','v',0}; - struct input_id device_id = {0}; - char buffer[MAX_PATH]; + static const int gamepad_axes[] = {1, 2, 5, 3, 4, 6}; + 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, + }; - if (ioctl(fd, EVIOCGID, &device_id) < 0) - WARN("ioctl(EVIOCGID) failed: %d %s\n", errno, strerror(errno)); - else + 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++) { - desc.vid = device_id.vendor; - desc.pid = device_id.product; - desc.version = device_id.version; + 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++]; } - if (!desc.manufacturer[0]) memcpy(desc.manufacturer, evdev, sizeof(evdev)); + 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 > (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; + } - if (!desc.product[0] && ioctl(fd, EVIOCGNAME(sizeof(buffer) - 1), buffer) > 0) + for (int i = BTN_MISC; i < KEY_MAX; i++) { - 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 (i >= BTN_GAMEPAD && i < BTN_DIGI) continue; + 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; } + } - if (!desc.serialnumber[0] && ioctl(fd, EVIOCGUNIQ(sizeof(buffer)), buffer) >= 0) - ntdll_umbstowcs(buffer, strlen(buffer) + 1, desc.serialnumber, ARRAY_SIZE(desc.serialnumber)); + 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); } + 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 (!desc.serialnumber[0]) + if (!(devnode = udev_device_get_devnode(dev))) { - static const WCHAR zeros[] = {'0','0','0','0',0}; - memcpy(desc.serialnumber, zeros, sizeof(zeros)); + if (fd >= 0) close(fd); + return; } - if (desc.vid == 0x28de && desc.pid == 0x11ff && !strcmp(subsystem, "input")) + if (fd < 0 && (fd = open(devnode, O_RDWR)) == -1) { - 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 */ + WARN("Unable to open udev device %s: %s\n", debugstr_a(devnode), strerror(errno)); + return; + } + + TRACE("udev %s syspath %s\n", debugstr_a(devnode), udev_device_get_syspath(dev)); + + get_device_subsystem_info(dev, "hid", NULL, &desc, &bus); + get_device_subsystem_info(dev, "input", NULL, &desc, &bus); + get_device_subsystem_info(dev, "usb", "usb_device", &desc, &bus); + if (bus == BUS_BLUETOOTH) desc.is_bluetooth = TRUE; + + if (!(subsystem = udev_device_get_subsystem(dev))) + { + WARN("udev_device_get_subsystem failed for %s.\n", debugstr_a(devnode)); + close(fd); + return; } + + if (desc.vid == 0x28de && desc.pid == 0x11ff && !strcmp(subsystem, "input")) + TRACE("evdev %s: detected steam input virtual controller\n", debugstr_a(devnode)); 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)); @@ -1662,53 +1716,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 + 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 @@ -1781,7 +1792,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 +1809,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 +1838,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 +1850,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 +1946,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 +1989,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 +1997,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 +2069,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 +2091,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 +2118,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/hid.c b/dlls/winebus.sys/hid.c index 78efe6116ce..ee1c9a9de07 100644 --- a/dlls/winebus.sys/hid.c +++ b/dlls/winebus.sys/hid.c @@ -343,6 +343,27 @@ 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 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, 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_end_input_report(iface)) return FALSE; + + return TRUE; +} + #include "pshpack1.h" struct hid_haptics_intensity { @@ -503,7 +524,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 @@ -556,6 +577,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 +767,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 +787,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 +895,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 +996,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 +1031,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 +1070,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 +1102,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 +1134,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 +1151,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 +1263,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 +1281,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/main.c b/dlls/winebus.sys/main.c index 30385225674..2ff76355b98 100644 --- a/dlls/winebus.sys/main.c +++ b/dlls/winebus.sys/main.c @@ -19,6 +19,7 @@ */ #include +#include #include #include "ntstatus.h" @@ -51,6 +52,7 @@ static DEVICE_OBJECT *keyboard_obj; static DEVICE_OBJECT *bus_pdo; static DEVICE_OBJECT *bus_fdo; +static struct bus_options options = {.devices = LIST_INIT(options.devices)}; static HANDLE driver_key; struct hid_report @@ -369,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; @@ -423,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; @@ -454,13 +467,22 @@ 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; SIZE_T len; DWORD size; - if (check_bus_option(L"DisableHidraw", FALSE)) return FALSE; + 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) { @@ -496,9 +518,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; @@ -538,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 */ @@ -650,11 +667,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; @@ -905,7 +922,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-", @@ -913,11 +930,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); - device = bus_create_hid_device(&event->device_created.desc, event->device); + 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); + if (device) IoInvalidateDeviceRelations(bus_pdo, BusRelations); else { @@ -978,7 +1011,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 +1020,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 +1087,200 @@ static void sdl_bus_load_mappings(struct sdl_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; + + /* @@ 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; + + 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++; + + /* @@ 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); + 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); + 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); + } + + 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) { - 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_NOT_SUPPORTED; + 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 +1289,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); + udev_driver_init(); + sdl_driver_init(); iohid_driver_init(); irp->IoStatus.Status = STATUS_SUCCESS; @@ -1156,6 +1316,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/unix_private.h b/dlls/winebus.sys/unix_private.h index 797ec7de740..52f50ef19e8 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; }; @@ -183,6 +184,7 @@ struct hid_physical BYTE set_ramp_force_report; struct hid_effect_state effect_state; + USHORT axes_count; }; struct hid_device_state @@ -251,8 +253,9 @@ 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); +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); diff --git a/dlls/winebus.sys/unixlib.h b/dlls/winebus.sys/unixlib.h index 02e7a1c6953..8dab8af0d57 100644 --- a/dlls/winebus.sys/unixlib.h +++ b/dlls/winebus.sys/unixlib.h @@ -47,24 +47,25 @@ struct device_desc WCHAR serialnumber[MAX_PATH]; }; -struct sdl_bus_options +struct device_options { - BOOL split_controllers; - BOOL map_controllers; - /* freed after bus_init */ - UINT mappings_count; - char **mappings; + struct list entry; + UINT vid; + UINT pid; + INT hidraw; }; -struct udev_bus_options +struct bus_options { + BOOL disable_sdl; BOOL disable_hidraw; BOOL disable_input; BOOL disable_udevd; -}; - -struct iohid_bus_options -{ + BOOL split_controllers; + BOOL map_controllers; + UINT mappings_count; + struct list devices; + char **mappings; }; enum bus_event_type diff --git a/dlls/winedmo/unix_media_type.c b/dlls/winedmo/unix_media_type.c index edde85172b6..eba74c80b69 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; } diff --git a/dlls/winegstreamer/main.c b/dlls/winegstreamer/main.c index c1b7a7a7bda..0c3e31d05cb 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/media-converter/videoconv.c b/dlls/winegstreamer/media-converter/videoconv.c index d8b77e5e172..4fe0805db86 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); } diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index 0bb2c6b4000..8289d81e8a5 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); } diff --git a/dlls/winegstreamer/video_processor.c b/dlls/winegstreamer/video_processor.c index 35974615ba3..ed09f3984d5 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) diff --git a/dlls/winegstreamer/wg_format.c b/dlls/winegstreamer/wg_format.c index 2134fa7cf40..ce8878f3f4d 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) { diff --git a/dlls/winegstreamer/wg_media_type.c b/dlls/winegstreamer/wg_media_type.c index abde06bca97..101f320f70a 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, diff --git a/dlls/winegstreamer/wg_parser.c b/dlls/winegstreamer/wg_parser.c index 989936acb34..eeee52c39eb 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); @@ -1985,7 +2054,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); diff --git a/dlls/winegstreamer/wg_transform.c b/dlls/winegstreamer/wg_transform.c index 917af7ebce9..7a1287b0fa9 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); @@ -674,6 +694,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 +716,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 +754,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; diff --git a/dlls/winegstreamer/wm_reader.c b/dlls/winegstreamer/wm_reader.c index 9145f59f474..df7c20e8749 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; diff --git a/dlls/winemac.drv/vulkan.c b/dlls/winemac.drv/vulkan.c index 063666407cd..f570eceb963 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/winepulse.drv/pulse.c b/dlls/winepulse.drv/pulse.c index 9cb29be21b6..07c2024a380 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 @@ -209,10 +222,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 +257,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 +289,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 +305,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; @@ -1233,42 +1257,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 (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; @@ -1556,102 +1544,145 @@ 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; + /* 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); +} - if (diff > stream->mmdev_period_usec) - { - stream->just_started = FALSE; - last_time = now; - } - } - else - { - INT32 adjust = last_time + stream->mmdev_period_usec - now; +static void pa_streams_timer_cb_destroy(pa_mainloop_api *api, pa_time_event *e, void *userdata) +{ + struct pulse_period *period = userdata; - adv_usec = now - last_time; + TRACE("period %p.\n", period); - 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; + list_remove(&period->entry); + free(period); +} - delay.QuadPart = -(stream->mmdev_period_usec + adjust) * 10; +static void remove_stream_from_period(struct pulse_stream *stream) +{ + if (!stream->period) + return; - last_time += stream->mmdev_period_usec; - } + list_remove(&stream->period_entry); + if (list_empty(&stream->period->streams) && pulse_ml) + { + pa_mainloop_api *api = pa_mainloop_get_api(pulse_ml); - if (stream->dataflow == eRender) - { - pulse_write(stream); + TRACE("freeing time event for period %p.\n", stream->period); + api->time_free(stream->period->time_event); + stream->period->time_event = NULL; + } +} - /* 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; - } +static void pulse_add_stream_to_period(struct pulse_stream *stream) +{ + struct pulse_period *period; + pa_mainloop_api *api; + + if (stream->period) + { + assert(stream->mmdev_period_usec == stream->period->period); + return; + } + + 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; } + } - if (stream->event) - NtSetEvent(stream->event, NULL); + 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); +} - 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)); +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; - pulse_unlock(); + if(params->timer_thread) { + 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))) + 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; } @@ -1698,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; diff --git a/dlls/winevulkan/loader.c b/dlls/winevulkan/loader.c index 9d42ae7539c..79bbe8a2758 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, @@ -739,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/make_vulkan b/dlls/winevulkan/make_vulkan index c9a033fa9f3..96fc524560b 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 @@ -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", } @@ -1365,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 @@ -3228,7 +3234,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 +3251,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(): diff --git a/dlls/winevulkan/vulkan.c b/dlls/winevulkan/vulkan.c index 3ade38428c3..dcf0d17a9ce 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; @@ -2179,6 +2181,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 +2242,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 +2302,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 +2320,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 +2333,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 +2349,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 +3398,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 +3407,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); @@ -4710,6 +4721,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); diff --git a/dlls/winewayland.drv/vulkan.c b/dlls/winewayland.drv/vulkan.c index 0e1c33707d1..03d1ad09db5 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/desktop.c b/dlls/winex11.drv/desktop.c index d9d306ef645..8afaef61c28 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/event.c b/dlls/winex11.drv/event.c index 7e736df9857..82ee4debb20 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,25 +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 * @@ -1340,7 +1321,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 ); @@ -1410,11 +1391,10 @@ 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; + Window window = 0; if (data->active_window) { @@ -1422,13 +1402,8 @@ static void handle_net_active_window( HWND hwnd, XPropertyEvent *event ) data->active_window = NULL; } - if (event->state == PropertyNewValue) window = get_net_active_window( event->display, &data->active_window ); + 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 +1420,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 +1435,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/keyboard.c b/dlls/winex11.drv/keyboard.c index efc44d3084a..3907828201f 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}, @@ -1343,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 bfffd51a076..b84bc16c01d 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 ); } @@ -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; diff --git a/dlls/winex11.drv/opengl.c b/dlls/winex11.drv/opengl.c index 6e2e1e5e8c1..478e3c253e5 100644 --- a/dlls/winex11.drv/opengl.c +++ b/dlls/winex11.drv/opengl.c @@ -3462,21 +3462,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; diff --git a/dlls/winex11.drv/vulkan.c b/dlls/winex11.drv/vulkan.c index 6e7f47cb349..908066c109e 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/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index ba04b6e1656..76ad973ed2a 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++] = '"'; @@ -313,28 +310,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 = (HWND)-1; - int format; - Atom type; - - if (!window) return 0; - if (!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 (HWND)-1; - return hwnd; -} - static BOOL is_managed( HWND hwnd ) { struct x11drv_win_data *data = get_win_data( hwnd ); @@ -343,7 +318,7 @@ static BOOL is_managed( HWND hwnd ) return ret; } -HWND *build_hwnd_list(void) +static HWND *build_hwnd_list(void) { NTSTATUS status; HWND *list; @@ -378,6 +353,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 @@ -399,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 * @@ -419,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 */ @@ -543,6 +564,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 ); } @@ -930,21 +953,65 @@ 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 ); } +/* 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 ) +{ + 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 */ + 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, 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; data->desired_state.mwm_hints = *new_hints; - if (!data->whole_window) return; /* no window, 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 )) + { + 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, @@ -957,7 +1024,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; @@ -994,9 +1061,7 @@ 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, swp_flags ); + window_set_mwm_hints( data, &mwm_hints ); } @@ -1032,6 +1097,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); @@ -1047,16 +1114,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 ); @@ -1141,7 +1219,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; @@ -1158,7 +1236,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 ); } @@ -1180,19 +1258,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 ); } @@ -1271,11 +1350,17 @@ 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 || 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 */ + 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) @@ -1317,6 +1402,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; @@ -1324,7 +1410,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 ); @@ -1345,17 +1431,37 @@ 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, 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; + BOOL old_above = data->pending_state.above; XWindowChanges changes; + 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 */ - 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 || 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 ); + 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. @@ -1381,10 +1487,8 @@ static void window_set_config( struct x11drv_win_data *data, const RECT *new_rec /* 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; @@ -1394,16 +1498,23 @@ 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) && - (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; 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) { @@ -1417,6 +1528,7 @@ static void window_set_config( struct x11drv_win_data *data, const RECT *new_rec } 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 ); @@ -1433,8 +1545,9 @@ 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->embedded) return; if (data->whole_window == root_window) { if (!is_virtual_desktop()) return; @@ -1443,17 +1556,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 ); @@ -1557,13 +1683,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 */ @@ -1579,8 +1705,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; } @@ -1589,22 +1715,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 @@ -1614,15 +1740,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()) { @@ -1673,31 +1799,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; @@ -1705,7 +1830,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 ); } @@ -1732,6 +1857,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); @@ -1778,19 +1904,40 @@ 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 */ + 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 */ 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; 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. */ @@ -1844,17 +1991,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 == (HWND)-1) *foreground = NtUserGetDesktopWindow(); + *foreground = hwnd_from_window( thread_data->display, thread_data->current_state.net_active_window ); if (*foreground == old_foreground) *foreground = 0; } @@ -1877,17 +2023,15 @@ 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 ); + memcpy( current, value, size ); 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 { @@ -1915,13 +2059,14 @@ 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_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 ); 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 ); @@ -1945,10 +2090,11 @@ 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_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 ); } void window_mwm_hints_notify( struct x11drv_win_data *data, unsigned long serial, const MwmHints *value ) @@ -1961,8 +2107,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 ) @@ -1975,34 +2128,73 @@ 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 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_mwm_hints( data, &data->desired_state.mwm_hints ); + window_set_config( data, data->desired_state.rect, FALSE ); } 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 ); +} + +Window get_net_active_window( Display *display ) +{ + unsigned long count, remaining; + Window window = None, *value; + int format; + Atom type; - 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 (!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 == (HWND)-1) 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 ) == (HWND)-1) 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; + + if (window) get_window_name( data->display, window, &data->active_window ); } static BOOL window_set_pending_activate( HWND hwnd ) @@ -2011,7 +2203,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; @@ -2023,9 +2215,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; @@ -2041,7 +2233,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, @@ -2073,7 +2265,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 ); } @@ -2112,8 +2309,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 ); } @@ -2344,7 +2541,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; @@ -2375,7 +2571,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 ); @@ -2425,7 +2621,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]; @@ -2460,8 +2655,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 ); @@ -2469,10 +2662,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 ); @@ -2526,13 +2719,13 @@ 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) ); 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) @@ -2613,7 +2806,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 */ { @@ -2679,7 +2872,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 ); @@ -2778,13 +2971,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; @@ -2926,7 +3128,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 ); } @@ -3293,12 +3495,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; @@ -3382,17 +3584,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 ); } } @@ -3483,7 +3685,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 ); } @@ -3535,7 +3737,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; } } @@ -3572,7 +3774,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 ); } } @@ -3645,9 +3847,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; @@ -3739,7 +3941,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; @@ -3796,7 +3998,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 27d1af192d5..2e79e893e00 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); @@ -534,7 +539,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, @@ -636,11 +640,12 @@ 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; + BOOL above; }; /* x11drv private window data */ @@ -705,15 +710,15 @@ 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 ); +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 ); @@ -749,7 +754,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 22a1e370b41..322cb33c125 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", diff --git a/dlls/winexinput.sys/main.c b/dlls/winexinput.sys/main.c index 45c6835bfe7..48743d6adf6 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/wininet/urlcache.c b/dlls/wininet/urlcache.c index 7975071ce91..49c8ce9becc 100644 --- a/dlls/wininet/urlcache.c +++ b/dlls/wininet/urlcache.c @@ -1455,17 +1455,19 @@ 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]; - for (lpszKey++; *lpszKey; lpszKey++) - { - for (i = 0; i < ARRAY_SIZE(key); i++) - key[i] = lookupTable[*lpszKey ^ key[i]]; - } + if (*input) + for (input++; *input; input++) + { + for (i = 0; i < ARRAY_SIZE(key); i++) + key[i] = lookupTable[*input ^ key[i]]; + } return *(DWORD *)key; } diff --git a/dlls/wintrust/asn.c b/dlls/wintrust/asn.c index f6c4086b508..152ff5a2fbf 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'; diff --git a/dlls/ws2_32/tests/sock.c b/dlls/ws2_32/tests/sock.c index 0f822402c71..c3383517acd 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(); diff --git a/dlls/xinput1_1/Makefile.in b/dlls/xinput1_1/Makefile.in index b5c13cde8df..770937e7612 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 f49e8f53191..a8c71b40ca8 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 28d46d8c97f..7dc406dd85a 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 92cd439f6b9..906d25d196b 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)); @@ -618,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) @@ -646,7 +626,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 +634,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); @@ -1127,7 +1107,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; @@ -1147,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; @@ -1158,18 +1142,44 @@ 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; + /* 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]); diff --git a/dlls/xinput1_3/tests/xinput.c b/dlls/xinput1_3/tests/xinput.c index 5c31f0be1fe..71905dc5a40 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); } diff --git a/dlls/xinput1_4/Makefile.in b/dlls/xinput1_4/Makefile.in index 69d6b9c018f..482f75d0f7e 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/Makefile.in b/dlls/xinput9_1_0/Makefile.in index d5f2adfa3ff..fe89cfc4b6e 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 00000000000..dc3c54e6498 --- /dev/null +++ b/dlls/xinput9_1_0/main.c @@ -0,0 +1,150 @@ +/* + * 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 *caps) +{ + DWORD ret; + + InitOnceExecuteOnce(&init_xinput1_4_once, init_xinput1_4_funcs, NULL, NULL); + + if (!pXInputGetCapabilities) return ERROR_DEVICE_NOT_CONNECTED; + 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) +{ + 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; +} diff --git a/include/bcrypt.h b/include/bcrypt.h index b7d6c161467..bb6ed5c1625 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" diff --git a/include/ipexport.h b/include/ipexport.h index a0e30ba1533..1a93a0136b3 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 diff --git a/include/iphlpapi.h b/include/iphlpapi.h index 84b9b7db34c..5794f6b2a68 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); diff --git a/include/netioapi.h b/include/netioapi.h index 9a9cff8ca79..9994e5b19ac 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*); @@ -276,6 +279,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**); diff --git a/include/wine/hid.h b/include/wine/hid.h index 8200ca9beec..cb31bd6e5a5 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 */ diff --git a/include/wine/mscvpdb.h b/include/wine/mscvpdb.h index d389d5d22d0..118ccdb68d4 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 @@ -1936,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 @@ -1949,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 @@ -1966,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 @@ -1987,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 @@ -2057,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 @@ -2112,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 @@ -2307,7 +2378,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 */ }; @@ -2327,7 +2398,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__ @@ -2376,7 +2447,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 @@ -2771,24 +2842,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; diff --git a/include/wine/nsi.h b/include/wine/nsi.h index 6bf3ce5bd5d..5028bbf9e19 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 @@ -334,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 @@ -434,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 */ }; diff --git a/include/wine/vulkan_driver.h b/include/wine/vulkan_driver.h index 15d03383e12..b385b93017a 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 *); diff --git a/include/winternl.h b/include/winternl.h index be01f3b31d0..465341849c8 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 diff --git a/libs/faudio/src/FAudio.c b/libs/faudio/src/FAudio.c index b85db87dd19..47e6b2d02fe 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; } diff --git a/loader/wine.inf.in b/loader/wine.inf.in index 4c5edde1d42..70472c19bbf 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] @@ -3009,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 diff --git a/nls/locale.nls b/nls/locale.nls index e46df56c6ab..c717bb328f1 100644 Binary files a/nls/locale.nls and b/nls/locale.nls differ diff --git a/programs/belauncher/main.c b/programs/belauncher/main.c index 850c69f7613..a2b0666ad91 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) @@ -94,6 +98,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) { @@ -142,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); @@ -170,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: diff --git a/programs/services/services.c b/programs/services/services.c index 2c19f0b14cc..78ac1bd3497 100644 --- a/programs/services/services.c +++ b/programs/services/services.c @@ -1030,15 +1030,25 @@ 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", + L"PROTON_DISABLE_HIDRAW", + L"PROTON_ENABLE_HIDRAW", + }; + 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); } diff --git a/programs/wineboot/wineboot.c b/programs/wineboot/wineboot.c index f058ac9f378..636b6d738bb 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) @@ -1043,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; @@ -1087,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 ); } diff --git a/programs/winedbg/memory.c b/programs/winedbg/memory.c index 5431c4297cd..3da70e1bfb2 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) diff --git a/programs/wmic/main.c b/programs/wmic/main.c index 02a07f03d2e..6ca3e680654 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 ); diff --git a/server/device.c b/server/device.c index 20950f50710..4c5eadcb399 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 ); diff --git a/server/process.c b/server/process.c index 73e85e9d61a..914ffc105f8 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 d2aadd521e8..e332069c56f 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 72cb633397a..5f5506adeb4 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; @@ -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 @@ -1979,6 +1981,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 e2eea50d673..a40c5f40847 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 */ diff --git a/server/queue.c b/server/queue.c index cb43e56a436..7a5f74ce334 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/sock.c b/server/sock.c index 21b4621bb0c..73409c5a1df 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 diff --git a/server/trace.c b/server/trace.c index 52b219a1f17..80621d579d3 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/server/winstation.c b/server/winstation.c index 5a83171e659..8803d9bf619 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; diff --git a/tools/gitlab/test.yml b/tools/gitlab/test.yml index 9ed9ed610b8..787cfee5341 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.2.0 cache: - key: wine-gecko-$GECKO_VER paths: diff --git a/tools/make_requests b/tools/make_requests index 53c57c1615c..7f1c9db3c95 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 ], diff --git a/tools/make_unicode b/tools/make_unicode index 4277c8c34f3..da5e960813f 100755 --- a/tools/make_unicode +++ b/tools/make_unicode @@ -2218,6 +2218,7 @@ sub load_data() my $dst = hex $2; if ($1 eq "narrow") { + next if $src >= 0xffe8 && $src <= 0xffef; # don't remap arrows $halfwidth_table[$dst] = $src; $fullwidth_table[$src] = $dst; } diff --git a/tools/makedep.c b/tools/makedep.c index 4c9333e401b..22a7c14710f 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 ); } } } diff --git a/tools/winedump/msc.c b/tools/winedump/msc.c index 997daab501b..46925e2ea4f 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, " "); diff --git a/tools/winedump/pdb.c b/tools/winedump/pdb.c index dc59695a826..06484a3dab7 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);